//---------------------------------------------------------------------------
// 김태성의 C++빌더 강좌 : 최소 실행파일크기 Win32 실제 어플 만들기.
//---------------------------------------------------------------------------
며칠전에는 C++빌더로 최소크기의 win32 어플리케이션을 제작하는 것을 어떻게 하는지 살펴보았다.
그때는 방법 소개만을 목적으로 하였으므로, 어플리케이션에 대한 인스턴스 핸들인 HInstance 가 없어
윈도 창을 만들 수 없었다.
이번에는 실제로 윈도 창이 있는 정상적인 진짜 어플을 제작해 보자.
방법은 WinMain으로 넘기는 파라메터중에 HInstance와 nCmdShow 값만 제대로 세팅해주면 된다.
물론 커맨드라인 인자를 받는 어플을 만드는 경우는 그것도 처리해야겠지만,
윈도 환경이 되면서 거의 쓰이지 않으므로 여기서는 생략한다.
아래는 메인의 소스이다.
//---------------------------------------------------------------------------
// 최소 크기 win32 어플리케이션 만들기. 2번째 버전.
// HINSTANCE 를 받을 수 있게 했다.
// 윈도를 만들려면 있어야 하기 때문이다.
//
// Written by 김태성
//---------------------------------------------------------------------------
#define _fmode xxx_fmode
#define _fileinfo xxx_fileinfo
#include <windows.h>
#pragma hdrstop
#undef _fmode
#undef _fileinfo
//---------------------------------------------------------------------------
#pragma link "c0w32.obj"
#pragma link "cw32.lib"
//WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow);
// cw32.lib에 들어 있는 것을 여기서 다시 구현한다.
extern "C"
{
void _startup()
{
WinMain(GetModuleHandle(NULL), NULL, NULL, SW_RESTORE); // 원래는 파라메터를 해석해서 보내줘야 함.
exit(0); // 정상종료를 위해 강제종료루틴을 부른다.
}
void __CRTL_VCL_Init(void) { }
void _RTLENTRY __declspec(naked) __CRTL_VCLLIB_Linkage (void)
{
__emit__(0xC3); /* asm ret */
}
void __CRTL_MEM_UseBorMM() { }
void _ExceptInit() { }
void __CRTL_TLS_Alloc() { }
void __CRTL_TLS_Free() { }
void __CRTL_TLS_GetValue() { }
void __CRTL_TLS_SetValue() { }
void __CRTL_TLS_InitThread() { }
void __CRTL_TLS_ExitThread() { }
void _matherr() { }
void _matherrl() { }
void _GetExceptDLLinfoInternal() { }
void _wargv_expand_ptr() { }
void _argv_expand_ptr() { }
void _handle_setargv() { }
void _handle_exitargv() { }
void _handle_wsetargv() { }
void _handle_wexitargv() { }
void _turboFloat() { }
void _setargv__() { }
void _setenvp__() { }
void _fmode() { }
void _fileinfo() { }
}
//---------------------------------------------------------------------------
LRESULT CALLBACK WndProc(HWND,UINT,WPARAM,LPARAM);
HINSTANCE g_hInst;
char *szClass = "CBuilderSample";
#pragma argsused
WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
HWND hWnd;
MSG Message;
WNDCLASS WndClass;
g_hInst = hInstance;
WndClass.cbClsExtra=0;
WndClass.cbWndExtra=0;
WndClass.hbrBackground=(HBRUSH)GetStockObject(WHITE_BRUSH);
WndClass.hCursor=LoadCursor(NULL,IDC_ARROW);
WndClass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
WndClass.hInstance=hInstance;
WndClass.lpfnWndProc=(WNDPROC)WndProc;
WndClass.lpszClassName= szClass;
WndClass.lpszMenuName=NULL;
WndClass.style=CS_HREDRAW | CS_VREDRAW;
RegisterClass(&WndClass);
hWnd=CreateWindow(szClass,szClass,WS_OVERLAPPEDWINDOW | WS_VSCROLL,
CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,
NULL, (HMENU)NULL, hInstance, NULL);
ShowWindow(hWnd, nCmdShow);
while(GetMessage(&Message,0,0,0))
{
TranslateMessage(&Message);
DispatchMessage(&Message);
}
return Message.wParam;
}
LRESULT CALLBACK PASCAL WndProc(HWND hWnd,UINT iMessage,WPARAM wParam,LPARAM lParam)
{
HDC hdc;
PAINTSTRUCT ps;
char *s;
switch(iMessage)
{
case WM_DESTROY:
PostQuitMessage(0);
return 0;
case WM_LBUTTONDOWN:
MessageBox(hWnd,
"붉은악마의 상징 도깨비 문양은 바로 배달국 14대 환웅이신 치우천황을 뜻합니다.\n\n"
"중국인의 시조인 헌원과의 73차례에 걸친 전쟁에서 모두 승리했던 분입니다.\n\n"
"그 중에서도 탁록의 전투가 유명하며, 치우천황은 최초로 철로 무기를 만들어 쓰셨던 분입니다.\n\n"
"중국인은 치우천황 소리만 들어도 벌벌 떨었고, 이후 치우천황은 전쟁에 나가는\n\n"
"모든 장수들이 반드시 천제를 지내는 숭배의 대상이 되었습니다.\n\n"
"중국역사에서 큰 전쟁을 치루는 왕들은 모두 치우천황에서 제사를 올리고 출정했습니다\n\n"
"단순한 도깨비 문양이 아니며 대륙을 호령했던 우리 조상의 혼입니다\n\n",
"위대한 우리의 조상",MB_OK);
return 0;
case WM_PAINT:
hdc=BeginPaint(hWnd,&ps);
SetTextAlign(hdc, TA_CENTER);
s = "C++ 개발툴의 중심 C++Builder";
TextOut(hdc,300,60, s, strlen(s));
s = "클릭해 보세요";
TextOut(hdc,300,100, s, strlen(s));
EndPaint(hWnd,&ps);
return 0;
}
return(DefWindowProc(hWnd,iMessage,wParam,lParam));
}
//---------------------------------------------------------------------------
WinMain 함수 안은 그냥 정상적인 어플일 뿐이다. 달리 설명할 것은 아무것도 없다.
다만 WinMain을 호출하는 startup 함수에서
어떻게 해서 WinMain을 호출하는가만 살펴보면 된다.
WinMain(GetModuleHandle(NULL), NULL, NULL, SW_RESTORE);
이것은 커맨드라인 인자를 제외하고는 정상적으로 WinMain을 호출하는 방식과 같다.
그러므로 정상적인 어플의 동작을 가능케 한다.
컴파일해보면 배포가능한 실행화일이 만들어 지는데, 정확하게 8KB가 된다.
필자가 중간에 메시지를 많이 넣어 크기가 이전보다 약간 증가했는데 이 크기 역시,
VC++이나 델파이로 순수 win32 어플을 만들때 보다 작은,
C++ 컴파일러로 만들 수 있는 가장 작은 크기이다.
중간에
#pragma link "cw32.lib"
로 라이브러리를 링크하는데 이미 프로그램에서 구현한 것이 링크 규약상
우선 링크되어 cw32.lib는 무시되므로 문장이 있어도 상관 없다.
이 프로젝트는 C++Builder6에서 테스트되었으나,
C++Builder5나 그 이하 버전에도 그대로 적용될 수 있다.
뿐만 아니라 최신의 BDS 2006에서도 비슷한 방법을 사용할 수 있으리라 생각된다.
BDS2006이 설치되어 있지 않아 실험은 하지 못했으나, 동작 원리가 대동소이 하기 때문이다.
실제 제작한 파일을 첨부해서 올린다.
//---------------------------------------------------------------------------
// 김태성 cppbuilder@naver.com
// 이 강좌는 아무 곳에나 비상업적인 용도에 한해 내용 변경 없이 원래의 출처를 명시하여
// 배포할 수 있습니다. 단, 필자의 이메일로 게제 사실을 통보해야 합니다.
//---------------------------------------------------------------------------
|