閱讀316 返回首頁    go 阿裏雲 go 技術社區[雲棲]


windows常用API函數

係統API查詢


https://www.vbgood.com/api.html


https://hi.baidu.com/3582077/item/9cc3483b581f53c5392ffae3


第一個:FindWindow根據窗口類名或窗口標題名來獲得窗口的句柄,該函數返回窗口的句柄,


這個函數的定義是這樣的 HWND WINAPI FindWindow(LPCSTR lpClassName,LPCSTR lpWindowName);第一個參數填窗口的類名,第二個填窗口的標題名,其實是不需要同時填兩個參數的,也就是說,你隻要知道窗口的類名或窗口的標題就可以了,沒有的那個就用NULL代替。比如現在有一個窗口名為"無標題.txt - 記事本"的記事本程序。那麼我就可以用上麵的函數獲得這個窗口的句柄,那獲得了這個窗口的句柄我可以幹什麼呢?作用可大了,因為很多操作窗口的函數,都需要窗口句柄作參數,如移動、改變窗口大小的MoveWindow函數,在這裏舉個例子,大家就更能體會到這個FindWindow的用法、用處。


FindWindow例子:已知一個窗口名稱,寫一個程序關閉該窗口,假設當前電腦正有一個窗口名為"無標題.txt - 記事本"的記事本程序運行


#include<windows.h>//API函數的頭文件
int main()
{
HWND wnd;//定義一個窗口句柄變量,用以存儲找到的窗口句柄
wnd=FindWindow(NULL,"無標題.txt - 記事本");//獲得窗口名為"無標題.txt - 記事本"的窗口句柄


SendMessage(wnd,WM_CLOSE,0,0);//調用SendMessage函數,發送一個WM_CLOSE(關閉)消息給wnd窗口句柄。


return 0;
}


如果要根據窗口類名來獲得窗口句柄話,隻要給函數的第一個參數填類名,第二個參數窗口名填NULL,即可,用Spy++可查看窗口類名。


第二個:SendMessage根據窗口句柄發送一個消息給窗口


函數定義:LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam);


第一個參數是窗口句柄,第二參數個是消息類型,下麵的消息表列舉了所有消息,第三,四個參數是消息附帶信息,解釋依賴於消息類型,比如一個字符消息(WM_CHAR),那麼第三個參數就儲存有一個字符的ASCII碼。


消息機製大家都應該知道吧,Windows是基於消息的係統,鼠標移動鍵盤按鍵都會產生消息。


接下來舉一個例子,發送一個WM_CHAR消息給窗口,也就是模仿鍵盤按鍵,接收消息的窗口依舊以"無標題.txt - 記事本"為例:


SendMessage例子:模仿鍵盤按鍵


#include<windows.h>


int main()


{


HWND wnd;
wnd=FindWindow(NULL,"無標題.txt - 記事本");


while(1)


{


SendMessage(wnd,WM_CHAR,WPARAM('a'),0);


Sleep(300);


}


return 0;


}


嗬嗬上麵的例子是不是沒用,這是為什麼呢,哪裏出錯了嗎?錯倒是沒有錯,隻是窗口句柄有問題,消息發送給了主窗口。接收消息的窗口不對。記事本窗口界麵有些有什麼東西呢?菜單,編輯框,狀態欄等控件,控件也是窗口,既然是窗口,那當然它們也有窗口句柄,而在記事本裏是在哪裏打字的?編輯框控件裏打字的嘛!所以消息應該發送編輯框控件,那如何獲得記事本裏編輯框控件的窗口句柄呢?用FindWindow嗎?不知道編輯框窗口標題名,類名也不知道,當然也有其它方法獲取編輯框窗口標題名和窗口類名,如Spy++。關於如何獲得編輯框句柄,將在以後的函數中會有介紹,這裏我們就用WindowFromPoint這個函數來獲取,這個函數獲取窗口句柄的方法比較笨,(相對於我這個例子來說),這個函數是根據什麼來獲取窗口句柄的呢?根據屏幕坐標點,如屏幕坐標點20,20,當前是哪個窗口占有,就返回哪個窗口的句柄。有了這個函數,我們還需要一個函數GetCursorPos獲取鼠標當前位置(針對於屏幕);


可行的例子:模仿鍵盤按鍵:


#include<windows.h>


int main()
{
POINT curpos;//一個可儲存坐標點的結構體變量,x橫坐標,y,縱坐標,如curpos.x curpos.y


while(1)
{
GetCursorPos(&curpos);//獲取當前鼠標的位置,位置將儲存在curpos裏。
HWND hWnd = WindowFromPoint(curpos);//根據curpos所指的坐標點獲取窗口句柄
SendMessage(hWnd,WM_CHAR,WPARAM('g'),0);//發送一個字符(按鍵)消息g給當前鼠標所指向的窗口句柄
Sleep(300);//睡眠三百毫秒,相當於等待三分之一秒
}


}


這個程序一運行後,隻要把鼠標指向要輸入字符的窗口句柄,那麼就相當於鍵盤每三分之一秒按了一個g鍵,試試吧!


如果這樣覺得模仿鍵盤按鍵太麻煩的話,那麼就用keybd_event這個函數,這個專門用於模仿鍵盤按鍵的,關於怎麼用,自己百度一搜,就知道了。既然SendMessage能模仿鍵盤按鍵的話,那也能模仿鼠標左擊,右擊。而此時SendMessage函數第三,四個參數的解釋就是儲存有鼠標左擊,右擊時的位置。如模仿鼠標右擊,想一想,一次鼠標右擊有哪幾步,分別是鼠標右鍵按下,鼠標右鍵鬆開,如果你按下鼠標右鍵不鬆開,那它是不是鼠標右擊,不是的,直到你鬆開鼠標右鍵,才能算是一次完整的鼠標右擊.鼠標右鍵按下的消息類型是“WM_RBUTTONDOWN”,右鍵鬆開的消息是“WM_RBUTTONUP”,那麼一次完整的鼠標右擊應該是:
SendMessage(wnd,WM_RBUTTONDOWN,0,0);//鼠標右鍵按下,第三,四個參數說明了鼠標按下時的位置
Sleep(100);//間隔100毫秒
SendMessage(wnd,WM_RBUTTONUP,0,0);//鼠標右鍵鬆開


同樣,也有一個專門模仿鼠標動作的函數,mouse_event這個函數,可以模仿鼠標的移動,單擊,雙擊等。以後會有專門介紹。


第三個:GetCursorPos獲取鼠標當前位置(屏幕)


這個函數在SendMessage函數有介紹,這裏僅舉一個例子,在界麵裏不停的輸出鼠標當前位置。


#include<windows.h>


#include<stdio.h>


int main()


{
POINT curpos;


while(1)


{


GetCursorPos(&curpos);


printf("x:%d,y:%d",curpos.x,curpos.y);


Sleep(300);


printf("\n");


}


}


第四個:WindowFromPoint根據坐標點獲得對應的窗口句柄


在SendMessage有解釋,這裏僅舉一個例子,鼠標指向哪個窗口,就關閉哪個窗口。


#include<windows.h>


int main()


{


Sleep(2500);//等待一會兒,用於把鼠標移到其它窗口上去,避免指向本身進程的窗口,關掉自己的窗口。


POINT curpos;


while(1)


{


GetCursorPos(&curpos);


HWND wnd=WindowFromPoint(curpos);


SendMessage(wnd,WM_CLOSE,0,0);


Sleep(300);


}


}


第五個MoveWindow根據窗口句柄移動窗口,改變窗口大小
函數定義:BOOL MoveWindow( HWND hWnd, int X, int Y, intnWidth, int nHeight, BOOL bRepaint );
hWnd是要改變大小的窗口的句柄,x,y相對於屏幕的坐標,窗口左上角的位置與之相對應,nWidth和nHeight是窗口新的寬高,bRepaint指定窗口是否重畫。
這裏依舊以"無標題.txt - 記事本"為例子,改變這個窗口大小,並把窗口移到左上角去。
#include<windows.h>
int main()
{
HWND wnd;
wnd=FindWindow(NULL,"無標題.txt - 記事本");
MoveWindow(wnd,0,0,220,120,NULL);
return 0;
}
第六個ShowWindow設置窗口顯示狀態,如隱藏,最大化,最小化


