본문 바로가기

4.개발 및 운영 환경

NSIS Plugin 맹글기

NSIS은 내가 좋아하는 설치 프로그램 만들어주는 스크립트이다. 무료이다. 기능은 강력하지 않지만 있을 것은 다 있다.
InstallShield 부럽지 않다. 정말? ㅡ.ㅡ;
급하게 플러그인이 필요하여 만들게 되었다. 뭐 이전에 모아둔 자료가 아주 유용했다. 코드도 간단해서 분석하는데도 어렵지 않다.
간단하게 사용해본 정도라 내용에 오류가 있을수 있으니 양해바란다.

작성: http://ospace.tistory.com/,2011.12.23 (ospace114@empal.com)

기본 구조

기본적으로 DLL을 사용한다. MFC용이 아닌 일반 C용 임을 주의하자. 자세한 코드 설명은 생략하겠다.
내용은 아래 보면 대충 알 수 있다. 그리고 아래 내용은 Andrei Ciubotaru의 process 코드를 참조하여 재구성하였다.
전역 변수를 사용하지 않고 nsis_t 구조체를 만들어서 사용했다.

#include <Windows.h>
#include <Strsafe.h>

typedef struct _stack_t {
    struct _stack_t    *next;
    char             text[1]; // this should be the length of string_size
} stack_t;

typedef struct _nsis_t {
    int string_size; //문자열 기본 할당 크기
    char *variables; // 사용자 변수
    stack_t **stacktop; // 스택 변수
} nsis_t;

typedef enum _variable_t
{
    INST_0,         // $0
    INST_1,         // $1
    INST_2,         // $2
    INST_3,         // $3
    INST_4,         // $4
    INST_5,         // $5
    INST_6,         // $6
    INST_7,         // $7
    INST_8,         // $8
    INST_9,         // $9
    INST_R0,        // $R0
    INST_R1,        // $R1
    INST_R2,        // $R2
    INST_R3,        // $R3
    INST_R4,        // $R4
    INST_R5,        // $R5
    INST_R6,        // $R6
    INST_R7,        // $R7
    INST_R8,        // $R8
    INST_R9,        // $R9
    INST_CMDLINE,   // $CMDLINE
    INST_INSTDIR,   // $INSTDIR
    INST_OUTDIR,    // $OUTDIR
    INST_EXEDIR,    // $EXEDIR
    INST_LANG,      // $LANGUAGE
    __INST_LAST
} variable_t;

int pop_string(nsis_t *self, char *str, size_t str_size) {
    stack_t *th;

    if(!self || !self->stacktop || !*self->stacktop )
        return 1;

    th = (*self->stacktop);
    if (0!=StringCchCopy(str, str_size, th->text))
        return 1;

    *self->stacktop = th->next;
    GlobalFree( (HGLOBAL)th );

    return 0;
}

void push_string(nsis_t *self, char *str) {
    stack_t *th;

    if (!self || !self->stacktop) return;
        return;

    th = (stack_t*)GlobalAlloc(GPTR, sizeof(stack_t) + self->string_size);
    if (S_OK!=StringCchCopy(th->text, self->string_size, str))
        return;
    //push stack
    th->next = *self->stacktop;
    *self->stacktop = th;
}

char *get_user_variable(nsis_t *self, variable_t var_idx) {
    if( !self || var_idx < 0 || var_idx >= __INST_LAST )
        return NULL;

    return (self->variables + var_idx*self->string_size);
}

void set_user_variable(nsis_t *self, variable_t var_idx, char *var) {
    if(!self || var == NULL) return;

    if( !self || var_idx < 0 || var_idx >= __INST_LAST )
        return;

    StringCchCopy(self->variables + var_idx*self->string_size, self->string_size, var);
}



extern "C" __declspec(dllexport) void myFunction(HWND hwndParent, int string_size, char *variables, stack_t **stacktop) {

nsis_t nsis={string_size, variables, stacktop};

//char * var_r0 = get_user_variable(&nsis, INST_R0);

//char buf[1024];

//pop_string(&nsis, buf, sizeof(buf));

//set_user_variable(&nsis, INST_R0, "10");
//push_string(&nsis, "20");

}



BOOL APIENTRY DllMain (HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved) {
    return TRUE;
}

기본 작동 방식

위의 코드에서 myFunction 함수로 구현해야할 함수이다. 함수 명이 NSIS에서 호출되는 함수이다. 호출 방식을 살펴 보자.
일단, 만들어진 DLL파일 명이 abc.dll 이고 구현된 함수 명이 myFunction이라고 하자. 그리고 DLL은 NSIS의 Plugins폴더에 복사해 놓는다.
NSIS 스크립트에서 아래와 같이 작성하면;

;...

abc::myFunction

;...
  1. 위의 호출에 의해 abc.dll이라는 파일을 찾고 있다면 동적로딩을 한다.
  2. 그리고 로딩한 dll에서 myFunction을 찾고 있다면 호출을 한다.
  3. 호출할 때에는 총 4개의 인자를 넘긴다. 여기서 중요한 것은 뒤쪽 3개 인자이다. NSIS와 값을 주고 받기 위해 사용된다.
  4. 호출된 함수는 결과를 스택 혹은 변수에 저장한다.
  5. NSIS에서 결과를 반환받아서 처리한다.

값을 주고 받는 방식은 변수 사용법과 스택 사용법이 있다. 변수는 09, R0R9, CMDLINE, INSTDIR, OUTDIR, EXEDIR, LANGUAGE를 사용한다. 또는 스택을 이용하여 push, pop을 통해서 값을 서로 주고 받는다.

;변수를 사용한 방식
$R0 = 10
abc::myFunction
MessageBox MB_OK "Result: $R0"
;스택을 사용한 방식
Push 10
abc::myFunction
Pop $R0
MessageBox MB_OK "Result: $R0"

참조

[1] http://nsis.sourceforge.net/Processes_plug-in

결론

사실 알고 보니 별거 아니다. 그냥 사용하면 된다. 그리고 값을 넘겨주고 받는 부분이 조금 복잡하지만 별거 없다. 문자열 처리하는데 힌트를 더 주면 string_size 인자는 nsis에서 사용하는 문자열 할당 크기이다. 즉, 모든 문자열 할당 크기는 기본값으로 string_size을 사용한다.

윈도우의 기능을 사용하여 쉽게 NSIS을 확장할 수있다는 장점이 있다. 그것도 쉽게...

모드 즐프~~

2011.ospace.

반응형