31 Jul
2011
Posted in: 코드
By    2 Comments

리소스 풀어 헤치기


리소스 풀어 헤치기
by 신영진(YoungJin Shin), codewiz at gmail.com, @codemaru, http://www.jiniya.net

디버그뷰나 프로세스 익스플로러같은 프로그램을 보면 실행 파일 하나로 구성되어 있는데 프로그램을 실행하면 드라이버를 로딩하는 것을 볼 수 있습니다. 어떻게 하는 걸까요? 답은 간단한데요. 해당 드라이버 파일을 리소스에 포함시켜 놓고 런타임에 풀어서 사용하는 방식입니다. 리소스에 추가하는 방법은 간단합니다. Visual Studio 리소스 뷰에서 오른쪽 클릭하시고 임포트 하시면 됩니다. sys 파일 같은건 안보이시죠. *.*로 필터링한다음 추가하시면 됩니다. 그러면 어떤 타입을 추가할지 물어볼텐데요. 아무렇게나 이름을 지어 주시면 됩니다. Binary 이런 식으로요. 자 그렇다면 이제 이렇게 추가된 리소스 파일을 런타임에 어떻게 조작하는지를 알아보도록 합시다.

조작하는 API는 소 심플합니다. FindResource, LoadResource, SizeOfResource, LockResource라는 API가 사용됩니다. 각각의 함수 사용 방법을 알아보도록 합시다.

HRSRC FindResource(HMODULE hModule, LPCTSTR lpName, LPCTSTR lpType);

FindResource 함수입니다. 이 함수는 우리가 분리해 내려고 하는 리소스를 찾는데 사용됩니다. hModule에는 리소스를 찾을 DLL의 모듈 핸들을 넣어주면 됩니다. NULL을 전달하면 현재 프로세스의 실행 파일 모듈에서 찾습니다. lpName에는 리소스 이름을 전달합니다. MAKEINTRESOURCE를 사용해서 리소스 ID를 문자열로 변환한 것을 대입해 주면 됩니다. 리소스 ID가 IDR_BINARY1이라면 MAKEINTRESOURCE(IDR_BINARY1)을 전달하면 되겠죠. lpType에는 리소스 타입을 넣어줍니다. 리소스 타입이 Binary라면 “Binary”를 전달하면 됩니다. 이렇게 넣어주면 리소스를 찾아서 해당 리소스의 핸들을 넘겨줍니다. 실패하면 NULL이 리턴되겠죠.

DWORD SizeofResource(HMODULE hModule, HRSRC hResInfo);

러소스의 크기를 구할 때 사용하는 함수입니다. hModule에는 모듈 핸들을 hResInfo에는 FindResource를 통해서 찾은 리소스 핸들을 넣어줍니다. 성공한 경우에는 리소스 크기를, 실패한 경우에는 0을 리턴합니다.

HGLOBAL LoadResource(HMODULE hModule, HRSRC hResInfo);

리소스를 로드하는 함수입니다. hModule에는 모듈 핸들을 hResInfo에는 FindResource로 찾은 리소스 핸들을 전달하면 되겠습니다. 해당 리소스의 포인터가 리턴됩니다.

LPVOID LockResource(HGLOBAL hResData);

마지막 함수입니다. LoadResource 한 메모리의 실제 포인터를 반환합니다. hResData에는 LoadResource에서 반환한 포인터를 넣어줍니다. 그럼 실제 포인터가 리턴됩니다.

종합해서 함수 하나로 만들어 봅시다. GetResourceInfo라는 함수입니다. module에서 type, name의 리소스를 찾아서 해당 리소스의 포인터를 data에 크기를 rsize에 있는지 없는지를 리턴 값으로 리턴해 주는 함수입니다.

BOOL WINAPI GetResourceInfo(HMODULE module
                            , LPCTSTR type
                            , LPCTSTR name
                            , PVOID *data
                            , SIZE_T *rsize)
{
    DWORD size;
    HGLOBAL mem;
    PVOID ptr;
    HRSRC src;

    src = FindResourceW(module, name, type);
    if(!src)
        return FALSE;

    size = SizeofResource(module, src);
    if(!size)
        return FALSE;

    mem = LoadResource(module, src);
    if(!mem)
        return FALSE;

    ptr = LockResource(mem);
    if(!ptr)
        return FALSE;

    __try
    {
        if(data)
            *data = ptr;

        if(rsize)
            *rsize = size;
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
        return FALSE;
    }

    return TRUE;
}

여기까지만 설명하고 마칠까 하는데 아마 똑똑한 분들이라면 먼가 찜찜한 기분이 드실 거예요. 화장실가서 볼 일 보고 뭔가 정리하지 않고 나온 느낌이겠죠. 실컷 로딩하고 락하고 했는데 그것들을 해제하는 작업에 대해서는 하나도 설명하지 않았으니 말입니다. UnlockResource, FreeResource와 같은 함수들이 있긴 한데, 그것들은 사용하지 않아도 됩니다. 왜냐하면 여기서 사용하는 리소스 핸들을 구하고 하는 함수들은 로드된 모듈에서 포인터를 참조해 오는 일이거든요. 여러분이 획득한 모든 포인터 내지는 리소스는 hModule이 FreeLibrary 되는 시점에 같이 사라집니다. 따라서 별도로 해제할 필요가 없는 셈이죠. 참고로 그래도 나는 해제하겠다고 UnlockResource, FreeResource를 호출한들 실제 해당 함수에서 하는 일은 return 밖에는 없답니다.

Browser does not supports flash movie

  • 트랙백 주소: http://www.jiniya.net/wp/archives/4366/trackback

관련 글

  • $Zero

    글 잘읽었습니다. 처음보는 함수라 이것저것 찾는데 정말 쉽게 설명해주셨네요 .

    하나 궁금한것이 있는데 답변 해주실수 있으신지요 ?

    리소스의 핸들을 반환받기위해 사용한 변수 HRSRC 는 어떠한 형태의 타입인가요 ?

    모든 리소스의 핸들은 HRSRC로 받아야만 하나요 ? HWND로는 받을 수 없을까요 ?

  • codewiz

    $Zero // HRSRC나 HWND같은 것들은 모두 그냥 포인터입니다. 보통 핸들이란 이름으로 부르죠. 타입을 구분하는 것 외에 큰 의미는 없으니 HWND로 사용하셔도 무방합니다.

    아래는 제가 2009년 7월에 마이크로소프트에 연재했던 핸들과 콜백의 메카니즘이라는 글입니다. 읽어보시면 이러한 타입이 내부적으로 어떻게 구현되는 것들인지 이해하시는데 도움이 될 것 같네요.

    http://www.jiniya.net/lecture/maso/rwp4.pdf