函數定義BOOL ShowWinow(HWND hWnd,int nCmdShow);
SW_HIDE:隱藏窗口並激活其他窗口。第一個參數hWnd指明了窗口句柄,第二個參數指明了窗口的狀態,現在給出第二個參數常用取值範圍:
  SW_MAXIMIZE:最大化指定的窗口。
  SW_MINIMIZE:最小化指定的窗口並且激活在Z序中的下一個頂層窗口。
  SW_RESTORE:激活並顯示窗口。如果窗口最小化或最大化,則係統將窗口恢複到原來的尺寸和位置。在恢複最小化窗口時,應用程序應該指定這個標誌。
SW_SHOW:在窗口原來的位置以原來的尺寸激活和顯示窗口。
ShowWindow例子:程序運行後,在桌麵上隱藏一個指定的窗口,並在4秒後再將其顯示
#include<windows.h>
int main()
{
HWND wnd;
wnd=FindWindow(NULL,"無標題.txt - 記事本");
ShowWindow(wnd,SW_HIDE);
Sleep(5000);
ShowWindow(wnd,SW_SHOW);
return 0;
}


第七個SetCursorPos設置鼠標的位置、把鼠標移動到指定的位置


函數定義:BOOL SetCursorPos(int x,int y);


這個函數的兩個參數我想大家應該知道是什麼意思吧,屏幕的坐標點。


直接看例子:


#include<windows.h>
int main()
{
int sec=0;
while(sec<200)
{
SetCursorPos(rand()%1024,rand()%768);//隨機設置鼠標的位置
Sleep(20);
sec++;
}
return 0;
}


第八個CopyFile複製一個文件


如何複製一個文件,比如,我要把E盤的abb.txt的文本文件複製到d盤的zhengyong.txt,則調用語句


CopyFile("e:\\abb.txt","d:\\zhengyong.txt",FALSE);即可。
第三個參數有以下說明:
如果設為TRUE(非零),那麼一旦目標文件已經存在,則函數調用會失敗。否則目標文件會被覆蓋掉。


第九個DeleteFile刪除一個文件


如何刪除一個文件,語句:DeleteFile("e\\abb.txt");既是刪除
如果目標為隱藏或隻讀,則無用。


第十個CreateDirectory創建一個文件夾(目錄)


假如E盤下什麼文件也沒有
CreateDirectory("e:\\aaa\\bbb",NULL);這樣是錯的,不能同時建兩個文件,除非E盤下已經有了個aaa文件夾了。
這樣是對的CreateDirectory("e:\\aaa",NULL);


 


https://hi.baidu.com/3582077/item/f032b82bbbfb71c0ee10f1e3


第十一個:GetClientRect獲得窗口大小(客戶區)


看例子:


#include<windows.h>
#include<stdio.h>


int main(int argc, char* argv[])
{
HWND wnd;
while(1)
{
wnd=FindWindow(NULL,"無標題.txt - 記事本");
RECT rect;//專門用來存儲窗口大小
GetClientRect(wnd,&rect);//獲取窗口大小
printf("%d,%d,%d,%d\n",rect.left,rect.top,rect.right,rect.bottom);//輸出窗口大小,試著用鼠標改變窗口大小
Sleep(300);
}
}


第十二個:GetWindowRect獲得窗口大小(相對屏幕)


例子:


#include<windows.h>
#include<stdio.h>


int main(int argc, char* argv[])
{
HWND wnd;
while(1)
{
wnd=FindWindow(NULL,"無標題.txt - 記事本");
RECT rect;//專門用來存儲窗口大小
GetWindowRect(wnd,&rect);//獲取窗口大小
printf("%d,%d,%d,%d\n",rect.left,rect.top,rect.right,rect.bottom);//輸出窗口大小,試著用鼠標改變窗口大小
Sleep(300);
}
}


第十三個FindFirstFile尋找文件以及獲得文件的信息


這裏舉一個例子吧,列舉E盤第一目錄下的所有文件,包括文件夾,結合FindNextFile


#include<windows.h>
#include<stdio.h>
int main()
{
BOOL done=TRUE;
WIN32_FIND_DATA fd;
HANDLE hFind = FindFirstFile("e:\\*.*", &fd);//第一個參數是路徑名,可以使用通配符,懂DOS的人應該知道吧!fd存儲有文件的信息


while (done)
{
printf("%s\n",fd.cFileName);
done=FindNextFile(hFind, &fd); //返回的值如果為0則沒有文件要尋了
}
return 0;
}


當然也可以直接找一個文件,不使用通配符,但這樣有什麼意義呢?,如FindFirstFile("e:\\aaa.txt",&fd);其實這個可以獲取一個文件的信息,如文件是不是隱藏的,或者有沒有隻讀屬性等。


當然通過控製通配符,也可以尋找特定類型的文件,比如我隻要找文本文件,那麼就是這個語句FindFirstFile("e:\\*.txt",&fd);就行了,關鍵看你自己靈活運用。


前麵說過fd裏存儲有文件的信息,那怎麼根據fd裏麵的成員判斷這個文件的屬性,文件是否隱藏,是不是文件夾。


fd裏的dwFileAttributes存儲有文件的信息,如判斷是否為文件夾,隻要把這個變量和FILE_ATTRIBUTE_DIRECTORY進行按位與運算,如果不為0的話,表明為文夾件,如if(fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY) printf("%s是文件夾\n",fd.cFileName);


其它判斷也是一樣,現在給出文件的屬性(常用幾個):FILE_ATTRIBUTE_HIDDEN(隱藏)


FILE_ATTRIBUTE_READONLY(隻讀)FILE_ATTRIBUTE_SYSTEM(係統)


第十四個FindNextFile尋找文件


參照FindFirstFile函數的例子!


第十五個MoveFile移動文件


如把一個盤裏的文本移到另一個盤裏去:MoveFile("e:\\a.txt","d:\\abc.txt");即可,意思把e盤下的a.txt移到d盤下去,並改名為abc.txt


第十六個GetClassName根據窗口句柄獲得窗口類名


函數定義:int GetClassName(HWND hWnd, LPTSTR IpClassName, intnMaxCount);


這種函數不需要再解釋了吧,前麵有太多類似的例子。


第十七個SetFileAttributes設置文件屬性


函數定義:BOOL SetFileAttributes( LPCTSTRlpFileName,DWORDdwFileAttributes);


這個函數的第二個參數dwFileAttributes和前麵講過的WIN32_FIND_DATA結構裏的dwFileAttributes成員相對應。假設E盤第一目錄下有一個文本文件a.txt的正常文件,我要把它設為隻讀和隱藏那要如何做呢?在前麵介紹過WIN32_FIND_DATA結構裏dwFileAttributes成員的幾個常用屬性,根據這個我們知道隱藏是FILE_ATTRIBUTE_HIDDEN,隻讀是FILE_ATTRIBUTE_READONLY。


那麼把E盤下文本文件的屬性設為隱藏和隻讀的語句就是:


SetFileAttributes("e:\\a.txt",FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_READONLY);


(說明:這個函數同樣也能設置文件夾屬性)


雖然這個語句可以達到要求,但不建議用,因為會覆蓋掉文件的原來屬性,也就是說如果這個文件之前有係統屬性(係統文件)的話,那麼這個語句一旦執行後,文件就隻有隱藏和隻讀屬性了。


比如一個文件原先就有隱藏屬性,依舊以a.txt為例子,那麼我把它設為隻讀,是不是這個語句就可以呢?


SetFileAttributes("e:\\a.txt",FILE_ATTRIBUTE_READONLY);這樣的話,雖然文件有隻讀屬性了,但隱藏屬性卻沒有了。


那要如何在不覆蓋掉原來的屬性下,把文件設為隻讀呢,其實說了這麼多的廢話,總結起來就一句話:如何增加一個文件的屬性!


前提是要獲得這個文件的原有屬性:獲得文件的屬性,在FindFirstFile函數講過。好吧!直接看例子:


假設e盤的a.txt文件屬性為隱藏,給它增加隻讀屬性:
#include<windows.h>
int main()


{
WIN32_FIND_DATA fd;
FindFirstFile("e:\\a.txt",&fd);
fd.dwFileAttributes|=FILE_ATTRIBUTE_READONLY;//在原來的屬性下增加隻讀屬性
SetFileAttributes("e:\\a.txt",fd.dwFileAttributes);//設置文件的屬性
return 0;
}


第二個例子:如何去掉一個文件的屬性


(補習一下,懂的人直接跳過)


我想懂這裏的按位或、按位與或者按位異或運算的人應該知道該如何去掉一個文件的屬性。其實一個文件信息都是以二進製碼說明的。


