我的第一個Windows程序, Hello,world!
在Charles Petzold的書中, 作者首先回顧了下C語言在控制台下通過標准輸入輸出函數輸出"Hello,world!"的程序, 代碼如下:
#include <stdio.h> int main() { printf( "Hello,world!\n" ) ; return 0 ; }
不過令我疑惑的是, 在我這本《Windows程序設計》(第五版) 2010年9月第5版的書中, 卻將這段經典代碼的"return 0 ;"寫成了"Return 0 ;", 筆者嘗試了下, "Return 0 ;"在VC6的編譯器和GCC下都是報錯的, 難道是我買到盜版書了?
可能是由於印刷失誤, 這里就先不管他了。
同樣, Charles Petzold也給出了Windows版的"Hello,world!"(其實他給出的是Hello,windows 98!), 代碼如下:
#include<windows.h> int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow ) { MessageBox( NULL, TEXT("Hello,world!"), TEXT("MessageBox"), 0 ); return 0; }
通過Visual C++ 6.0的"文件"-->"新建"-->"工程", 選擇"Win32 Application"創建一個空的項目, 再在這個項目中新建一個"文件", 文件類型為"C++ Source File", 文件以.c為擴展名, 將上面的代碼敲入或者復制粘貼到這個文件內容中, 經過編譯運行就可以得到一個對話框了, 趕緊截圖留念吧!
在這個對話框中, 有標題欄, 標題欄的內容是"MessageBox", 對話框的內容為"Hello,world!", 還有一個"確定"按鈕, 而且, 沒有那個黑框框窗口, 一切看起來都是那么美好, 來一起看看這段Windows版的Hello,world!吧!
-----------------------------------------------------------
*Windows版的Hello,world!代碼注釋*
*第一行
#include<windows.h>
稍微有點C語音基礎的都能明白, 這是要包含"windows.h"這個頭文件, 也就說明, 在下面的代碼中, 要用到這個頭文件, 如果我們將#include<windows.h>這句去掉再進行編譯看看會有什么情況:
Compiling... HelloWorld.c d:\project\lwinc\helloworld\helloworld.c(3) : error C2061: syntax error : identifier 'WinMain' d:\project\lwinc\helloworld\helloworld.c(3) : error C2059: syntax error : ';' d:\project\lwinc\helloworld\helloworld.c(3) : error C2146: syntax error : missing ')' before identifier 'hInstance' d:\project\lwinc\helloworld\helloworld.c(3) : error C2061: syntax error : identifier 'hInstance' d:\project\lwinc\helloworld\helloworld.c(3) : error C2059: syntax error : ',' d:\project\lwinc\helloworld\helloworld.c(3) : error C2059: syntax error : ')' 執行 cl.exe 時出錯.
意料之內的, 報錯了, 第一條就是標識符"WinMain"錯誤, 具體的細節暫時就不深究了, 繼續向下看。
*關於windows.h頭文件:
在windows.h這個頭文件中, 實際上已經包含了若干的其他相關的頭文件, 用書上的話說, windows.h是個非常重要的包含文件, 其中包含的其他比較重要的頭文件有:
■ WINDEF.H 基本數據類型定義
■ WINNT.H 支持Unicode的類型定義
■ WINBASE.H 內核函數
■ WINUSER.H 用戶界面函數
■ WINGDI.H 圖像設備接口函數
不過我還是好奇windows.h到底包含了那些頭文件, 找到VC6的安裝目錄, 打開Include文件夾, 找到WINDOWS.H並打開, 雖說看不太懂, 但找#include關鍵詞還是無壓力的.
除去上面的5個還有:
■ WINRESRC.H ■ EXCPT.H ■ STDARG.H ■ WINNLS.H ■ WINCON.H
■ WINVER.H ■ WINREG.H ■ WINNETWK.H ■ CDERR.H ■ DDE.H
■ DDEML.H ■ DLGS.H ■ LZEXPAND.H ■ MMSYSTEM.H ■ NB30.H
■ RPC.H ■ SHELLAPI.H ■ WINPERF.H ■ WINSOCK2.H ■ MSWSOCK.H
■ WINSOCK.H ■ WINCRYPT.H ■ COMMDLG.H ■ WINSPOOL.H ■ OLE.H
■ OLE2.H ■ WINWLM.H ■ WINSVC.H ■ MCX.H ■ IMM.H
*程序的入口
在Win32控制台程序(Win32 Console Application)中, 應用程序的入口為main()函數, windows程序的程序入口和win32控制台程序的入口類似, 為WinMain()函數.
程序的入口函數在WINBASE.H作出了聲明, 聲明如下:
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd );
其中由聲明可以看出, WinMain函數的返回值被定義為int型;
WINAPI為WinMain函數的調用規則, 在WINDEF.H對"WINAPI"作出了如下宏定義:
#define WINAPI __stdcall
說明, WinMain函數的調用規則為"__stdcall"方式, 對於"__stdcall"調用規則, 現在暫時先不去深究, 知道有這么回事就行, 以后會詳細了解到的, 現在如果深究"__stdcall"就偏離了這篇博文的主題。
*WinMain函數的參數:
1>. WinMain的第一個參數 HINSTANCE hInstance, 用書上的解釋為"實例句柄", 由於第一次接觸C語言Windows程序設計, 對這個句柄的概念也不是很了解, 去百科了下, 句柄的解釋為"一個句柄是指使用的一個唯一的整數值,即一個四字節長的數值,來標志應用程序中的不同對象和同類對象中的不同的實例,諸如,一個窗口,按鈕,圖標,滾動條,輸出設備,控件或者文件等。"——引用自百度百科->句柄。
筆者是這樣對句柄進行理解的, 在一個應用程序中, 通常創建了很多的窗口、按鈕、標簽, 或者使用了一個文件等, 在程序的任何地方, 只要能夠獲得這個被稱為句柄的東西, 就能夠找到該控件或者窗口在內存中的位置, 從而對其進行操作。感覺有點像帶參數的main函數, 只是這里的主函數參數為一個句柄。
2>. WinMain函數的第二個參數, 同樣是個實例句柄, 但書上又進一步解釋說在32位的Windows程序設計中, WinMain函數的實例句柄概念已不再采用, 因此WinMain的第二個參數通常總是NULL。
筆者的見解: 感覺馬上就要暈了, 疑問一: "因此WinMain的第二個參數通常總是NULL", 那么第一個呢?WinMain的第一個參數會不會也可以是NULL呢? 疑問二: WinMain函數的參數從何而來?是操作系統么?帶着疑問繼續向下看。
3>. WinMain的第三個參數是用來運行程序的命令行, PSTR: 用來指向一個字符串的指針類型, szCmdLine, sz:表示以0結尾的字符串; 目的是通過命令行方式運行程序並向主函數中傳入參數, 應該就像給main函數傳入參數一樣;
4>. WinMain的第四個參數是一個int型參數, 用來指明程序(窗口)最初如何被顯示, 例如最小化?最大化?全屏?
筆者的見解: 應該很有用, 經常見一些游戲一啟動就是全屏的, 但是這個參數也是操心系統傳給程序的么?因為從平時運行Windows程序時都是直接雙擊, 並沒有通過命令行給它傳入參數, 在編程時應該對程序啟動時的顯示方式有交代才對, 這樣系統再運行時再把這個交代的參數傳入給程序告訴程序啟動時應該如何顯示.
(在"筆者的見解"部分的觀點均為筆者個人的見解, 如果有誤肯定指正, 我會及時更正, 避免誤導其他讀者。)
*WinMain函數函數體的MessageBox函數:
MessageBox(), 名如其"人", 不用猜也知道這個就是顯示一個對話框的函數, 打開API文檔,MSDN Library通過索引找到MessageBox函數, 發現其聲明如下:
int MessageBox( HWND hWnd, // handle of owner window, 窗口的一個句柄 LPCTSTR lpText, // address of text in message box, 一個文本(字符串)的指針 LPCTSTR lpCaption, // address of title of message box, 標題字符串的指針 UINT uType // style of message box, 對話框的風格 );
在上面示例中對MessageBox函數的調用如下:
MessageBox( NULL, TEXT("Hello,world!"), TEXT("MessageBox"), 0 );
第一個參數窗口的句柄的實參為NULL, 意思為不屬於任何窗口.
第二個參數為對話框的內容, 第三個參數為對話框的標題, 但是這兩個參數都使用了一個TEXT()的函數, 書上講使用TEXT()的目的是將這些字符串打包到TEXT宏代碼里面, 筆者嘗試了不用這個TEXT()函數而直接像這樣:
MessageBox( NULL, "Hello,world!", "MessageBox", 0 );
調用並沒有出現警告或者報錯信息, 具體使用TEXT()函數的詳細原因還不太清楚, 暫時先在這里畫個圈。
第四個參數為對話框的風格, 一些以MB_開頭的一些常量的組合, 可以使用OR(|)運算進行組合, 這些常量定義在WINUSER.H中, 例如常用的有:
1>.對話框按鈕類型:
#define MB_OK 0x00000000L //僅有一個"確定"按鈕 #define MB_OKCANCEL 0x00000001L //"確定" + "取消" #define MB_ABORTRETRYIGNORE 0x00000002L //"終止" + "重試" + "忽略" #define MB_YESNOCANCEL 0x00000003L //"是" + "否" + "取消" #define MB_YESNO 0x00000004L //"是" + "否" #define MB_RETRYCANCEL 0x00000005L //"重試" + "取消"
2>.對話框中的圖標類型:
#define MB_ICONHAND 0x00000010L //一個紅X的錯誤/停止圖標 #define MB_ICONQUESTION 0x00000020L //一個問號的詢問圖標 #define MB_ICONEXCLAMATION 0x00000030L //一個黃色感嘆號的警告圖標 #define MB_ICONASTERISK 0x00000040L //一個帶有i的信息提示圖標
同時, 在這些圖標中有的還可以用其他名稱代替, 這些別名在WINUSER.H的定義如下:
#define MB_ICONWARNING MB_ICONEXCLAMATION //警告 #define MB_ICONERROR MB_ICONHAND //錯誤 #define MB_ICONINFORMATION MB_ICONASTERISK //信息 #define MB_ICONSTOP MB_ICONHAND //停止
------------------------------------------------------------------
下午的學習暫時就到這里, 在學習的過程中出現了幾個疑問, 在這里對疑問進行下總結:
疑問一: 在書中介紹WinMain函數的參數時講到 "因此WinMain的第二個參數通常總是NULL", 那么第一個呢?WinMain的第一個參數也可以是NULL嗎?
疑問二: WinMain函數的參數從何而來?是操作系統么?
疑問三: 使用TEXT()函數的作用是什么呢?
在這篇博文中筆者表達可幾個個人的見解(也可以說是猜測), 如果有錯誤或者不妥之處懇請指出, 筆者將立即更正, 避免誤導其他讀者。
Wid, 2012.10.06