许多相关书籍或文章尝试以各种方式简化Windows程序的第一步,因为单单一个Hello程序就要上百行,怕把大家吓坏了。我却宁愿各位早一点接触正统写法,早一点看到全貌。Windows 的东西又多又杂,早一点一窥全貌是很有必要的。而且你会发现,经过有 条理的解释之后,程序代码的多寡其实构不成什么威胁(否则无字天书最适合程序员阅读)。再说,上百进程序代码哪算得了什么!
你可以从图1-2 得窥 Win32 应用程序的本体与操作系统之间的关系。Win32 程序中最具代表意义的动作已经在该图显示出来,完整的程序代码展示于后。本章后续讨论都围绕着此一程序。
稍后会出现一个makefile。关于makefile的语法,可能已经不再为大家所熟悉了。我想我有必要做个说明。所谓makefile,就是让你能够设定某个文件和某个文件相比——比较其产生日期。由其比较结果来决定要不要做某些你所指定的动作。例如:
generic.res : generic.rc generic.h
rc generic.rc
意思就是拿冒号(:)左边的generic.res和冒号右边的generic.rc 和generic.h 的文件日期相比。只要右边任一文件比左边的文件更新,就执行下一行所指定的动作。这动作可以是任何命令列动作,本例为rc generic.rc。
因此,我们就可以把不同文件间的依存关系做一个整理,以makefile 语法描述,以产生必要的编译、链接动作。makefile 必须以NMAKE.EXE(Microsoft 工具)或MAKE.EXE(Borland 工具)处理之,或其它编译器套件所附的同等工具(可能也叫做MAKE.EXE)处理之。
图 1-2 Windows 程序的本体与操作系统之间的关系
Generic.mak(请在DOS窗口中执行nmake generic.mak。环境设定请参考 p.224)
#0001 # filename : generic.mak
#0002 # make file for generic.exe (Generic Windows Application)
#0003 # usage : nmake generic.mak (Microsoft C/C++ 9.00) (Visual C++ 2.x)
#0004 # usage : nmake generic.mak (Microsoft C/C++ 10.00) (Visual C++ 4.0)
#0005
#0006 all: generic.exe
#0007
#0008 generic.res : generic.rc generic.h
#0009 rc generic.rc
#0010
#0011 generic.obj : generic.c generic.h
#0012 cl-c-W3-Gz-D_X86_-DWIN32 generic.c
#0013
#0014 generic.exe : generic.obj generic.res
#0015 link/MACHINE:I386 -subsystem:windows generic.res generic.obj /
#0016 libc.lib kernel32.lib user32.lib gdi32.lib
Generic.h
#0001 //---------------------------------------------------------------
#0002 // 文件名 : generic.h
#0003 //---------------------------------------------------------------
#0004 BOOL InitApplication(HANDLE);
#0005 BOOL InitInstance(HANDLE, int);
#0006 LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
#0007 LRESULT CALLBACK About(HWND, UINT, WPARAM, LPARAM);
Generic.c(粗体代表Windows API函数或宏)
#0001 //---------------------------------------------------------------
#0002 // Generic - Win32 程序的基础写法
#0003 // Top Studio * J.J.Hou
#0004 // 文件名 : generic.c
#0005 // 作者 : 侯俊杰
#0006 // 编译链接 : 请参考 generic.mak
#0007 //---------------------------------------------------------------
#0008
#0009 #include <windows.h> // 每一个 Windows 程序都需要含入此文件
#0010 #include "resource.h" // 内含各个 resource IDs
#0011 #include "generic.h" // 本程序之含入文件
#0012
#0013 HINSTANCE _hInst; // Instance handle
#0014 HWND _hWnd;
#0015
#0016 char _szAppName[] = "Generic"; // 程序名称
#0017 char _szTitle[]= "Generic Sample Application"; // 窗口标题
#0017 char _szTitle[]
#0018
#0019 //---------------------------------------------------------------
#0020 // WinMain - 程序进入点
#0021 //---------------------------------------------------------------
#0022 int CALLBACK WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
#0023 LPSTR lpCmdLine, int nCmdShow)
#0024 {
#0025 MSG msg;
#0026
#0027 UNREFERENCED_PARAMETER(lpCmdLine); // 避免编译时的警告
#0028
#0029 if (!hPrevInstance)
#0030 if (!InitApplication(hInstance))
#0031 return (FALSE);
#0032
#0033 if (!InitInstance(hInstance, nCmdShow))
#0034 return (FALSE);
#0035
#0036 while (GetMessage(&msg, NULL, 0, 0)) {
#0037 TranslateMessage(&msg);
#0038 DispatchMessage(&msg);
#0039 }
#0040
#0041 return (msg.wParam); // 传回 PostQuitMessage 的参数
#0042 }
#0043 //---------------------------------------------------------------
#0044 // InitApplication - 注册窗口类
#0045 //---------------------------------------------------------------
#0046 BOOL InitApplication(HINSTANCE hInstance)
#0047 {
#0048 WNDCLASS wc;
#0049
#0050 wc.style = CS_HREDRAW | CS_VREDRAW;
#0051 wc.lpfnWndProc= (WNDPROC)WndProc; // 窗口函数
#0052 wc.cbClsExtra= 0;
#0053 wc.cbWndExtra= 0;
#0054 wc.hInstance= hInstance;
#0055 wc.hIcon= LoadIcon(hInstance, "jjhouricon");
#0056 wc.hCursor= LoadCursor(NULL, IDC_ARROW);
#0057 wc.hbrBackground = GetStockObject(WHITE_BRUSH); // 窗口后台颜色
#0058 wc.lpszMenuName = "GenericMenu"; // .RC 所定义的窗体
#0059 wc.lpszClassName = _szAppName;
#0060
#0061 return (RegisterClass(&wc));
#0062 }
#0063 //---------------------------------------------------------------
#0064 // InitInstance - 产生窗口
#0065 //---------------------------------------------------------------
#0066 BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
#0067 {
#0068 _hInst = hInstance; // 储存为全局变量,方便使用。
#0069
#0070 _hWnd = CreateWindow(
#0071 _szAppName,
#0072 _szTitle,
#0073 WS_OVERLAPPEDWINDOW,
#0074 CW_USEDEFAULT,
#0075 CW_USEDEFAULT,
#0076 CW_USEDEFAULT,
#0077 CW_USEDEFAULT,
#0078 NULL,
#0079 NULL,
#0080 hInstance,
#0081 NULL
#0082 );
#0083
#0084 if (!_hWnd)
#0085 return (FALSE)
#0086
#0087 ShowWindow(_hWnd, nCmdShow); // 显示窗口
#0088 UpdateWindow(_hWnd); // 送出 WM_PAINT
#0089 return (TRUE);
#0090 }
#0091 //---------------------------------------------------------------
#0092 // WndProc - 窗口函数
#0093 //---------------------------------------------------------------
#0094 LRESULT CALLBACK WndProc(HWND hWnd, UINT message,
#0095 WPARAM wParam, LPARAM lParam)
#0096 {
#0097 int wmId, wmEvent;
#0098
#0099 switch (message) {
#0100 case WM_COMMAND:
#0101
#0102 wmId = LOWORD(wParam);
#0103 wmEvent = HIWORD(wParam);
#0104
#0105 switch (wmId) {
#0106 case IDM_ABOUT:
#0107 DialogBox(_hInst,
#0108 "AboutBox",// 对话框资源名称
#0109 hWnd, // 父窗口
#0110 (DLGPROC)About // 对话框函数名称
#0111 );
#0112 break;
#0113
#0114 case IDM_EXIT: // 使用者想结束程序。处理方式与 WM_CLOSE 相同。
#0116 DestroyWindow(hWnd);
#0117 break;
#0118
#0119 default:
#0120 return (DefWindowProc(hWnd, message, wParam, lParam));
#0121 }
#0122 break;
#0123
#0124 case WM_DESTROY: // 窗口已经被摧毁 (程序即将结束)。
#0125 PostQuitMessage(0);
#0126 break;
#0127
#0128 default:
#0129 return (DefWindowProc(hWnd, message, wParam, lParam));
#0130 }
#0131 return (0);
#0132 }
#0133 //---------------------------------------------------------------
#0134 // About - 对话框函数
#0135 //---------------------------------------------------------------
#0136 LRESULT CALLBACK About(HWND hDlg, UINT message,
#0137 WPARAM wParam, LPARAM lParam)
#0138 {
#0139 UNREFERENCED_PARAMETER(lParam); // 避免编译时的警告
#0140
#0141 switch (message) {
#0142 case WM_INITDIALOG:
#0143 return (TRUE); // TRUE 表示我已处理过这个消息
#0144
#0145 case WM_COMMAND:
#0146 if (LOWORD(wParam) == IDOK
#0147 ||LOWORD(wParam) == IDCANCEL) {
#0148 EndDialog(hDlg, TRUE);
#0149 return (TRUE); // TRUE 表示我已处理过这个消息
#0150 }
#0151 break;
#0152 }
#0153 return (FALSE); // FALSE 表示我没有处理这个消息
#0154 }
Generic.rc
#0001 //---------------------------------------------------------------
#0002 // 文件名 : generic.rc
#0003 //---------------------------------------------------------------
#0004 #include "windows.h"
#0005 #include "resource.h"
#0006
#0007 jjhouricon ICON DISCARDABLE "jjhour.ico"
#0008
#0009 GenericMenu MENU DISCARDABLE
#0010 BEGIN
#0011 POPUP "&File"
#0012 BEGIN
#0013 MENUITEM "&New", IDM_NEW, GRAYED
#0014 MENUITEM "&Open...", IDM_OPEN, GRAYED
#0015 MENUITEM "&Save", IDM_SAVE, GRAYED
#0016 MENUITEM "Save &As...", IDM_SAVEAS, GRAYED
#0017 MENUITEM SEPARATOR
#0018 MENUITEM "&Print...", IDM_PRINT, GRAYED
#0019 MENUITEM "P&rint Setup...", IDM_PRINTSETUP, GRAYED
#0020 MENUITEM SEPARATOR
#0021 MENUITEM "E&xit", IDM_EXIT
#0022 END
#0023 POPUP "&Edit"
#0024 BEGIN
#0025 MENUITEM "&Undo/tCtrl+Z", IDM_UNDO, GRAYED
#0026 MENUITEM SEPARATOR
#0027 MENUITEM "Cu&t/tCtrl+X", IDM_CUT, GRAYED
#0028 MENUITEM "&Copy/tCtrl+C", IDM_COPY, GRAYED
#0029 MENUITEM "&Paste/tCtrl+V", IDM_PASTE, GRAYED
#0030 MENUITEM "Paste &Link", IDM_LINK, GRAYED
#0031 MENUITEM SEPARATOR
#0032 MENUITEM "Lin&ks...", IDM_LINKS, GRAYED
#0033 END
#0034 POPUP "&Help"
#0035 BEGIN
#0036 MENUITEM "&Contents", IDM_HELPCONTENTS, GRAYED
#0037 MENUITEM "&Search for Help On...", IDM_HELPSEARCH, GRAYED
#0038 MENUITEM "&How to Use Help", IDM_HELPHELP, GRAYED
#0039 MENUITEM SEPARATOR
#0040 MENUITEM "&About Generic...", IDM_ABOUT
#0041 END
#0042 END
#0043
#0044 AboutBox DIALOG DISCARDABLE 22, 17, 144, 75
#0045 STYLE DS_MODALFRAME | WS_CAPTION | WS_SYSMENU
#0046 CAPTION "About Generic"
#0047 BEGIN
#0048 CTEXT "Windows 95", -1,0, 5,144,8
#0049 CTEXT "Generic Application",-1,0,14,144,8
#0050 CTEXT "Version 1.0",-1,0,34,144,8
#0051 DEFPUSHBUTTON "OK", IDOK,53,59,32,14,WS_GROUP
#0052 END