比如一個八位二進製碼:10000010,這裏的每一位是不是隻有0和1取值,不是0,就是1,正好可以表示一個文件屬性的有無,如這個文件是隱藏的嗎?隻有是和不是,這樣我們規定把這八位二進製碼的第一位用於確定文件是否具有隱藏屬性,如果為1那便是隱藏,無則沒有,以此類推第二位就代表文件的隻讀,第三位係統。。。但要如何判斷呢,或者把某一位的值改變呢,用按位運算就可以,00000010,我要把第2位的值設為0,其它位上的值保持不變,用按位異或運算即可,與00000010進行按位異或運算,但這裏並不是與它本身進行運算,不管任何八位二進製數的值是多少隻要與00000010進行按位異或運算,那第二位都會變成0,而其它的位保持不變。這樣為了方便,我們就把00000010進行宏定義,方便記憶,這個二進製數的十進製為2。宏定義#define FILE_ATTRIBUTE_READONLY 2


明白了這個我們就來清除一個文件的一種屬性吧!


清除一個文件的隱藏屬性,假設a.txt為隱藏文件:


#include<windows.h>
int main()


{
WIN32_FIND_DATA fd;
FindFirstFile("e:\\a.txt",&fd);//獲取文件信息
fd.dwFileAttributes^=FILE_ATTRIBUTE_HIDDEN;//在原來的屬性下刪除隱藏屬性
SetFileAttributes("e:\\a.txt",fd.dwFileAttributes);//設置文件的屬性
return 0;
}


如果單單隻針對文件的屬性進行操作的話,可以用GetFileAttributes函數獲取文件的屬性,該函數隻一個參數,那就是文件的路徑,函數返回一個DWORD值,包含文件屬性信息。


第十八個ShellExecute運行一個程序
函數定義:ShellExecute(HWND hwnd, LPCSTR lpOperation, LPCSTRlpFile, LPCSTR lpParameters, LPCSTR lpDirectory, INT nShowCmd);
第一個參數hwnd是父窗口的句柄,可以為NULL,第二個參數lpOperation表示行為,第三個參數lpFile是程序的路徑名,第四個參數lpParameters是給所打開程序的參數,可以為NULL,第五個參數lpDirectory可以為NULL,第六個參數nShowCmd跟ShowWindow函數的第二個參數一樣,作用也一樣,如果打開的程序有窗口的話,這個參數就指明了窗口如何顯示.
例如打開一個記事本:
ShellExecute(NULL,"open","NOTEPAD.EXE",NULL,NULL,SW_SHOWNORMAL);
而且這個函數還可以指定程序打開一個屬於程序本身類型的文件,假如e盤有一個a.txt文件;我調用函數運行記事本程序並打開這個文本文件.
ShellExecute(NULL,"open","NOTEPAD.EXE","e:\\a.txt",NULL,SW_SHOWNORMAL);
這裏由於記事本程序屬於係統本身自帶的程序,所以沒有絕對路徑.
這個函數還可以打開一個網站:
ShellExecute(NULL,"open","https://www.baidu.com",NULL,NULL,SW_SHOWNORMAL);
ShellExecute(NULL,"open","C:",NULL,NULL,SW_SHOWNORMAL);


還可以根據文件後綴名選擇相應的程序打開一個文件:
ShellExecute(NULL,"open","e:\\a.bmp",NULL,NULL,SW_SHOWNORMAL);
類似的函數還有WinExec,隻有兩個參數,它的最後一個參數跟ShellExecute函數的最後一個參數一樣.
而第一個參數則是程序路徑名.舉個例子:WinExec("NOTEPAD.EXE",SW_SHOWNORMAL);
這個函數也可以給程序傳遞一個文件名供要運行的程序打開,那要如何加進去呢,這裏又沒有第三個參數,
方法把路徑名加在NOTPEPAD.EXE的後麵,要以空格來分開如:
WinExec("NOTEPAD.EXE e:\\a.txt",SW_SHOWNORMAL);


第十九個PlaySound播放一個WAV文件


函數定義:BOOL PlaySound(LPCSTR pszSound, HMODULE hmod,DWORDfdwSound);


第一個參數是WAV文件的路徑名,第二個參數如果不是播放MFC裏以資源ID命名的文件,則可以為空,第三個參數,指明了以何種方式播放文件。注意這個函數隻能播放100K以下的WAV文件。


假如E盤有個a.wav文件,下麵這個例子播放這個文件:


#include<windows.h>
#include<mmsystem.h>//PlaySound函數的頭文件
#pragma comment(lib, "winmm.lib")//鏈接庫,PlaySound函數必須使用
int main()


{
PlaySound("e:\\19.wav",NULL,SND_SYNC);
return 0;
}


第二十個GetModuleFileName根據模塊導入表獲取程序的完整路徑


函數定義:DWORD GetModuleFileName( HMODULE hModule, LPTSTRlpFilename, DWORD nSize );


關於第一個參數,將在以後的動態鏈接庫裏會有介紹,這裏我們隻要獲得程序本身的路徑,那麼第一個參數可以為空。


第二個參數用以存儲路徑,nSize指明字符數組大小。


這個舉個例子,運行後,把自身程序移動到e盤下,並改名為a.exe;


#include<windows.h>


int main()


{


char szAppName[128]={0};
GetModuleFileName(NULL,szAppName,128);


MoveFile(szAppName,"e:\\a.exe");


return 0;


}


 


https://hi.baidu.com/3582077/item/fc3e11164b01c00dd0d66de5


第二十一個CreateWindow創建一個窗口


//補習懂的人直接跳過


之前API函數的例子,都是針對DOS編程的,嚴格來說是在windows下的仿DOS(cmd)進行編程,編寫控製台應用程序大家都知道,主函數是main,那針對windows編程的主函數也是main嗎?不是的,windows下的主函數(入口函數)是WinMain。在定義main主函數的時候,可以給它帶兩個參數,也可以不帶。而WinMain函數就不能這樣了,它有固定的格式,它必須帶四個參數。


現給出WinMain函數的固定格式:


int WINAPI WinMain( HINSTANCE hInstance, HINSTANCEhPrevInstance, LPSTR lpCmdLine, int nCmdShow)


大家如果有興趣可以通過其它渠道了解一下各參數的意思,現在我們隻需要知道WinMain函數就是這樣定義的,不理解也沒關係。


知道了這個我們就來編一個WINDOWS程序吧!


因為我們是針對windows編程,所以要建一個Win32Application工程,步驟是點擊文件,然後選擇新建,在彈出的對話框裏選擇工程,再選中Win32Application 接著在右邊的工程名稱下填工程名稱,名字隨便取。之後點確定。接著又彈出了一個對話框,這裏為了方便,我們選擇“一個簡單的 Win32 程序”,點完成。接著雙擊WinMain彈出代碼編輯窗口,做完這個我們就可以打代碼了。


簡單的例子如下:


#include "stdafx.h"


int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{


while(1)


Sleep(100);
return 0;
}


怎麼樣夠簡單吧,是不是覺得奇怪,怎麼沒有窗口,因為窗口要自己創建,不像控製台程序,隻要一運行便會有窗口。雖然沒有窗口,但你創建了一個進程,打開任務管理器,可以找到你所創建的那個進程,其實也沒什麼奇怪的,像WINDOWS本身的一些係統服務,也是隻有進程,沒有窗口的像spoolsv.exe,svchost.exe。


那要如何創建一個窗口呢?要創建一個窗口,就必須要向係統提供窗口的信息,如你要創建的窗口名字叫什麼,窗口圖標是什麼,窗口大小,窗口背景色等,不然,係統怎麼給你創建窗口呢?所以為了方便,VC就定義了一個結構,專門用於存儲窗口信息。


現給出這個結構的定義。


typedef struct _WNDCLASS { 
UINT style; //描述類風格
WNDPROC lpfnWndProc; //窗口處理函數
int cbClsExtra; //表示窗口類結構之後分配的額外的字節數。係統將該值初始化為0
int cbWndExtra; //表示窗口實例之後分配的額外的字節數。係統將該值初始化為0
HINSTANCE hInstance;// 應用程序實例句柄由WinMain函數傳進來 
HICON hIcon; //窗口圖標句柄 
HCURSOR hCursor; //窗口光標句柄
HBRUSH hbrBackground; //畫刷句柄
LPCTSTR lpszMenuName; //窗口菜單名
LPCTSTR lpszClassName; //窗口類名
} WNDCLASS, *PWNDCLASS;


