C語言的學習,一般的方式是,先學C,然後是C++,最好還要有組合語言和微機原理基礎,然後才是Visual C++。這樣的方式,對學習者來說,要花費很多時間和耐力。而在學校教學中,也沒有時間深入學習Windows編程的實用技術了。
其實,具有了C語言基礎後,再有一些基本的C++類的概念,就可以直接學習Windows C編程了。
一、走近Windows C語言
很多語言都把顯示一個“Hello,World!”做為第一個入門程式, C語言的第一個程式是這樣的:
#include<stdio.h>
main()
{
printf(“Hello,World!”);
}
如果把main函數寫成帶參數的main函數,應該是:
#include<stdio.h>
main(int arge,char *argv[])
{
printf(“Hello,World!”);
}
Windows C的第一個程式和這個程式在形式和原理上都是一致的,只是有兩點不同:
1. 主函數接收的形參不只是命令列中的字串的個數和字串的首地址。
2. C語言的很多函數在Windows C中都可以繼續使用,但象printf()螢幕顯示等函數就不能繼續使用了。因為Windows是多任務作業系統,螢幕已不再為某一個應用程式所專屬,Windows C應用程式要顯示字串,需要使用Windows提供的API函數,開自己的視窗
下面是一個最簡單的,顯示“Hello,World!”的Windows C程式:
#include<windows.h>
APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
MessageBox(NULL,"Hello,World!","第一個Windows C程式",MB_OK|MB_ICONASTERISK);
}
主函數的形參有四個:
1) Hinstance:接收程式運行時當前執行個體的控制代碼;
2) HprivInstance:前一個執行個體的控制代碼;
3) LpCmdLine:程式命令列指標;
4) NcmdShow:一個用來指定視窗顯示方式的整數。
這幾個參數的使用我們會在深入的學習中介紹的。
顯示Hello,Word!字串,我們使用了一個MessageBox函數,這個函數會在螢幕上顯示一個對話方塊,它的原型是:
int MessageBox(HWND hWnd,LPCTSTR lpText,LPCTSTR lpCaption,UNIT uType)
四個參數分別是:
1) HWnd:父視窗的控制代碼;
2) LpText:要顯示字串的指標;
3) LpCaption:對話方塊標題字串的指標;
4) UType:顯示在對話方塊上的小表徵圖的類型。
使用這個函數要包含windows.h標頭檔。
調試一下,怎麼樣?視窗上彈出了一個“第一個Windows C程式”對話方塊,上面有一行字:“Hello,World!”。
世界真的很美好啊!!
深入編程:
在C語言中,函數的聲明,如果沒有指明傳回值類型,預設值為void,這個程式的主函數就沒有傳回值。不過,在Windows編程時,我們最好養成個好習慣,指明函數的傳回值類型,因為在C++中,函數傳回值類型是不可以預設的。而我們在Windows C編程時,還是會用到C++的一些概念,這樣做,有利於以後深入地學習。
規範一點的程式應該是這樣的:
#include<windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
MessageBox(NULL,"Hello,World!","第一個Windows C程式",MB_OK|MB_ICONASTERISK);
return 0;
}
這裡,我們聲明的類型為int型,並且返回一個值0,這樣的函數就可以使用在複雜一點的函數調用中了。
在這一節中,我們有幾處都提到了控制代碼的概念,控制代碼和指標的概念不同,它是作為作業系統內部索引表中的一個值來使用的,這樣可以防止應用程式直接存取名對象的內部結構,體現了Windows資源管理的優越性。譬如說,一個視窗找開之後,好對應記憶體中的一個記憶體塊,這個視窗所在的記憶體快地址往往會由作業系統做動態調整,但其卻不會隨之變化。不過,通過它可以訪問這個視窗,所以在使用的時候,可以把它當做指標一樣看待。
二、 擷取本機電腦的主機名稱和IP地址
和C語言一樣,函數是Windows C編程的最基本的單位。不過,Windows C主要使用API函數,而網路編程則主要使用Winsock提供的API函數。
Winsock是90年代初,為了方便網路編程,由Microsoft聯合了其他幾家公司共同制定的一套WINDOWS下的網路編程介面,它是通過C語言的動態連結程式庫方式提供給使用者及軟體開發人員的,主要由winsock.h標頭檔和動態連結程式庫winsock.dll組成,目前有兩個版本:Winsock1.1和Winsock2.0。
在Win32平台上,訪問眾多的基層網路通訊協定,Winsock是首選介面。
用Visual C++6.0編譯Windows C程式,使用Winsock API函數時,首先要把wsock32.lib添加到它的庫模組中,否剛在連結的時候,會出現“error LNK2001”錯誤。添加wsock32.lib的具體步驟是:開啟工程菜單,選擇設定,在彈出的Project settings對話方塊中,點擊link選項卡,然後在對象/庫模組文字框中添加wsock32.lib。
最簡單的網路編程是擷取原生主機名稱和IP地址,這個程式使用了WSAStart()、WSAClenaup()、gethostname()、gethostbyname()四個winsock API函數,這四個函數的功能和使用方法介紹如下:
1. WSAStartup():
【函數原型】
int PASCAL FAR WSAStartup(WORD wVersionRequired, LPWSADATA lpWSAData);
【使用說明】
每一個使用winsock的應用程式,都必須進行WSAStart函數調用,並且只有在調用成功之後才能使用其它的winsock網路操作函數。
WVersionRequired:<輸入>表示欲使用的Winsock版本,這是一個WORD類型的整數,它的高位位元組定義的是次版本號碼,低位位元組定義的是主要版本號。
LpWSAData:<輸出>是一個指向WSADATA資料的指標。這個資料我們一般不使用。
傳回值:調用成功返回0;否則,返回出錯資訊。
2. WSAClenaup():
【函數原型】
int PASCAL FAR WSACleanup(void);
【使用說明】
winsock使用後,要調用WSACleanup函數關閉網路裝置,以便釋放其佔用的資源。
3.gethostname()
【函數原型】
int PASCAL FAR gethostname (char FAR * name, int namelen);
【使用說明】
該函數可以擷取本地主機的主機名稱,其中:
name:<輸出>用於指向所擷取的主機名稱的緩衝區的指標。
Namelen:<輸入>緩衝區的大小,以位元組為單位。
傳回值:若無錯誤,返回0;否則,返回錯誤代嗎。
4.gethostbyname()
【函數原型】
struct hostent FAR * PASCAL FAR gethostbyname(const char FAR * name);
【使用說明】
該函數可以從主機名稱資料庫中得到對應的“主機”。
該函數唯一的參數name就是前面調用函數gethostname()得到的主機名稱。若無錯誤,剛返回一個指向hostent結構的批針,它可以標識一個“主機”列表。
Hostent結構定義如下:
Struct hostent
{
char FAR * h_name;
char FAR FAR ** h_aliases;
short h_addrtype;
char FAR FAR ** h_addr_list;
}
其中:
h_name:<輸入>主機名稱地址(PC)。
h_aliases:一個由主機備用名組成的空中止數組。
H_addrtype:返回地址的類型,對於Winsock,這個域總是PF_INET。
H_lenth:每個地址的長度(位元組數),對應於PF_INET域應該為4。
H_addr_list:應該以null 指標結尾的主機地址的列表,返回的地址是以網路順序排列的。
其中,h_addr_list[0]存放的就是本地主機的4個位元組的IP地址,即:
h_addr_list[0][0].h_addr_list[0][1].h_addr_list[0][2].h_addr_list[0][3]
一個簡單的用訊息框顯示主機名稱和IP地址的來源程式如下:
#include<winsock.h>
int WSA_return;
WSADATA WSAData;
HOSTENT *host_entry;
char host_name[256];
char host_address[256];
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
WSA_return=WSAStartup(0x0101,&WSAData);
if(WSA_return==0)
{
gethostname(host_name,256);
host_entry=gethostbyname(host_name);
if(host_entry!=0)
{
wsprintf(host_address,"%d.%d.%d.%d",
(host_entry->h_addr_list[0][0]&0x00ff),
(host_entry->h_addr_list[0][1]&0x00ff),
(host_entry->h_addr_list[0][2]&0x00ff),
(host_entry->h_addr_list[0][3]&0x00ff));
MessageBox(NULL,host_address,host_name,MB_OK);
}
}
WSACleanup();
return 0;
}
深入編程:
前面顯示IP地址的時候,我們使用的是訊息框,規範一點的編程應該使用對話方塊,如何編輯一個對話方塊,很多書中都有介紹,編輯的對話方塊可參考圖5的運行介面。
標頭檔Get_IP.h如下:
BOOL APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,WPARAM wParam,LPARAM lParam);
這個程式只使用了一個對話方塊過程,一般把這個過程的聲明放在標頭檔中。
來源程式Get_IP.c:
#include<winsock2.h>
#include"Get_IP.h"
#include"resource.h" //這個標頭檔在建立資源的時候會自動產生,
//並會在插入資源時自動產生控制項標識號.
int WSA_return;
WSADATA WSAData;
HOSTENT *host_entry;
char host_name[256];
char host_address[256];
int APIENTRY WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
LPSTR lpCmdLine,int nCmdShow)
{
WSA_return=WSAStartup(0x0101,&WSAData);
if(WSA_return==0)
{
gethostname(host_name,256);
host_entry=gethostbyname(host_name);
if(host_entry!=0)
{
wsprintf(host_address,"%d.%d.%d.%d",
(host_entry->h_addr_list[0][0]&0x00ff),
(host_entry->h_addr_list[0][1]&0x00ff),
(host_entry->h_addr_list[0][2]&0x00ff),
(host_entry->h_addr_list[0][3]&0x00ff));
}
}
WSACleanup();
DialogBox(hInstance,"DIALOG1",NULL,(DLGPROC)Hostname_ipDlgPro);
return 0;
}
BOOL APIENTRY Hostname_ipDlgPro(HWND hDlg,UINT message,
WPARAM wParam,LPARAM lParam)
{
switch(message)
{
case WM_INITDIALOG:
return(TRUE);
case WM_COMMAND:
if(LOWORD(wParam)==IDOK)
{
SetDlgItemText(hDlg,IDC_EDIT1,host_name);
SetDlgItemText(hDlg,IDC_EDIT2,host_address);
SetDlgItemText(hDlg,IDCANCEL,"確定");
}
if(LOWORD(wParam)==IDCANCEL)
EndDialog(hDlg,TRUE);
return(TRUE);
break;
}
return(FALSE);
}
三、利用VisualC++6.0編譯Windows C程式
利用Visual C++6.0編譯Windows C程式一般要經過以下四個步驟:建立項目、添加代碼、添加資源和編譯連結。下面我們簡單地介紹一下程式上面介紹的規範的擷取原生主機名稱和IP地址程式的編譯過程:
(一) 建立項目
1.啟動MicrosoftVisualC++,然後在【檔案】菜單中先擇【建立】命令,彈出1所示的【建立】對話方塊:
2.在【建立】對話方塊中,系統開啟的是預設的【工程】選項卡,【工程】選項卡左側的列表框中有多種建立工程的方式,我們選中“Win32 Application”選項。
3. 在【位置】文字框中輸入建立工程的路徑(例如:F:/),在【工程】文字框中輸入工程名稱(例如:Get_IP)。
4. 選中【平台】列表框中的Win32複選框,然後單擊【確定】按鈕。
5. 在隨後的對話方塊中,都選擇預設設定,完成後,進入圖2示介面:
(二) 添加代碼
在VisualC++6.0中,原始碼一般存放在原始碼檔案和標頭檔中,往項目中添加原始碼是非常方便的,為項目建立一個原始碼檔案一般要按下述方法操作:
1. 選擇【工程】|【添加工程】|【建立】選項,彈出圖3所示【建立】對話方塊:
2. 在對話方塊的【檔案】選項卡中,左側的列表框選中“C++ Source File”選項,右側選中【添加工程】複選框,並在【檔案】文字框中輸入源檔案名稱(例如:Get_IP.c)。
3. 單擊【確定】按鈕,【建立】對話方塊將被閉,使用者就可以在建立的Get_IP.c中輸入程式的原始碼了。
4. 添加標頭檔Get_IP.h的方法和上面所述過程一樣,只是在【檔案】選項卡中,左側的列表框要先中“C/C++ Header File”選項。在【檔案】文字框中輸入標頭檔名(例如:Get_IP.h)。
(三) 添加資源
在添加資源前,必須在項目中先添加一個資源檔,然後可利用Visual C++6.0提供的資源編輯器為項目建立一個資源,具體步驟如下:
1. 選擇【工程】|【添加工程】|【建立】選項,彈出圖3所示【建立】對話方塊。
2. 在對話方塊的【檔案】選項卡中,左側的列表框選中“Rsource Script”選項,右側選中【添加工程】複選框,並在【檔案】文字框中輸入資源檔名(例如:Get_IP.rc)。
3. 單擊確定,回到主視窗後,選擇【插入】|【資源】選項,開啟【插入資源】對話方塊,4所示, 在【資源類型】列表框中選中“Dialog”選項,單擊【建立】按鈕,返回主視窗後,即可利用對話方塊編輯器進行編輯了。編輯後的對話方塊
(四) 編譯連結
在添加了原始碼與資源檔後,就可以對程式編譯串連了,可按Ctrl+F7鍵編譯,按F7鍵串連,按Ctrl+F5鍵運行程式。在串連前是要注意,資源檔Get_IP.rc也要進行編譯。
由於這個程式引用了Winsock API函數,在編譯串連前,還要添加wsock32.dll,具體方法前面已經介紹過,這裡就不再贅述了。
一點看法:
利用C語言編寫Windows應用程式有兩種方式:一種是Windows C編程方式,另一種是Visual C++編程方式。在一般情況下,Visual C++編程方式編寫的程式原始碼量小、開發時的工作量小、工作難度也較小,但編譯後的代碼量較大,運行速度略低;而Windows C編程方式編寫的程式原始碼量雖然較大,但可執行代碼效率高。隨著技術的進步,Visual C++編程方式已被廣泛採用,但象網路編程等一些對速度要求高、對硬體操作較多的程式,大多數還是用Windows C編程方式開發的。另外,學習Windows C程式設計,還有助於更深入地瞭解Windows的內幕和Windows API。
從教學角度講,在學生具備了C語言和其它一些前置課程基礎後,直接進入Windows C網路編程等實用編程技術課程,不僅可以讓學生儘早地接觸到前沿的實用編程技術,而且還可以極大地調動學生的學習積極性,在有限的時間裡,學到更多的知識和技術。
2.在【建立】對話方塊中,系統開啟的是預設的【工程】選項卡,【工程】選項卡左側的列表框中有多種建立工程的方式,我們選中“Win32 Application”選項。
3. 在【位置】文字框中輸入建立工程的路徑(例如:F:/),在【工程】文字框中輸入工程名稱(例如:Get_IP)。
4. 選中【平台】列表框中的Win32複選框,然後單擊【確定】按鈕。
5. 在隨後的對話方塊中,都選擇預設設定,完成後,進入圖2示介面:
(二) 添加代碼
在VisualC++6.0中,原始碼一般存放在原始碼檔案和標頭檔中,往項目中添加原始碼是非常方便的,為項目建立一個原始碼檔案一般要按下述方法操作:
1. 選擇【工程】|【添加工程】|【建立】選項,彈出圖3所示【建立】對話方塊:
2. 在對話方塊的【檔案】選項卡中,左側的列表框選中“C++ Source File”選項,右側選中【添加工程】複選框,並在【檔案】文字框中輸入源檔案名稱(例如:Get_IP.c)。
3. 單擊【確定】按鈕,【建立】對話方塊將被閉,使用者就可以在建立的Get_IP.c中輸入程式的原始碼了。
4. 添加標頭檔Get_IP.h的方法和上面所述過程一樣,只是在【檔案】選項卡中,左側的列表框要先中“C/C++ Header File”選項。在【檔案】文字框中輸入標頭檔名(例如:Get_IP.h)。
(三) 添加資源
在添加資源前,必須在項目中先添加一個資源檔,然後可利用Visual C++6.0提供的資源編輯器為項目建立一個資源,具體步驟如下:
1. 選擇【工程】|【添加工程】|【建立】選項,彈出圖3所示【建立】對話方塊。
2. 在對話方塊的【檔案】選項卡中,左側的列表框選中“Rsource Script”選項,右側選中【添加工程】複選框,並在【檔案】文字框中輸入資源檔名(例如:Get_IP.rc)。
3. 單擊確定,回到主視窗後,選擇【插入】|【資源】選項,開啟【插入資源】對話方塊,4所示, 在【資源類型】列表框中選中“Dialog”選項,單擊【建立】按鈕,返回主視窗後,即可利用對話方塊編輯器進行編輯了。編輯後的對話方塊
(四) 編譯連結
在添加了原始碼與資源檔後,就可以對程式編譯串連了,可按Ctrl+F7鍵編譯,按F7鍵串連,按Ctrl+F5鍵運行程式。在串連前是要注意,資源檔Get_IP.rc也要進行編譯。
由於這個程式引用了Winsock API函數,在編譯串連前,還要添加wsock32.dll,具體方法前面已經介紹過,這裡就不再贅述了。
一點看法:
利用C語言編寫Windows應用程式有兩種方式:一種是Windows C編程方式,另一種是Visual C++編程方式。在一般情況下,Visual C++編程方式編寫的程式原始碼量小、開發時的工作量小、工作難度也較小,但編譯後的代碼量較大,運行速度略低;而Windows C編程方式編寫的程式原始碼量雖然較大,但可執行代碼效率高。隨著技術的進步,Visual C++編程方式已被廣泛採用,但象網路編程等一些對速度要求高、對硬體操作較多的程式,大多數還是用Windows C編程方式開發的。另外,學習Windows C程式設計,還有助於更深入地瞭解Windows的內幕和Windows API。
從教學角度講,在學生具備了C語言和其它一些前置課程基礎後,直接進入Windows C網路編程等實用編程技術課程,不僅可以讓學生儘早地接觸到前沿的實用編程技術,而且還可以極大地調動學生的學習積極性,在有限的時間裡,學到更多的知識和技術。