好了,如果我們已經把窗口信息填好了,那我們要怎樣把這個信息告訴係統呢,也就是把要創建窗口的信息傳給係統。這裏我們調用RegisterClass函數就能實現這個功能。注冊完窗口,我們就要創建窗口,用CreateWindow函數就能實現,不要問為什麼注冊窗口後直接顯示不就行了,還要搞什麼創建窗口。這我也不知道,反正你隻要記住這格式就行了,硬式規定的,你想創建一個窗口,就必須按這些步驟來。


好了,窗口創建了,我們就要調用ShowWindow函數顯示窗口,然後用UpdateWindow函數刷新一下,確保窗口能立即顯示。


以下詳細實現代碼:


#include "stdafx.h"
#include<windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{


WNDCLASS wndcls; //定義一個存儲窗口信息WNDCLASS變量
wndcls.cbClsExtra=0; //默認為0
wndcls.cbWndExtra=0; //默認為0
wndcls.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //背景畫刷
wndcls.hCursor=LoadCursor(NULL,IDC_CROSS); //十字光標
wndcls.hIcon=LoadIcon(NULL,IDI_ERROR); //窗口圖標
wndcls.hInstance=hInstance; //應用程序實例句柄由WinMain函數傳進來 
wndcls.lpfnWndProc=NULL; //窗口消息處理函數
wndcls.lpszClassName="windowclass"; //窗口類名
wndcls.lpszMenuName=NULL; //窗口菜單名,沒有菜單,為NULL
wndcls.style=CS_HREDRAW | CS_VREDRAW;//窗口類型,CS_HREDRAW和CS_VERDRAW 表明
//當窗口水平方向垂直方向的寬度變化時重繪整個窗口
RegisterClass(&wndcls); //把窗口信息提交給係統,注冊窗口類
HWND hwnd; //用以存儲CreateWindow函數所創建的窗口句柄
hwnd=CreateWindow("windowclass","first windows", 
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);//創建窗口
ShowWindow(hwnd,SW_SHOWNORMAL);//窗口創建完了,顯示它
UpdateWindow(hwnd); //更新窗口,讓窗口毫無延遲的顯示
return 0;
}


是不是出錯了,內存不能讀取,為什麼了呢,因為你創建的窗口沒有消息處理函數,windows係統當然不允許這樣一個窗口存在,對按鍵,鼠標都沒有反應,這樣的窗口是沒有實際意義的。 wndcls.lpfnWndProc=NULL; //窗口消息處理函數,就是前麵這句,必須要填
窗口過程(消息)處理函數,那這個函數是怎樣定義的呢,像WinMain一樣,它也有固定的格式。


窗口過程處理函數的格式:LRESULT CALLBACK WinSunProc(HWND wnd,UINTuMsg,WPARAM wParam,LPARAM lParam)


下麵的這個是一個窗口創建的完整例子:


#include "stdafx.h"
#include<windows.h>
LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
if(uMsg==WM_LBUTTONDOWN)MessageBox(NULL,"kdjfkdf","Kjdfkdfj",MB_OK);//處理鼠標按下消息,彈出消息框
return DefWindowProc(hwnd,uMsg,wParam,lParam); //未處理的消息通過DefWindowProc函數交給係統處理
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{


WNDCLASS wndcls; //定義一個存儲窗口信息WNDCLASS變量
wndcls.cbClsExtra=0; //默認為0
wndcls.cbWndExtra=0; //默認為0
wndcls.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //背景畫刷
wndcls.hCursor=LoadCursor(NULL,IDC_ARROW); //光標
wndcls.hIcon=LoadIcon(NULL,IDI_ERROR); //窗口圖標
wndcls.hInstance=hInstance; //應用程序實例句柄由WinMain函數傳進來 
wndcls.lpfnWndProc=WinSunProc; //窗口消息處理函數
wndcls.lpszClassName="windowclass"; //窗口類名
wndcls.lpszMenuName=NULL; //窗口菜單名,沒有菜單,為NULL
wndcls.style=CS_HREDRAW | CS_VREDRAW;//窗口類型,CS_HREDRAW和CS_VERDRAW 表明
//當窗口水平方向垂直方向的寬度變化時重繪整個窗口
RegisterClass(&wndcls); //把窗口信息提交給係統,注冊窗口類
HWND hwnd; //用以存儲CreateWindow函數所創建的窗口句柄
hwnd=CreateWindow("windowclass","first windows", 
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);//創建窗口
ShowWindow(hwnd,SW_SHOWNORMAL);//窗口創建完了,顯示它
UpdateWindow(hwnd); //更新窗口,讓窗口毫無延遲的顯示
MSG msg;//消息結構類型
while(GetMessage(&msg,NULL,0,0))//獲取消息
{
//TranslateMessage(&msg); //此函數用於把鍵盤消息(WM_KEYDOWN,WM_KEYUP)轉換成字符消息WM_CHAR
DispatchMessage(&msg); //這個函數調用窗口過程處理函數,並把MSG裏的信息處理後傳給過程函數的四個參數
}
return 0;
}


WinSunProc函數的四個參數,分別對應著SendMessage函數四個參數,詳情參見SendMessage函數參數解釋。


MSG類型解釋 :


結構定義:


typedef struct tagMSG 
{
HWND hwnd;//hwnd表示消息將要發送給的窗口句柄
UINT message;//消息類型,如WM_WMCLOSE,WM_CHAR,WM_LBUTTONDOWN,參見消息表
WPARAM wParam;//消息附帶信息,取值的意思具體依據消息類型而定
LPARAM lParam;//消息附帶信息,取值的意思具體依據消息類型而定
DWORD time;//消息的發送時間,不常用
POINT pt;//消息發送時,鼠標所在的位置,不常用
}MSG;


大家試著把上麵的例子運行一遍,然後關掉窗口,再運行一遍,是不是出錯了,因為前一個程序雖然窗口關閉了,但進程還在運行,還記得那個循環語句嗎?while(GetMessage(&msg,NULL,0,0))就是這個。隻要條件成立,進程就會一直運行下去。如何讓這個循環結束呢?用 PostQuitMessage(0); 這個語句就行了,參數0表示給自身窗口發送一個退出消息,當GetMessage函數接到PostQuitMessage函數發出的消息後,就會返回0值。


如在窗口過程函數中處理窗口關閉WM_CLOSE消息:if(uMsg==WM_CLOSE)PostQuitMessage(0); 這樣隻要一關閉窗口,它的進程也會結束。


接下來解釋一下CreateWindow函數參數的意思,函數定義


HWND CreateWindow(LPCTSTR lpClassName,//窗口類名,應與WNDCLASS結構裏的成員lpszClassName一致
LPCTSTR lpWindowName,,//窗口標題名
DWORD dwStyle,//窗口的風格,取值參見表Style


int x,
int y,//x,y表示所創建窗口左上角位置
int nWidth,
int nHeight,//nWidth,nHeight表示窗口的寬高
HWND hWndParent,//父窗口句柄,如果不是子窗口,這裏取值為NULL
HMENU hMenu,//菜單句柄,沒菜單的話,取NULL值
HANDLE hlnstance,//對應著WinMain函數的第一個參數
LPVOID lpParam);//NULL


表Style:(參考:百度)


WS_BORDER:創建一個單邊框的窗口。   
WS_CAPTION:創建一個有標題框的窗口(包括WS_BODER風格)。
WS_CHILD:創建一個子窗口。這個風格不能與WS_POPUP風格合用。
WS_CHLDWINDOW:與WS_CHILD相同。
WS_CLIPCHILDREN:當在父窗口內繪圖時,排除子窗口區域。在創建父窗口時使用這個風格。   
WS_CLlPBLINGS;排除子窗口之間的相對區域,也就是,當一個特定的窗口接收到WM_PAINT消息時,WS_CLIPSIBLINGS 風格將所有層疊窗口排除在繪圖之外,隻重繪指定的子窗口。如果未指定WS_CLIPSIBLINGS風格,並且子窗口是層疊的,則在重繪子窗口的客戶區時,就會重繪鄰近的子窗口。
WS_DISABLED:創建一個初始狀態為禁止的子窗口。一個禁止狀態的窗口不能接受來自用戶的輸入信息.
WS_DLGFRAME:創建一個帶對話框邊框風格的窗口。這種風格的窗口不能帶標題條。
WS_GROUP:指定一組控製的第一個控製。這個控製組由第一個控製和隨後定義的控製組成,自第二個控製開始每個控製,具有WS_GROUP風格,每個組的第一個控製帶有WS_TABSTOP風格,從而使用戶可以在組間移動。用戶隨後可以使用光標在組內的控製間改變鍵盤焦點。  
WS_HSCROLL:創建一個有水平滾動條的窗口。   
WS_ICONIC:創建一個初始狀態為最小化狀態的窗口。
與WS_MINIMIZE風格相同。   
WS_MAXIMIZE:創建一個初始狀態為最大化狀態的窗口。   
WS_MAXIMIZEBOX:創建一個具有最大化按鈕的窗口。該風格不能與WS_EX_CONTEXTHELP風格同時出現,同時必須指定WS_SYSMENU風格。   
WS_OVERLAPPED:產生一個層疊的窗口。一個層疊的窗口有一個標題條和一個邊框。與WS_TILED風格相同。  WS_OVERLAPPEDWINDOW:創建一個具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU WS_THICKFRAME,WS_MINIMIZEBOX,WS_MAXIMIZEBOX風格的層疊窗口,與WS_TILEDWINDOW風格相同。   WS_POPUP;創建一個彈出式窗口。該風格不能與WS_CHLD風格同時使用。   
WS_POPUWINDOW:創建一個具有WS_BORDER,WS_POPUP,WS_SYSMENU風格的窗口,WS_CAPTION和WS_POPUPWINDOW必須同時設定才能使窗口某單可見。  
WS_SIZEBOX:創建一個可調邊框的窗口,與WS_THICKFRAME風格相同。   
WS_SYSMENU:創建一個在標題條上帶有窗口菜單的窗口,必須同時設定WS_CAPTION風格。  
WS_TABSTOP:創建一個控製,這個控製在用戶按下Tab鍵時可以獲得鍵盤焦點。按下Tab鍵後使鍵盤焦點轉移到下一具有WS_TABSTOP風格的控製。   
WS_THICKFRAME:創建一個具有可調邊框的窗口,與WS_SIZEBOX風格相同。   
WS_TILED:產生一個層疊的窗口。一個層疊的窗口有一個標題和一個邊框。
與WS_OVERLAPPED風格相同。   
WS_TILEDWINDOW:創建一個具有WS_OVERLAPPED,WS_CAPTION,WS_SYSMENU, WS_THICKFRAME,WS_MINIMIZEBOX,WS_MAXMIZEBOX風格的層疊窗口。與WS_OVERLAPPEDWINDOW風格相同。  
WS_VISIBLE創建一個初始狀態為可見的窗口。   
WS_VSCROLL:創建一個有垂直滾動條的窗口。


第二十二個GetMessage獲取窗口消息


參照CreateWindow函數例子,以後的例子可能是在控製台下,也可能是Win32 Application,大家以後根據主函數判斷該建什麼工程。


第二十三個RegisterClass注冊窗口類,參照CreateWindow


第二十四個UpdateWindow參照CreateWindow


第二十五個DispatchMessage參照CreateWindow


第二十六個LoadCursorFromFile從磁盤加載一個光標文件,函數返回該光標句柄


假設e盤下有一個名為a.cur的光標文件。


HCURSOR cursor//定義一個光標句柄,用於存放LoadCursorFromFile函數返回的光標句柄


cursor=LoadCursorFromFile("e:\\a.cur");


獲得了光標句柄有什麼用呢?看一下窗口類WNDCLASS裏的hCursor成員,這個成員也是一個光標句柄,明白了吧!


第二十七個CreateSolidBrush創建一個畫刷,函數返回畫刷句柄


HBRUSH hbr=CreateSolidBrush(RGB(12,172,59));//三個數字分別表明RGB的顏色值,RGB根據三種顏色值返回一個COLORREF類型的值


第二十八個LoadImage裝載位圖、圖標、光標函數


函數定義:HANDLE LoadImage(HINSTANCE hinst,LPCTSTR lpszName,UINTuType,int cxDesired,int CyDesired,UINT fuLoad)


這裏我們隻要這個函數的幾個簡單功能:從磁盤加載位圖,從磁盤加載圖標,從磁盤加載光標。所以第一個參數hinst我們不用管它,直接填NULL就行,第二個參數lpszName是圖片文件所在路徑名,第三個參數uType指明要加載的是什麼類型的圖片,


是位圖(填IMAGE_BITMAP),還是光標(填IMAGE_CURSOR),還是圖標(填IMAGE_ICON)。第四個cxDesired和第五個參數CyDesired,指定要加載的圖片的寬高(可以放大光標,或者縮小),如果加載的是位圖的話,則兩個參數必須為0,第六個參數fuLoad表示以何種方式加載文件,這裏我們是從磁盤加載文件,所以填LR_LOADFROMFILE;


好了,假設e盤下有一個c.cur和i.ico文件。例子:設置窗口圖標和光標,還有背景色


#include "stdafx.h"//這個頭文件是編譯器自動生成的,不是空工程,都會有,
//如果是直接建C++源文件,包含這個頭文件,會出錯


#include <windows.h> 
#include <stdio.h> 
LRESULT CALLBACK WinSunProc( 
HWND hwnd, // handle to window 
UINT uMsg, // message identifier 
WPARAM wParam, // first message parameter 
LPARAM lParam // second message parameter 
); //窗口過程函數聲明
int WINAPI WinMain( 
HINSTANCE hInstance, // handle to current instance 
HINSTANCE hPrevInstance, // handle to previous instance 
LPSTR lpCmdLine, // command line 
int nCmdShow // show state 


//設計一個窗口類 
WNDCLASS wndcls; 
wndcls.cbClsExtra=0; 
wndcls.cbWndExtra=0; 
wndcls.hbrBackground=CreateSolidBrush(RGB(12,172,59));//畫刷
wndcls.hCursor=(HCURSOR)LoadImage(NULL,"e:\\c.cur",IMAGE_CURSOR,24,24,LR_LOADFROMFILE);//加載光標
wndcls.hIcon=(HICON)LoadImage(NULL,"e:\\i.ico",IMAGE_ICON,48,48,LR_LOADFROMFILE);//加載圖標
wndcls.hInstance=hInstance; //應用程序實例句柄由WinMain函數傳進來 
wndcls.lpfnWndProc=WinSunProc; //定義窗口處理函數
wndcls.lpszClassName="windowclass"; 
wndcls.lpszMenuName=NULL; 
wndcls.style=CS_HREDRAW | CS_VREDRAW; 
RegisterClass(&wndcls); 


//創建窗口,定義一個變量用來保存成功創建窗口後返回的句柄 
HWND hwnd; 
hwnd=CreateWindow("windowclass","first window", 
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);
//顯示及刷新窗口 
ShowWindow(hwnd,SW_SHOWNORMAL); 
UpdateWindow(hwnd);
//定義消息結構體,開始消息循環 
MSG msg; 
while(GetMessage(&msg,NULL,0,0)) 

TranslateMessage(&msg); 
DispatchMessage(&msg); 

return msg.wParam; 



//編寫窗口過程函數 
LRESULT CALLBACK WinSunProc( 
HWND hwnd, // handle to window 
UINT uMsg, // message identifier 
WPARAM wParam, // first message parameter 
LPARAM lParam // second message parameter 


switch(uMsg) 

case WM_CHAR: //字符消息
char szChar[20]; 
sprintf(szChar,"char code is %c",wParam); 
MessageBox(hwnd,szChar,"char",0); 
break; 
case WM_LBUTTONDOWN: //鼠標左鍵按下消息
MessageBox(hwnd,"mouse clicked","message",0); 
break; 
case WM_CLOSE: 
if(IDYES==MessageBox(hwnd,"是否真的結束?","message",MB_YESNO))

DestroyWindow(hwnd); //銷毀窗口,並發送WM_DESTROY消息給自身窗口

break; 
case WM_DESTROY:
PostQuitMessage(0); 
break; 
default: 
return DefWindowProc(hwnd,uMsg,wParam,lParam); 

return 0; 
}


第二十九個GetDC根據窗口句柄獲取設備上下文(DC)返回DC句柄


得到了一個窗口的設備上下文,就可以進行畫圖操作了,像畫圓,畫正方形,顯示圖片等函數都是要設備上下文(DC)句柄做參數的。


HDC dc//定義一個DC句柄


HWND wnd=FindWindow(NULL,"無標題.txt- 記事本");//獲取窗口句柄


dc=GetDC(wnd)//獲取這個窗口的設備上下文


第三十個Rectnagle在窗口中畫一個矩形


以"無標題.txt - 記事本"窗口為例,在這個窗口簡單的畫一個矩形


#include<windows.h>
void main()
{
HDC dc;
HWND wnd=FindWindow(NULL,"無標題.txt - 記事本");
dc=GetDC(wnd);//獲取窗口設備上下文(DC)
while(1)//用循環語句重複畫,是為了確保不會被窗口刷新給刷掉
{
Rectangle(dc,50,50,200,200);//畫一個矩形
Sleep(200);
}
}


 


https://hi.baidu.com/3582077/item/535b4211faa5df8f88a956e5


第三十個CreateToolhelp32Snapshot給當前進程拍一個照


HANDLEhProcessSnap=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);


//記住這種格式就行了,返回的句柄,存儲有進程信息,可以用Process32Firs函數找出來。


第三十一個Process32First根據CreateToolhelp32Snapshot函數返回的句柄獲取進程信息


結合Process32Next函數使用,有點像文件尋找函數。


看完整例子:顯示係統進程名,以及進程ID號


#include<windows.h>
#include<tlhelp32.h>//聲明快照函數的頭文件
#include<stdio.h>
int main()
{
PROCESSENTRY32 pe32;//進程的信息將會存儲在這個結構裏
//在使用這個結構之前,先設置它的大小
pe32.dwSize=sizeof(pe32);
//給係統內的所有進程拍一個快照
HANDLE hProcessSnap=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
BOOL bMore=::Process32First(hProcessSnap,&pe32);//第一次查找
while(bMore)
{
printf("進程名稱:%s\n",pe32.szExeFile);//szExeFile是進程名
printf("進程ID號:%u\n\n",pe32.th32ProcessID);//th32ProcessID是進程ID號
bMore=::Process32Next(hProcessSnap,&pe32);//尋找下個進程,函數返回0,則沒有進程可尋
}
return 0;
}


第三十二個OpenProcess根據進程ID號獲得進程句柄,句柄通過函數返回


函數定義:HANDLE OpenProcess( DWORD dwDesiredAccess,BOOL bInheritHandle, DWORD dwProcessId);


第一個參數不要管它,填PROCESS_ALL_ACCESS,第二個參數也一樣,填FALSE,那最後一個參數就是進程ID號。


第三十三個TerminateProcess結束一個進程(需進程句柄做參數)


該函數隻有兩個參數,第一個是進程句柄,第二個填0就行了。


現在給個例子:假設當前有一個進程名為abc.exe的進程正在運行,編一個程序結束它。


#include<windows.h>
#include<tlhelp32.h>//聲明快照函數的頭文件
int main(int argc,char *argv[])
{




PROCESSENTRY32 pe32;
//在使用這個結構之前,先設置它的大小
pe32.dwSize=sizeof(pe32);
//給係統內的所有進程拍一個快照
HANDLE hProcessSnap=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
//遍曆進程快照,輪流顯示每個進程的信息
BOOL bMore=::Process32First(hProcessSnap,&pe32);
while(bMore)
{


if(strcmp("abc.exe",pe32.szExeFile)==0)//如果找到進程名為abc.exe
{
HANDLE hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);//獲取句柄
::TerminateProcess(hProcess,0);//結束它
}
bMore=::Process32Next(hProcessSnap,&pe32);//尋找下一個
}
return 0;
}


上麵的這個例子,隻能結束普通權限進程,如果為係統進程的話,則沒有用,結束不了。在後麵的提升權限函數,會有例子說明如何結束係統進程。


第三十四個CreatePen創建一個畫筆(返回畫筆句柄)


函數定義:BOOL CreatePen(int nPenStyle, int nWidth, COLORREFcrColor);


第一個參數,表示是什麼類型的線,取值有以下:


PS_SOLID 畫筆畫出的是實線 PS_DASH 畫筆畫出的是虛線(nWidth必須是1) PS_DOT 畫筆畫出的是點線(nWidth必須是1)
PS_DASHDOT 畫筆畫出的是點劃線(nWidth必須是1) PS_DASHDOTDOT 畫筆畫出的是點-點-劃線(nWidth必須是1)
第二個參數是畫筆的寬度,第三個參數是畫筆的顏色,COLORREF類型可以RGB來獲得如RGB(233,128,88);分別是紅綠藍。


如創建一個畫筆:HPEN pen=CreatePen(PS_SOLID,3,RGB(255,78,99));


第三十五個CreateSolidBrush創建一個畫刷


隻有一個COLORREF類型的參數


HBRUSH brush=CreateSolidBrush(RGB(22,182,111));


第三十六個SelectObject把GDI對象選入相應的DC中


像畫筆(句柄HPEN),畫刷(HBURSH),位圖(HBITMAP)等都是GID對象。因為畫圖函數,如畫圓,畫矩形,畫直線,它們所畫出圖形,默認屬性都是不變的,如線的寬度。那麼想要改變畫出來時線的寬度,比如我想畫出來的圖形它的線條寬度為5(像素),那麼就要創建一個寬度為5的畫筆,然後再通過SelectObject函數,給這個畫筆選入,就可以了.


接下舉個例子:SelectObject應用




#include "stdafx.h"
#include<windows.h>
LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
static HPEN pen=CreatePen(PS_SOLID,3,RGB(255,78,99));//創建畫筆
static HBRUSH brush=CreateSolidBrush(RGB(22,182,111));//創建畫刷
if(uMsg==WM_PAINT)//窗口需要重畫的時候
{
HDC hDC; 
PAINTSTRUCT ps; 
hDC=BeginPaint(hwnd,&ps); //BeginPaint隻能在響應WM_PAINT,不能用GetDC獲取設備上下文
SelectObject(hDC,pen);//選入畫筆
SelectObject(hDC,brush);//選入畫刷
Rectangle(hDC,100,100,200,200);
EndPaint(hwnd,&ps); 
}
else if(uMsg==WM_CLOSE)//用戶關閉了窗口
DestroyWindow(hwnd);//銷毀窗口,並發送WM_DESTROY消息
else if(uMsg==WM_DESTROY)//如果窗口被銷毀
PostQuitMessage(0);//讓進程退出
return DefWindowProc(hwnd,uMsg,wParam,lParam); //未處理的消息通過DefWindowProc函數交給係統處理
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{


WNDCLASS wndcls; //定義一個存儲窗口信息WNDCLASS變量
wndcls.cbClsExtra=0; //默認為0
wndcls.cbWndExtra=0; //默認為0
wndcls.hbrBackground=(HBRUSH)GetStockObject(GRAY_BRUSH); //背景畫刷
wndcls.hCursor=LoadCursor(NULL,IDC_ARROW); //光標
wndcls.hIcon=LoadIcon(NULL,IDI_ERROR); //窗口圖標
wndcls.hInstance=hInstance; //應用程序實例句柄由WinMain函數傳進來 
wndcls.lpfnWndProc=WinSunProc; //窗口消息處理函數
wndcls.lpszClassName="windowclass"; //窗口類名
wndcls.lpszMenuName=NULL; //窗口菜單名,沒有菜單,為NULL
wndcls.style=CS_HREDRAW | CS_VREDRAW;//窗口類型,CS_HREDRAW和CS_VERDRAW 表明
//當窗口水平方向垂直方向的寬度變化時重繪整個窗口
RegisterClass(&wndcls); //把窗口信息提交給係統,注冊窗口類
HWND hwnd; //用以存儲CreateWindow函數所創建的窗口句柄
hwnd=CreateWindow("windowclass","first windows", 
WS_OVERLAPPEDWINDOW,0,0,600,400,NULL,NULL,hInstance,NULL);//創建窗口
ShowWindow(hwnd,SW_SHOWNORMAL);//窗口創建完了,顯示它
UpdateWindow(hwnd); //更新窗口,讓窗口毫無延遲的顯示
MSG msg;//消息結構類型
while(GetMessage(&msg,NULL,0,0))//獲取消息
{
//TranslateMessage(&msg); //此函數用於把鍵盤消息(WM_KEYDOWN,WM_KEYUP)轉換成字符消息WM_CHAR
DispatchMessage(&msg); //這個函數調用窗口過程處理函數,並把MSG裏的信息處理後傳給過程函數的四個參數
}
return 0;
}


第三十七個 ReadProcessMemory根據進程句柄讀取相應的一段內存(讀其它進程裏的內存)


函數定義:BOOL ReadProcessMemory(HANDLE hProcess,PVOIDpvAddressRemote,PVOID pvBufferLocal,DWORD dwSize,


PDWORD pdwNumBytesRead);總共四個參數


第一個參數hProcess是遠程進程句柄,被讀取者 。第二個pvAddressRemote是遠程進程中內存地址。 從具體何處讀取


pvBufferLocal是本地進程中內存地址. 函數將讀取的內容寫入此處,dwSize是要讀取的字節數。要讀取多少


pdwNumBytesRead是實際讀取的內容(函數執行後,實際讀了多少字節,將存儲在該變量裏)


遠程進程的內存地址是什麼意思呢,比如我現在定義一個變量a,int a;就是了,大家知道int型是占四個字節的,也就是說如果a變量所占的內存起始地址是0x1234,那麼變量a就占用0x1234,0x1235,0x1236,0x1237這四個字節,這四個字節的內容決定了a變量的值。


好了知道了這個,我們就來舉個例子,讀取另一個進程裏一個變量的值:需設計兩個程序,一個用於讀(Read)一個用於被讀(BeRead);


那麼要如何獲得另一個進程中一個變量的地址呢?這裏我們用一個簡單的方法,讓另一個進程自己去獲取,然後輸出地址值。


被讀的程序代碼如下:假設該進程名為:BeRead.exe
#include<stdio.h>
int main()
{
int a=10;//要讀取的變量。
printf("%x\n",&a);//輸出這個變量的起始地址,假設輸出為12ff7c
while(1)
{
Sleep(1000);
}


return 0;
}


必須先讓這個程序運行,然後根據輸出的地址值,才能在下麵的程序填入地址值。


讀取的程序代碼如下:


#include<windows.h>
#include<stdio.h>
#include<tlhelp32.h>
int main()
{


//先要獲取進程句柄,如何獲取,參照TerminateProcess函數,結束一個進程


HANDLE ReProcess;
PROCESSENTRY32 pe32;
pe32.dwSize=sizeof(pe32);
HANDLE hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
BOOL bMore=::Process32First(hProcessSnap,&pe32);
while(bMore)
{
if(strcmp(pe32.szExeFile,"BeRead.exe")==0)//如果是BeRead.exe
{
ReProcess=::OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);//獲取該進程句柄
break;
}
bMore=Process32Next(hProcessSnap,&pe32);
}
int *ReAddress=(int *)0x12ff7c;//要讀取的內存的地址值
int *p=new int;
unsigned long size;
ReadProcessMemory(ReProcess,ReAddress,p,4,&size);//讀取BeRead進程的內存
printf("%d\n",*p);//輸出讀取來的值
return 0;
}


第三十八個WriteProcessMemory根據進程句柄寫入相應的一段內存(寫入其它進程裏的內存)


這個函數裏的參數跟ReadProcessMemory函數參數意思一樣,隻不過一個是寫,一個是讀。


下麵直接舉個例子,形式跟讀內存函數的例子一樣。


被寫的程序代碼如下:假設該進程名為:BeWrite.exe


#include<stdio.h>
int main()
{
int a=10;
printf("%x\n",&a);//假設輸出為12ff7c
while(1)

printf("%d\n",a);//每隔一秒輸出,查看值有沒有改變
Sleep(1000);
}


return 0;
}


寫入的代碼如下:


#include<windows.h>
#include<stdio.h>
#include<tlhelp32.h>
int main()
{
HANDLE ReProcess;
PROCESSENTRY32 pe32;
pe32.dwSize=sizeof(pe32);
HANDLE hProcessSnap=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
BOOL bMore=::Process32First(hProcessSnap,&pe32);
while(bMore)
{
if(strcmp(pe32.szExeFile,"BeWrite.exe")==0)
{
ReProcess=::OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID);
break;
}
bMore=Process32Next(hProcessSnap,&pe32);
}
int *ReAddress=(int *)0x12ff7c;
int *p=new int;
*p=300;
unsigned long size;
WriteProcessMemory(ReProcess,ReAddress,p,4,&size);


return 0;
}


第三十九個CreateThread創建一個線程(多線程)


線程是什麼意思呢,代碼是由線程來執行的,一個程序默認隻有一個線程(主線程),打個比方,線程就好比一個人,而不同功能的代碼或函數就好是一件件不同的事情,如洗碗,洗衣服,擦地。一個人要把這幾種事情做完,可以有好幾種方案,第一種就是,洗完碗,就去洗衣服,衣服洗完了,再去擦地。第二種就是:洗一分鍾碗,再去洗一分鍾衣服,再去擦一分鍾,然後又去洗一分鍾衣服.......直到做完。好了,現在你可以再創造一個人幫你做事,創造這個人後,你就叫他洗衣服,而你就洗碗,這樣兩件事就可以同時被做了。而這裏的創造一個人指的就是CreateThread函數。


函數定義:HANDLE CreateThread(LPSECURITY_ATTRIBUTESlpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINElpStartAddress,LPVOID lpParameter,DWORD dwCreationFlags,LPDWORD lpThreadId);


該函數有六個參數,第一個參數不用管它,填NULL,第二個參數dwStackSize用於新線程的初始堆棧大小,默認為0,第三個lpStartAddress填函數名(指標),但這個函數必須是這種固定格式的DWORD _stdcall ThreadProc(LPVOID lpParameter),新的線程將會執行這個函數裏麵的代碼,直到函數結束,線程死亡。第四個lpParameter是一自定義參數,用戶可以通過這個參數,傳遞需要的類型,這個參數與線程函數的參數相對應。第五個dwCreationFlags填0表示立即執行,如果是CREATE_SUSPENDED表示掛起,直到用ResumeThread函數喚醒。第六個lpThreadId填NULL就行了。


現舉個例子,兩個線程同時每隔一秒輸出一個數字,也就是一秒會有兩數字輸出。


#include<windows.h>
#include<stdio.h>
DWORD _stdcall ThreadProc(LPVOID lpParameter)//線程執行函數
{
int si=100;
while(si>0)
{
printf("子線程輸出數字:%d\n",si--);
Sleep(1000);
}
return 0;
}


int main()
{
int mi=0;
CreateThread(NULL,0,ThreadProc,NULL,0,NULL);//創建一個線程,去執行ThreadProc函數
while(mi<100)
{
printf("主線程輸出數字:%d\n",mi++);
Sleep(1000);
}
return 0;
}


第四十個GetCurrentProcessId獲得當前進程ID


DWORD currentPID;
currentPID=::GetCurrentProcessId();//返回進程ID號
cout<<currentPID<<endl;


 


https://hi.baidu.com/3582077/item/1e77f39fbee5b1de1f4271e3


第四十一個CreateCompatibleDC創建一個兼容的內存設備上下文(DC)


根據DC創造一個兼容的內存DC,此時創造出來的內存DC僅僅是一些屬性跟源DC一樣,如DC的大小,以及對應窗口的寬高等。內存DC裏的數據沒有具體取值,需用其它函數給裏麵的數據賦值。


第四十二個GetObject獲取一個對象信息(如位圖,圖標,光標)


函數定義:int GetObject(HGDIOBJ hgdiobj, int cbBuffer, LPVOIDlpvObject);


第一個參數hgdiobj是對象句柄,第二個參數cbBuffer是待寫入lpvObject指針指向緩存區數據大小,第三個參數lpvObject是一個指針,指向一個緩存區。


這裏舉一個獲取位圖的信息,獲取位圖的大小,假設E盤下有一個aa.bmp的位圖文件,輸出位圖的寬高


#include<windows.h>
#include<stdio.h>
int main()
{
BITMAP bmInfo;//這個結構存儲位圖信息
HBITMAP bmp;
bmp=(HBITMAP)LoadImage(NULL,"e:\\aa.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
GetObject(bmp,sizeof(BITMAP),&bmInfo);
printf("位圖寬:%d,位圖高:%d\n",bmInfo.bmWidth,bmInfo.bmHeight);
return 0;
}


第四十三個BitBlt在窗口輸出一個位圖


其實倒不如說這個BitBlt函數是拷貝一個設備上下文(DC),或者合並兩個窗口,再延伸一下,合並兩個圖片?也並無不可,往大了說,窗口難道不是圖片嗎?用截屏軟件,把窗口截成圖片,這樣窗口便成了圖片。可能有點瞎說,大家還是按照標準來吧,反正,你隻要掌握這個函數就行了,而且這個概念也不會有什麼影響,那就足夠了。


BitBlt的作用跟把兩幅圖片合在一起一樣,合並兩幅圖片。可能兩幅圖片大小不一樣也可以合並,但合並DC就不行了,必須兩個信息一樣的DC才可以合並,那要如何確保兩個DC一樣呢?這就要用到CreateCompatibleDC函數了。


函數定義:BOOL BitBlt(HDC hdcDest,int nXDest,int nYDest,intnWidth,int nHeight,HDC hdcSrc,int nXSrc,int nYSrc,DWORD dwRop);


第一個參數hdcDest是原DC句柄,被覆蓋的DC,nXdest,nYDest,nWidth,nHeight這四個參數,指明了一個矩形,覆蓋原DC哪塊區域。


第六個參數hdcSrc是覆蓋的DC句柄,nXSrc,nYSrc參數指明從哪裏開始覆蓋。(覆蓋DC的左上角),第九個參數dwPop表示以何種方式覆蓋。因為這裏我們隻要輸出一個位圖,所以用SRCCOPY,直接覆蓋。


好了,直接舉個例子,在窗口輸出一副圖片,假設e盤下有一個aa.bmp的位圖。為了方便,我們直接在記事本窗口輸出位圖,先運行一個窗口名為"無標題.txt - 記事本"記事本窗口程序。


#include<windows.h>
#include<stdio.h>
int main()
{
BITMAP bmInfo;//這個結構存儲位圖信息
HBITMAP bmp;
bmp=(HBITMAP)LoadImage(NULL,"e:\\aa.bmp",IMAGE_BITMAP,0,0,LR_LOADFROMFILE);
GetObject(bmp,sizeof(BITMAP),&bmInfo);//獲取位圖信息
HWND wnd=FindWindow(NULL,"無標題.txt - 記事本");
HDC hdc=GetDC(wnd);
HDC memDC=::CreateCompatibleDC(hdc);//創造兼容的DC
SelectObject(memDC,bmp);//選入位圖
while(1)
{


BitBlt(hdc,0,0,bmInfo.bmWidth,bmInfo.bmHeight,memDC,0,0,SRCCOPY);//輸出位圖
Sleep(200);


}
return 0;


}


下麵介紹一下BitBlt函數最後一個參數的常用取值及意思。


參考(百度)
BLACKNESS:表示使用與物理調色板的索引0相關的色彩來填充目標矩形區域,(對缺省的物理調色板而言,該顏色為黑色)。  
DSTINVERT:表示使目標矩形區域顏色取反。   
MERGECOPY:表示使用布爾型的AND(與)操作符將源矩形區域的顏色與特定模式組合一起。   MERGEPAINT:通過使用布爾型的OR(或)操作符將反向的源矩形區域的顏色與目標矩形區域的顏色合並。 NOTSRCCOPY:將源矩形區域顏色取反,於拷貝到目標矩形區域。   
NOTSRCERASE:使用布爾類型的OR(或)操作符組合源和目標矩形區域的顏色值,然後將合成的顏色取反。 PATCOPY:將特定的模式拷貝到目標位圖上。   
PATPAINT:通過使用布爾OR(或)操作符將源矩形區域取反後的顏色值與特定模式的顏色合並。然後使用OR(或)操作符將該操作的結果與目標矩形區域內的顏色合並。   
PATINVERT:通過使用XOR(異或)操作符將源和目標矩形區域內的顏色合並。   
SRCAND:通過使用AND(與)操作符來將源和目標矩形區域內的顏色合並。   
SRCCOPY:將源矩形區域直接拷貝到目標矩形區域。   
SRCERASE:通過使用AND(與)操作符將目標矩形區域顏色取反後與源矩形區域的顏色值合並。   SRCINVERT:通過使用布爾型的XOR(異或)操作符將源和目標矩形區域的顏色合並。  
SRCPAINT:通過使用布爾型的OR(或)操作符將源和目標矩形區域的顏色合並。   
WHITENESS:使用與物理調色板中索引1有關的顏色填充目標矩形區域。(對於缺省物理調色板來說,這個顏色就是白色)


第四十四個GetWindowText根據窗口句柄獲得窗口標題名


函數定義:int GetWindowText(HWND hWnd,LPTSTR lpString,intnMaxCount);


第一個參數hWnd是要獲取窗口標題名的窗口句柄,第二個lpString是個字符串,窗口標題名,將會存儲在這裏麵,第三個參數nMaxCount指明了第二個參數字符數組的大小。


下麵結合GetCursorPos和WindowFromPoint舉個例子,鼠標指向哪個窗口,就在界麵顯示那窗口的標題名


#include<windows.h>
#include<stdio.h>
int main()
{
char Text[256]={0};
HWND wnd;
POINT curpos;
while(1)
{
GetCursorPos(&curpos);
wnd = WindowFromPoint(curpos);
GetWindowText(wnd,Text,256);
printf("%s\n",Text);
Sleep(300);
}


return 0;
}


第四十五個SetWindowText根據窗口句柄設置窗口標題名


這個函數有兩個參數,一個是窗口句柄,一個是標題名,這裏就不需要解釋了吧,直接看例子,設置一個窗口標題名,依舊以


"無標題.txt - 記事本"為例。


#include<windows.h>
#include<stdio.h>
int main(int argc, char* argv[])
{
HWND wnd;
wnd=FindWindow(NULL,"無標題.txt - 記事本");//獲取窗口句柄
SetWindowText(wnd,"新的窗口標題");//設置窗口標題名
return 0;
}


第四十六個GetCurrentProcess獲得當前線程句柄


沒有參數,直接調用即可,該函數返回線程句柄


第四十七個OpenProcessToken獲得一個進程的訪問令牌句柄


獲得一個進程的訪問令牌有什麼用呢?主要是為了修改它的權限,前麵在介紹結束一個進程的時候說過了,無法結束係統進程,是什麼原因呢,原因是調用OpenProcess函數失敗,無法獲取係統進程句柄而引起的,那為什麼會失敗呢,權限不夠,普通程序的進程沒有SeDeDebug權限,而一個進程的權限是與訪問令牌相關的,這樣我們隻要獲取一個進程的訪問令牌句柄,再以這個句柄為參數調用相應的函數提升進程的權限為SeDeDebug就可以獲取係統進程句柄,進而結束它。


函數定義:BOOL OpenProcessToken(HANDLE ProcessHandle,DWORDDesiredAccess,PHANDLE TokenHandle)


第一個參數ProcessHandle待獲取的進程句柄,第二個參數DesiredAccess操作類型,填TOKEN_ADJUST_PRIVILEGES就行了,


第三個TokenHandle是訪問令牌句柄的指針,該參數接收句柄。


如獲得本進程的訪問令牌句柄:HANDLE hToken;


OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES,&hToken);


第四十七個LookupPrivilegeValue函數查看對應係統權限的特權值,返回信息到一個LUID結構體裏
上麵講過了,進程有權限一說,那麼大家也能猜到,進程權限的信息也一定存儲在一個結構體裏,這個結構體描述了進程權限相關的一些信息。這個結構體在這裏就不具體描述了,我們所要做的,隻是把一個進程權限設置成SeDeDebug就行了,所以我們隻要知道TOKEN_PRIVILEGES便是描述進程權限的結構體就可以了。而LookupPrivilegeValue函數是根據訪問令牌句獲取相應的權限信息嗎?
不是的。TOKEN_PRIVILEGES結構裏的Privileges[0].Luid跟這個函數所查詢的東西相對應,也就是說,如果進程是SeDeDeBug權限,那Privileges[0].Luid的取值是怎樣的呢?用LookupPrivilegeValue函數便可以獲取其取值。
這個函數是這樣定義的:BOOL LookupPrivilegeValue(LPCTSTRlpSystemName,LPCTSTR lpName,PLUID lpLuid);
第一個參數lpSystemName通常都填NULL,本地係統調用,第二個參數lpName填要查詢的權限名,如要查詢的是SeDeDebug權限則取值是SE_DEBUG_NAME,第三個參數lpLuid接收其取值。
如LUIDluid;LookupPrivilegeValue(NULL,SE_DEBUG_

最後更新:2017-04-03 16:49:10

  上一篇:go J2EE的web.xml中filter-mapping的位置導致的亂碼問題
  下一篇:go SplishActivity的作用