第一個:FindWindow根據窗口類名或窗口標題名來得到窗口的句柄,該函數返回窗口的句柄html
函數的定義:HWND WINAPI FindWindow(LPCSTR lpClassName ,LPCSTR lpWindowName);linux
第一個參數填窗口的類名,第二個填窗口的標題名,實際上是不須要同時填兩個參數的,也就是說,你只要知道窗口的類名或窗口的標題就能夠了,沒有的那個就用NULL代替。c++
好比如今有一個窗口名爲"無標題.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++可查看窗口類名。windows
第二個: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, int nWidth, 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);
第十一個: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);
}
}
第十二個:GetCWindowRect得到窗口大小(相對屏幕)
例子:
#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);
}
}
試着去找一下GetClientRect和GetWindowRect之間有什麼區別;
第十三個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進行按位與運算,若是爲1的話,代表爲文夾件,如if(fd.dwFileAttributes&FILE_ATTRIBUTE_DIRECTORY==1) 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, int nMaxCount);
這種函數不須要再解釋了吧,前面有太多相似的例子。
第十七個SetFileAttributes設置文件屬性
函數定義:BOOL SetFileAttributes( LPCTSTR lpFileName, DWORD dwFileAttributes);
這個函數的第二個參數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, LPCSTR lpFile, 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","http://www.baidu.com",NULL,NULL,SW_SHOWNORMAL);
ShellExecute(NULL,"open","C:",NULL,NULL,SW_SHOWNORMAL);
相似的函數還有WinExec,只有兩個參數,它的最後一個參數跟ShellExecute函數的最後一個參數同樣.
而第一個參數則是程序路徑名.舉個例子:WinExce("NOTEPAD.EXE",SW_SHOWNORMAL);
這個函數也能夠給程序傳遞一個文件名供要運行的程序打開,那要如何加進去呢,這裏又沒有第三個參數,
方法把路徑名加在NOTPEPAD.EXE的後面,要以空格來分開如:
WinExce("NOTEPAD.EXE e:\\a.txt",SW_SHOWNORMAL);
第十九個PlaySound播放一個WAV文件
函數定義:BOOL PlaySound(LPCSTR pszSound, HMODULE hmod,DWORD fdwSound);
第一個參數是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, LPTSTR lpFilename, 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;
}
第二十一個CreateWindow建立一個窗口
//補習懂的人直接跳過
以前API函數的例子,都是針對DOS編程的,嚴格來講是在windows下的仿DOS(cmd)進行編程,編寫控制檯應用程序你們都知道,主函數是main,那針對windows編程的主函數也是main嗎?不是的,windows下的主函數(入口函數)是WinMain。在定義main主函數的時候,能夠給它帶兩個參數,也能夠不帶。而WinMain函數就不能這樣了,它有固定的格式,它必須帶四個參數。
現給出WinMain函數的固定格式:
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, instance LPSTR lpCmdLine, int nCmdShow)
你們若是有興趣能夠經過其它渠道瞭解一下各參數的意思,如今咱們只須要知道WinMain函數就是這樣定義的,不理解也不要緊。
知道了這個咱們就來編一個WINDOWS程序吧!
由於咱們是針對windows編程,因此要建一個Win32 Application工程,步驟是點擊文件,而後選擇新建,在彈出的對話框裏選擇工程,再選中Win32 Application 接着在右邊的工程名稱下填工程名稱,名字隨便取。以後點肯定。接着又彈出了一個對話框,這裏爲了方便,咱們選擇「一個簡單的 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,UINT uMsg,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,UINT uType,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);
}
}
第三十個CreateToolhelp32Snapshot給當前進程拍一個照
HANDLE hProcessSnap=::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, COLORREF crColor);
第一個參數,表示是什麼類型的線,取值有如下:
如建立一個畫筆:HPEN pen=CreatePen(PS_SOLID,3,RGB(255,78,99));
PS_SOLID 畫筆畫出的是實線 PS_DASH 畫筆畫出的是虛線(nWidth必須是1) PS_DOT 畫筆畫出的是點線(nWidth必須是1)
PS_DASHDOT 畫筆畫出的是點劃線(nWidth必須是1) PS_DASHDOTDOT 畫筆畫出的是點-點-劃線(nWidth必須是1)
第二個參數是畫筆的寬度,第三個參數是畫筆的顏色,COLORREF類型能夠RGB來得到如RGB(233,128,88);分別是紅綠藍。
第三十五個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,PVOID pvAddressRemote,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_ATTRIBUTES lpThreadAttributes,DWORD dwStackSize,LPTHREAD_START_ROUTINE lpStartAddress,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;
第四十一個CreateCompatibleDC建立一個兼容的內存設備上下文(DC)
簡單的來講,就是複製一個模同樣的DC。就把窗口當作一幅幅圖畫,窗口有大有小,裏面的內容也不同(顏色值),每一個像素點的顏色值可能不同,因此就用設備上下文來描述每一個窗口的信息,對於DC具體是怎樣描述設備上下文的,咱們暫時還不須要知道,只要瞭解這個概念就好了。這個窗口信息,得到一個窗口設備上下文,就用GetDC函數就好了,如HDC hDC=GetDC(hWnd);而CreateCompatibleDC的做用是根據一個設備上下文,再建立一個兼容的設備上下文,如 HDC mDC=CreateCompatibleDC(hDC)。這樣mDC裏的信息就跟hDC裏的同樣,那這有什麼用呢?這個將會在後面的BitBltl輸出一個位圖(合併兩個DC)函數裏會用到。
第四十二個GetObject獲取一個對象信息(如位圖,圖標,光標)
函數定義:int GetObject(HGDIOBJ hgdiobj, int cbBuffer, LPVOID lpvObject);
第一個參數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,int nWidth,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,int nMaxCount);
第一個參數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,DWORD DesiredAccess,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(LPCTSTR lpSystemName,LPCTSTR lpName,PLUID lpLuid);
第一個參數lpSystemName一般都填NULL,本地系統調用,第二個參數lpName填要查詢的權限名,如要查詢的是SeDeDebug權限則取值是SE_DEBUG_NAME,第三個參數lpLuid接收其取值。
如LUID luid;LookupPrivilegeValue(NULL,SE_DEBUG_NAME,&luid);
第四十八個AdjustTokenPrivileges調整一個進程的訪問令牌信息(權限)
函數定義:BOOL AdjustTokenPrivileges(HANDLE TokenHandle,BOOL DisableAllPrivileges,PTOKEN_PRIVILEGES NewState,DWORD BufferLength,PTOKEN_PRIVILEGES PreviousState,PDWORD ReturnLength)
第一個參數TokenHandle是令牌句柄,第二個是禁用全部權限標誌,後面填FALSE就好了。第三個NewState是待刷進令牌句柄的PTOKEN_PRIVILEGES結構信息指針,第四個BufferLength指明TOKEN_PRIVILEGES結構大小,第五,六個參數填NULL就好了。
那麼結束上面兩個函數,提高一個進程權限制,讓它可以結束系統進程的代碼就是:
HANDLE hToken;
OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
TOKEN_PRIVILEGES tp;
LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid );
tp.PrivilegeCount = 1; //tp裏其它一些屬性設置
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof( TOKEN_PRIVILEGES ), NULL, NULL );
只上把上面的代碼,加入結束普通進程例子的前面,那麼就能結束系統進程了。
第四十九個LoadLibrary加載動態連接庫,返回動態連接庫模塊句柄
該函數只有一個參數,那就是動態連接庫的名稱,如user32.dll,函數返回HMOUDLE類型的模塊句柄,得到了一個動態連接庫的模塊句柄,就能夠調用GetProcAddress函數得到模塊裏面的函數地址,從而調用動態連接庫裏的函數。
第五十個GetProcAddress根據模塊句柄獲取相應的函數地址
提到GetProcAddress函數,不得不講一下怎麼設計一個動態連接庫,這裏咱們就以本身設計動態連接庫做爲GetProcAddress函數的例子。
動態連接庫裏的函數相對於頭文件的函數有什麼優點呢?更節省內存,相對於比較經常使用的函數而已。若是在一個程序裏,調用一個頭文件裏的函數的話,那無論如何,函數的代碼就會被複制一份到當前程序裏,因此,當有十幾個程序調用同一個函數的時候,這個函數在內存中所佔用的空間,就會有十幾份,分別在各自調用的進程內存空間裏,而動態連接庫的函數,只在內存中有一份空間(公用空間)若是哪一個程序要用到這個函數的話,只要給這個函數的地址,進程就能夠跑到這個空間執行函數,那麼如何獲取函數地址呢,用GetProcAddress函數就好了。
下面咱們就本身設計一個動態連接庫,點「文件->新建->工程",而後選中「Win32 Dynamic-Link Library」,再在右邊給工程取一個名,點肯定。接着彈出了一個對話框,詢問但願建立什麼類型,咱們選擇第二個「一個簡單的DLL工程」,點完成->肯定.而後單擊右邊的「+」號,很小的一個,接着下面會出現一個Globals的"+"號,單擊該加號,而後再雙擊DllMain函數,進入代碼編輯區,在這裏編寫代碼,這裏已經有了一些代碼了,編譯器自動生成的。那個DllMain函數,即是動態連接庫的主函數。在程序進程加載動態連接的時候,進程會自動調用DllMain函數,也就是說會自動執行DllMain函數裏的代碼,也就是說,若是哪程序執行了這個語句「LoadLibrar("user32.dll")",那麼執行這個語句的進程,便會自動執行user32.dll裏的DllMain函數。若是是主線程加載動態庫的話,那麼該DllMain函數裏的代碼會被執行兩次,分別是加載的時候執行一次,調用FreeLibrary函數釋放或程序結束自動釋放動態連接庫的時候執行一次,至因而什麼緣由致使DllMain函數被調用,DllMain函數的第二個參數ul_reason_for_call說明了緣由,它有四個取值,表明了四個緣由。分別是:
DLL_PROCESS_ATTACH(進程加載),DLL_THREAD_ATTACH (線程加載)
DLL_THREAD_DETACH(線程釋放),DLL_PROCESS_DETACH(進程釋放)
由於這裏咱們只要設計一個動態連接函數,因此便不用管DllMain函數,DllMain函數將會在介紹CreateRemoteThread(建立一個遠程線程)函數的時候講到,因此咱們只要在DllMain函數外定義一個函數就好了。
那麼在動態連接庫是如何定義函數呢?若是函數不須要導出的話,則跟普通函數定義沒什麼兩樣,導出是什麼意思,就是能夠用GetProcAddress函數獲取地址的函數。那導出的函數要如何定義呢?
只要在函數前面加上extern "C" __declspec(dllexport)就好了,聲明導出函數,防止函數重命名。那麼接下來就舉個例子。
動態連接裏的代碼:
#include "stdafx.h"
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
extern "C" __declspec(dllexport) int Add(int a,int b)
{
return a+b;
}
點編譯執行,而後就會彈出一個調試對話框,直接點取消,接着便生成了動態連接庫DLL,而後到你的工程裏把後綴名爲dll的文件找到,
位置在MyProject\"你的工程名"\Debug下。接着把這個文件複製到要調用的工程下,或者直接複製C:\windows\system32目錄下。
假設這個文件名爲"sss.dll",那麼要調用裏面的Add函數即是以下代碼:
HMODULE hmod=::LoadLibrary("sss.dll"); //獲取sss.dll的模塊,加載sss.dll動態連接庫
typedef int (*pAdd)(int a,int b); //定義一個對應的函數型,以便識別
pAdd add=(pAdd)GetProcAddress(hmod,"Add"); //獲取hmod模塊裏的Add函數地址
int a=add(3,5); //調用模塊裏的Add函數
第五十一個SetWindowsHookEx安裝一個鉤子
WINDOWS是基於消息的系統,鼠標移動,單擊,鍵盤按鍵,窗口關閉等都會產生相應的消息,那麼鉤子是什麼意思呢,它能夠監控一個消息,好比在一個窗口裏單擊了一下,首先得到這個消息的,不是應用程序,而是系統,系統獲取這個消息後,就去查看這個消息是在哪一個窗口產生的,找到窗口後,再把消息投遞到相應程序裏的消息隊列裏,這之間有一個傳遞過程,那麼鉤子的做用就是在消息到達應用程序以前截獲它,鉤子能夠關聯一個函數(鉤子處理函數),也就是說,若是對一個進程安裝了一個鉤子,進程再接收到相應在消息以前,會先去執行鉤子所關聯的函數,
先來看一下這個函數定義:
HHOOK WINAPI SetWindowsHookEx(int idHook,HOOKPROC lpfn,HINSTANCE hmod,DWORD dwThreadId)
第一個參數idHook指明要安裝的鉤子類型,如WH_KEYBOARD(鍵盤鉤子),WH_MOUSE(鼠標鉤子),第二個參數是鉤子處理函數的地址,該函數必須是這種固定的格式:LRESULT WINAPI HookProc(int nCode,WPARAM wParam,LPARAM lParam)
第三個參數hmod是鉤子函數所在模塊的句柄,第四個參數dwThreadId是線程ID,待監視消息的ID,若是爲0,則爲全局鉤子,監視全部消息
好,接下來咱們舉一個例子,鉤子類型爲WH_KEYBOARD,全局鉤子。截獲鍵盤按鍵消息,並扔掉該消息,讓鍵盤失靈。
因爲是裝的是全局鉤子,因此鉤子處理函數必須放在動態連接庫裏。那麼咱們就設計一個動態連接庫吧。
現給出動態連接庫的全部代碼:(KeyDll.dll)
#include "stdafx.h"
#include<windows.h>
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}
HMODULE WINAPI ModuleFromAddress(PVOID pv) //該函數根據內存地址,得到其所在的模塊句柄
{
MEMORY_BASIC_INFORMATION mbi;
VirtualQuery(pv,&mbi,sizeof(mbi));
return (HMODULE)mbi.AllocationBase;
}
LRESULT CALLBACK HookKey(int nCode,WPARAM wParam,LPARAM lParam)
{
return TRUE; //返回真,扔掉該消息
}
extern "C" __declspec(dllexport) void SetHook(void)
{
SetWindowsHookEx(WH_KEYBOARD,HookKey,ModuleFromAddress(HookKey),0);
}
生成dll文件後,把它複製到相應的目錄下去。
再新建一個工程,調用用動態連接庫裏的函數,代碼以下:
#include<windows.h>
int main()
{
HMODULE hMod=LoadLibrary("KeyDll.dll");
typedef void(*pSetHook)(void);
pSetHook SetHook=(pSetHook)GetProcAddress(hMod,"SetHook");
SetHook();
while(1)
{
Sleep(1000); //避免程序結束,自動釋放動態連接庫
}
return 0;
}
這樣當按下了一個鍵後,接收該按鍵消息的進程,會先去執行鉤子處理函數,而後再處理消息,而鉤子處理函數的幾個參數說明了按鍵的詳細信息,如按了哪一個鍵,是按下(KEYDOWN)仍是鬆開(KEYUP)。若是有興趣的話,把上面那鉤子處理函數的代碼換成下面這個
LRESULT CALLBACK HookKey(int nCode,WPARAM wParam,LPARAM lParam)
{
char sz[25];
sprintf(sz,"%c",wParam); //這個函數頭文件#include<stdio.h>
MessageBox(NULL,sz,sz,MB_OK);
return FALSE;
}
每按下一個鍵,就會彈出一個提示框,並輸出所按下的鍵,只對字符鍵有用。
第五十二個SHGetFileInfo獲取一個文件的各項信息(文件關聯圖標,屬性等)
函數定義: DWORD SHGetFileInfo(LPCSTR pszPath, DWORD dwFileAttributes, SHFILEINFOA FAR *psfi, UINT cbFileInfo, UINT uFlags);
pszPath是文件的路徑,dwFileAttributes通常取0,若是想要獲取文件夾信息的話,則取值爲FILE_ATTRIBUTE_DIRECTORY,psfi是一個SHFILEINFO結構的指針,該結構存儲文件信息,定義以下:
typedef struct _SHFILEINFOA
{
HICON hIcon; // 文件關聯圖標句柄
int iIcon; // 系統圖標列表索引
DWORD dwAttributes; // 文件的屬性
CHAR szDisplayName[MAX_PATH]; // 文件的路徑名
CHAR szTypeName[80]; // 文件的類型名,如是bmp文件,仍是執行文件exe,或者其它
} SHFILEINFO;
第四個參數cbFileInfo指明SHFILEINFO結構的大小,填sizoef(SHFILEINFO);
最後一個參數uFlags指定獲取文件的什麼信息,可選取值以下:(對應着SHFILEINFO裏的成員)
SHGFI_ICON; //得到圖標
SHGFI_DISPLAYNAME; //得到顯示名
SHGFI_TYPENAME; //得到類型名
SHGFI_USEFILEATTRIBUTES; //得到屬性
SHGFI_LARGEICON; //得到大圖標
SHGFI_SMALLICON; //得到小圖標
SHGFI_PIDL; // pszPath是一個標識符
好比,我只要獲取文件圖標,那麼參數填SHGFI_LARGEICON就好了。若是又想獲取文件關聯的圖標,又想獲取文件類型名,那麼就是
SHGFI_LARGEICON|SHGFI_TYPENAME;
函數例子:
SHFILEINFO sfi;
SHGetFileInfo("e:\\aa.bmp",0,&sfi,sizeof(sfi),
SHGFI_ICON|SHGFI_LARGEICON|SHGFI_USEFILEATTRIBUTES|SHGFI_TYPENAME);
接着能夠用DrawIcon函數畫出文件關聯圖標:該函數定義:BOOL DrawIcon(HDC hDC,int X,int Y, HICON hlcon );
第五十三個RegCreateKeyEx在註冊表裏建立一個子鍵,或獲取一個子鍵的句柄
在這裏咱們先來了解一下注冊表的基本概念,打開運行對話框,輸入regedit,而後回車,便打開了註冊表編輯器,首先映入眼前的,即是五個根鍵
HKEY_CLASSES_ROOT
HKEY_CURRENT_USER
HKEY_LOCAL_MACHINE
HKEY_USER
HKEY_CURRENT_CONFIG
在根鍵下面即是主鍵了,如HKEY_CURRENT_CONFIG根鍵下有兩個主鍵,分別是Software和System(可能會不同),那麼主鍵下面是什麼呢,對了,就是跟 RegCreateKeyEx函數相關的子鍵,子鍵下面就是具體的鍵值項了,但也能夠又是子鍵。鍵值有五種可選類型,分別是:字符串值(REG_SZ),二進制值(REG_BINARY),DWORD值(REG_DWORD),多字符串值(REG_MULTI_SZ)和可擴充字符值(REG_EXPAND_SZ)。鍵值項還有其它信息,它的名稱,數據。
瞭解了上面這些東西,接着就來了解下RegCreateKeyEx函數的各個參數吧,先來看一下函數定義:
LONG RegCreateKeyEx (
HKEY hKey, //根鍵句柄,指明要在哪一個根鍵下建立子鍵,填根鍵名既可
LPCSTR lpSubKey, //子鍵名,包含完整路徑名
DWORD Reserved,. //通常取0
LPSTR lpClass, //通常取NULL
DWORD dwOptions, //建立子鍵時的選項,可選值REG_OPTION_NON_VOLATILE,REG_OPTION_VOLATILE,這裏取0既可
REGSAM samDesired, //打開方式,填KEY_ALL_ACCESS,在任何狀況都行。
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指定繼承性,仍是取0
PHKEY phkResult, //子鍵對應句柄,待建立或打開的子鍵句柄將存儲在該句柄裏
LPDWORD lpdwDisposition //打開仍是建立子鍵,對應REG_CREATED_NEW_KEY和REG_OPENED_EXISTING_KEY
);
在這裏舉一個例子,以便咱們能更好的理解該函數。
在HKEY_CURRENT_CONFIG根鍵下的Software主鍵裏建立一個名爲MySelf的子鍵。
#include<windows.h>
int main()
{
HKEY hroot; //子鍵句柄
DWORD dwDisposition; //對應着最後一個參數
RegCreateKeyEx(HKEY_CURRENT_CONFIG,"Software\\MySelf",0,NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
return 0;
}
第五十四個RegSetValueEx根據子鍵句柄在其下建立或修改一個鍵值
函數定義:
LONG RegSetValueEx(
HKEY hKey, // 子鍵句柄
LPCTSTR lpValueName, // 鍵值名稱,若是提供的子鍵下沒有該名稱,則建立
DWORD Reserved, // 保留,填0
DWORD dwType, // 鍵值類型,
CONST BYTE *lpData, // 鍵值的數據
DWORD cbData // 鍵值的數據的大小
);
接着咱們以增長開機自啓動爲例,來看一下函數是如何建立一個鍵值的,咱們知道,像程序添加開機自啓動通常都在
HKEY_LOCAL_MACHINE\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run下添加一個鍵值,鍵值類型爲二進制(REG_SZ),而鍵值的數據就爲要自啓動程序的路徑。
假設e盤下有一個AutoRun.exe的應用程序,讓電腦開機時自動運行它。
#include<windows.h>
int main()
{
HKEY hroot; //子鍵句柄
DWORD dwDisposition;
RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
RegSetValueEx(hroot,"AutoRun",0,REG_SZ,(BYTE *)"e:\\AutoRun.exe",sizeof("e:\\AutoRun.exe"));
return 0;
}
第五十五個RegDeleteValue根據子鍵句柄刪除其下的一個鍵值
這裏直接舉一個例子,刪除RegSetValueEx函數建立的鍵值
#include<windows.h>
int main()
{
HKEY hroot; //子鍵句柄
DWORD dwDisposition;
RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition);
RegDeleteValue(hroot,"AutoRun"); //刪除子鍵下名爲AutoRun的鍵值
return 0;
}
第五十六個RegQueryValueEx根據子鍵句柄獲取一個鍵值數據,類型。
函數定義:
LONG RegQueryValueEx (
HKEY hKey, //根鍵句柄
LPCWSTR lpValueName, //鍵值名稱
LPDWORD lpReserved, //預留,填0
LPDWORD lpType, //接收鍵值類型
LPBYTE lpData, //接收鍵值數據
LPDWORD lpcbData //接收數據的大小
);
例子,獲取RegSetValueEx函數建立的鍵值的類型,數據
int main()
{
char Data[52];
DWORD Size,Type;
HKEY hroot; //子鍵句柄
DWORD dwDisposition;
RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition); //獲取根鍵句柄
RegQueryValueEx(hroot,"AutoRun",0,&Type,(BYTE *)Data,&Size); //獲取AutoRun的信息
printf("鍵值名稱:AutoRun ");
switch(Type)
{
case REG_SZ:printf("鍵值類型:REG_SZ");break;
case REG_BINARY:printf("鍵值類型:REG_BINARY");break;
case REG_DWORD:printf("鍵值類型:REG_DWORD");break;
case REG_MULTI_SZ:printf("鍵值類型:REG_MULTI_SZ");break;
case REG_EXPAND_SZ:printf("鍵值類型:REG_EXPAND");break;
}
printf(" 鍵值數據:%s %d\n",Data,Size);
return 0;
}
第五十七個RegEnumValue根據子鍵句柄返回對應索引的鍵值信息(名稱,數據,類型,子鍵下第一個鍵值索引爲0,以此類推,函數成功執行返回ERROR_SUCCESS)
函數定義:
LONG RegEnumValue (
HKEY hKey, //子鍵句柄
DWORD dwIndex, //鍵值索引
LPWSTR lpValueName, //接收鍵值名稱,字符數組
LPDWORD lpcbValueName, //指明數組大小
LPDWORD lpReserved, //預留,0
LPDWORD lpType, //鍵值類型,填NULL,不獲取
LPBYTE lpData, //鍵值數據,填NULL,不獲取
LPDWORD lpcbData //接收數據的大小,若是鍵值數據那項參數爲NULL,則該項也爲NULL
);
例子:輸出Run下的全部鍵值名
#include<windows.h>
#include<stdio.h>
int main()
{
char Name[52];
int Index=0;
DWORD dwSize=52;
DWORD Size,Type;
HKEY hroot; //子鍵句柄
DWORD dwDisposition;
RegCreateKeyEx(HKEY_LOCAL_MACHINE,"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run",0,
NULL,0,KEY_ALL_ACCESS,NULL,&hroot,&dwDisposition); //獲取根鍵句柄
while(RegEnumValue(hroot,Index,Name,&dwSize,NULL,NULL,NULL,NULL)==ERROR_SUCCESS)
{
printf("%s\n",Name);
Index++; //索引從0開始每次自增一,函數若是執行失敗,則索引已到頭
}
return 0;
}
其實也還能夠擴充一下,能夠像msconfig程序那樣列出當前計算機的全部開機自啓動程序,固然,註冊表也不僅就前面的那一個子鍵下能夠添加自啓動程序,在HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run下也能夠添加,因此這些子鍵都須要去查看,更多添加自啓動程序的子鍵能夠到百度裏去搜一下,你們若是掌握前面那幾個註冊表操做函數,能夠結合起來試着作一個能夠添加,查看,刪除開機自啓動程序的小程序。
第五十八個ExitWindowsEx關機,重啓,註銷計算機函數
這個函數只有兩個參數,後一個參數爲系統預留,填0就能夠了,而第一個參數則,指明關機,仍是重啓,或註銷,可選值以下:
EWX_LOGOFF//註銷 EWX_REBOOT//重啓 NT系統中需SE_SHUTDOWN_NAME 特權 EWX_SHUTDOWN//關機,需權限。
例子:關閉計算機,因爲須要SE_SHUTDOWN_NAME權限,因此咱們得先提高權限,代碼以下:
#include<windows.h>
int main()
{
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
OpenProcessToken(GetCurrentProcess(),TOKEN_ADJUST_PRIVILEGES|TOKEN_QUERY,&hToken);
LookupPrivilegeValue(NULL,SE_SHUTDOWN_NAME,&tkp.Privileges[0].Luid);
tkp.PrivilegeCount=1;
tkp.Privileges[0].Attributes=SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken,FALSE,&tkp,0,(PTOKEN_PRIVILEGES)NULL,0);
::ExitWindowsEx(EWX_SHUTDOWN,0);
return 0;
}
第五十九個VirtualAllocEx在其它的進程中分配內存空間
函數定義:
LPVOID VirtualAllocEx(
HANDLE hProcess, //進程句柄,將會在該進程句柄相關的進程分配空間
LPVOID lpAddress, //默認爲系統指定,填NUL
DWORD dwSize, //分配多大的內存
DWORD flAllocationType, //填MEM_COMMIT
DWORD flProtect //指定分配的內存屬性,爲PAGE_READWRITE,內存可讀寫
);
函數返回分配的內存首地址,
第六十個CreateRemoteThread建立一個遠程線程(在其它進程中建立線程)
函數定義:
HANDLE WINAPI
CreateRemoteThread(HANDLE hProcess, //進程句柄,函數將在這個進程句柄關聯的進程建立線程
LPSECURITY_ATTRIBUTES lpThreadAttributes,
DWORD dwStackSize,
LPTHREAD_START_ROUTINE lpStartAddress,
LPVOID lpParameter,
DWORD dwCreationFlags,
LPDWORD lpThreadId
);
這個函數比CreateThread函數多了一個參數,就是這個函數的第一個hProcess(函數在該進程裏建立線程),後面的六個參數跟第三十九個函數CreateThread的六個參數同樣,這裏就再也不解釋了。
例子:遠程線程注入
建立一個遠程線程,就必須得有一個線程函數供線程執行,而線程函數又不能在其它程序裏。那要怎麼辦呢?你們看一下線程函數的定義,和LoadLibrary函數的定義,它們的定義類似,都是隻有一個參數,並且每一個程序都能調用LoadLibrary函數,這樣咱們便能把LoadLibrary函數做爲線程函數。這樣建立的線程就會去執行LoadLibrary函數。於是咱們就有了一次讓其它程序調用LoadLibrar函數的機會,並還能夠指定LoadLibrary函數的參數(經過建立遠程線程函數傳遞)。前面在動態連接庫提到,一個程序若是調用LoadLibrary函數,它都會自動去執行相應動態連接庫裏的DllMain函數,因此咱們本身能夠編寫一個動態連接庫,在DllMain函數裏寫入想要其它程序執行的代碼。再經過CreateRemoteThread函數在其它程序建立一個線程去執行LoadLibary加載咱們已經編寫好的動態連接庫,這樣就可讓其它程序執行咱們的代碼了。這裏還有一個問題,CreateRemoteThread函數傳遞過去的參數,由於要供注入的那個程序訪問,因此參數數據所存儲的空間不能在調用CreateRemoteThread函數的程序裏。必須調用VirtualAllocEx函數,在注入程序裏分配一個空間,把數據(動態連接庫的名稱)存在裏面,而新分配空間的首地址則做爲CreateRemoteThread函數的參數傳過去。這樣注入程序訪問的是本身的地址空間。
遠程線程注入:
假設動態連接庫爲「ReCode.dll」它的代碼以下:
#include<windows.h>
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
) //DllMain函數,只要加載這個動態連接庫的程序,都會跑來執行這個函數
{ //在這裏填讓其它程序執行的代碼
while(1)
{
MessageBox(NULL,"aaaa","aaaa",MB_OK); //簡單的讓其它程序每隔3秒彈出一個提示框
Sleep(3000);
}
return TRUE;
}
編譯運行,而後把生成的「ReCode.dll」文件複製到c:\\windows\\system23下去。
注入線程的代碼:
//選擇ctfmon.exe(輸入法管理)做爲咱們要注入進線程的程序
#include<windows.h>
#include<tlhelp32.h>
#include<stdio.h>
int main()
{
char DllName[25]="ReCode.dll";
HANDLE hProcess; //用於存儲ctfmon.exe的進程句柄
//先提高進程權限,使其能獲取任何進程句柄,並對其進行操做
HANDLE hToken;
OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken );
TOKEN_PRIVILEGES tp;
LookupPrivilegeValue( NULL, SE_DEBUG_NAME, &tp.Privileges[0].Luid );
tp.PrivilegeCount = 1;
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges( hToken, FALSE, &tp, sizeof( TOKEN_PRIVILEGES ), NULL, NULL );
////////////////////////////////////////////////////////////////////////////
//Process32First和Process32Next函數結合(尋找)獲取ctfmon.exe進程ID號
//再調用OpenProcess函數根據進程ID得到進程句柄
PROCESSENTRY32 pe32; //進程相關信息存儲這個結構裏
pe32.dwSize=sizeof(pe32);
//給系統內的全部進程拍一個快照
HANDLE hProcessSnap=::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
BOOL bMore=::Process32First(hProcessSnap,&pe32);
while(bMore)
{
if(strcmp("ctfmon.exe",pe32.szExeFile)==0) //若是找到進程名爲ctfmon.exe
hProcess=OpenProcess(PROCESS_ALL_ACCESS,FALSE,pe32.th32ProcessID); //獲取句柄
bMore=::Process32Next(hProcessSnap,&pe32); //尋找下一個
}
//在ctfmon進程中分配空間
LPVOID lpBuf=VirtualAllocEx(hProcess,NULL,strlen(DllName),MEM_COMMIT, PAGE_READWRITE );
DWORD WrSize;
//把DllName裏的數據寫入到分配的空間裏
WriteProcessMemory(hProcess, lpBuf, (LPVOID)DllName, strlen(DllName), &WrSize);
//建立遠程線程
CreateRemoteThread(hProcess,NULL,0,(LPTHREAD_START_ROUTINE)LoadLibraryA,lpBuf,0,NULL);
return 0; //程序使命完成,結束
}
固然,給一個程序安裝鉤子,也可讓指定的應用程序加載特定的動態連接庫,但要了解,加載動態連接庫的是是應用程序的主程序,你總不能讓應用程序不干它本身的事,而來一直執行DllMain函數裏的代碼吧!並且即便這樣,當安裝鉤子的程序退出或卸載鉤子的時候,那麼被系統強迫加載動態連接庫的程序,也會自動釋放動態鏈庫,退出DllMain函數。如此,那就沒有辦法了嗎?,辦法確定是有的,用CreateThread函數。當其它程序主線程執行DllMain函數的時候,使其調用CreateThread再建立一個線程,就好了
第六十一個GetWindowThreadProcessId根據窗口句柄得到對應進程ID號,和線程ID號
函數只有兩個參數,第一個參數是窗口句柄,第二個參數是一個DOWRD類型的指針,函數返回線程ID
如:
DWORD ThreadId,ProcessId;
ThreadId=GetWindowThreadProcessId(wnd,&ProcessId);
第六十二個EnumWindows枚舉當前正運行的全部主窗口,不包括子窗口
調用這個函數,還必須定義一個回調函數,它的格式是這樣的:BOOL CALLBACK lpEnumFunc(HWND wnd, LPARAM lParam);
EnumWindows函數有兩個參數,第一個就是回調函數的地址,另外一個是自定義參數,對應着回調函數的第二個參數。
每枚舉一次窗口,這個回調函數就會被執行一次,而得到的窗口句柄,就會傳遞給回調函數,對應着回調函數的第一個參數。直到枚舉完全部窗口。而在回調用函數裏,返回真表示繼續枚舉,返回假則中止枚舉。
例子:枚舉窗口
#include<windows.h>
#include<stdio.h>
BOOL CALLBACK lpEnumFunc(HWND hwnd, LPARAM lParam);
int main()
{
::EnumWindows(lpEnumFunc,0);
return 0;
}
BOOL CALLBACK lpEnumFunc(HWND wnd, LPARAM lParam)
{
char WindowName[52]={0};
GetWindowText(wnd,WindowName,sizeof(WindowName)); //根據窗口句柄得到窗口名
printf("窗口句柄:%d 窗口名稱:%s\n,",wnd,WindowName);
//能夠在這裏加個判斷當前是否有一個窗口正在運行
//如if(strcmp(WindowName,"Windows 任務管理器")==0) return 0;結束枚舉,
return 1;
}
第六十三個MessageBox彈出一個消息提示框
int MessageBox(
HWND hWnd , //指明消息框是屬於哪一個窗口的,能夠爲NULL
LPCSTR lpText, //消息提示具體內容
LPCSTR lpCaption, //提示框窗口標題
UINT uType); //指明提示框類型,按鈕,圖標
這裏只說下uType的經常使用可選值:
按鈕:
MB_ABORTRETRYIGNORE 終止 重試 忽略
MB_OK 肯定
MB_OKCANCEL 肯定 取消
MB_RETRYCANCEL 重試 取消
MB_YESNO 是 否
MB_YESNOCANCEL 是 否 取消
圖標:
MB_ICONWARNING 感嘆號
MB_ICONINFORMATION 提示i
MB_ICONQUESTION 問號
MB_ICONSTOP 紅X號
按鈕和圖標能夠結合用,如:
MessageBox(NULL,"該內存不能訪問","出錯",MB_OK|MB_ICONSTOP);
系統模式:MB_SYSTEMMODAL
函數的返回值肯定用戶選擇了哪一個按鈕,正面給各按鈕的定義:
IDABORT 「放棄」按鈕
IDCANCEL 「取消」按鈕
IDIGNORE 「忽略」按鈕
IDNO 「否」按鈕
IDOK 肯定
IDRETRY 重試
IDYES 是
判斷返回值是否與其相等便可。
第六十四個GetForegroundWindow得到當前激活的窗口句柄
函數沒參數,調用即返回最前窗口句柄
這裏舉一個例子:每當切換窗口的時候彈出一個消息提示框
#include<windows.h>
int main()
{
char WindowName[52];
HWND wnd,Orgwnd;
wnd=Orgwnd=NULL;
while(1)
{
wnd=GetForegroundWindow();
if(wnd!=Orgwnd)
{ GetWindowText(wnd,WindowName,sizeof(WindowName));
MessageBox(NULL,WindowName,"切換窗口到",MB_OK);
Orgwnd=wnd;
}
Sleep(200);
}
}
第六十五個GetTopWindow根據窗口句柄得到其下第一子窗口句柄(若是有)
用過MFC的人都知道,在對話編輯區,若是要爲控件排序的話,就按CTRL+D顯示出每一個控件的順序,以下圖:
而GetTopWindow函數獲取的就是控件順序爲1的窗口句柄。
例子:改變一個主窗口下的第一子窗口的顯示內容(前提得它有),這裏就以上面那個abc對話框爲例:
#include<windows.h>
int main()
{
HWND wnd;
wnd=FindWindow(NULL,"abc");
HWND FirstWnd=GetTopWindow(wnd);
SetWindowText(FirstWnd,"first");
return 0;
}
執行效果:
第六十六個GetNextWindow根據子窗口句柄得到下一個或上一個同級的窗口句柄(返回NULL,函數執行失敗)
函數有兩個參數,第一個是子窗口句柄,第二個參數指明尋找上一個,仍是一下個窗口句柄,值:GW_HWNONEXT(下一個),GW_HWNDPREV(上一個)。好比子窗口句柄在主窗口的順序爲3,那麼獲取的是順序爲2或順序爲3的窗口句柄(具體取決於第二個參數),函數返回得到的窗口句柄.這樣GetNextWindow結合GetTopWindow函數就能夠遍歷一個主窗口裏的全部子窗口了。
例子:遍歷一個窗口裏的全部子窗口,以上面的abc窗口爲例
#include<stdio.h>
#include<windows.h>
int main()
{
char Name[52];
HWND wnd;
wnd=FindWindow(NULL,"abc");
wnd=GetTopWindow(wnd);
while(wnd!=NULL)
{
GetWindowText(wnd,Name,sizeof(Name));
printf("窗口句柄:%d,名稱:%s\n",wnd,Name);
wnd=GetNextWindow(wnd,GW_HWNDNEXT); //GW_HWNDNEXT獲取下一個
}
return 0;
}
第六十七個InvalidateRect發送一個WM_PAINT消息給窗口(刷新窗口)
函數定義:
BOOL InvalidateRect(
HWND hWnd , //要刷新窗口的句柄
CONST RECT *lpRect, //刷新的範圍
BOOL bErase //重畫爲TRUE
);
例子:在SetTimer函數裏會舉例
第六十八個SetTimer設置一個定時器(每隔一段時間執行一次定時器函數)
函數定義:
UINT SetTimer(
HWND hWnd , //窗口句柄
UINT nIDEvent, //定時器ID號,爲了能設置多個定時器
UINT uElapse, //時間,指明間隔多久執行一次定時器函數,單位:毫秒
TIMERPROC lpTimerFunc); //定時器回調函數的地址
定時器函數的固定格式:VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent, DWORD dwTime);
例子:在用w,a,s,d鍵控制一個矩形移動的同時,一個相同的矩形自動移動。
#include<windows.h>
LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam);//函數聲明
VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent, DWORD dwTime);
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); //更新窗口,讓窗口毫無延遲的顯示
SetTimer(hwnd,1,200,(TIMERPROC)TimerProc); //設置定時器
MSG msg; //消息結構類型
while(GetMessage(&msg,NULL,0,0)) //獲取消息
{
TranslateMessage(&msg); //此函數用於把鍵盤消息(WM_KEYDOWN,WM_KEYUP)轉換成字符消息WM_CHAR
DispatchMessage(&msg); //這個函數調用窗口過程處理函數,並把MSG裏的信息處理後傳給過程函數的四個參數
}
return 0;
}
VOID CALLBACK TimerProc(HWND hwnd,UINT uMsg,UINT_PTR idEvent, DWORD dwTime) //定時器函數
{
static int x=0,y=0;
x+=15;
if(x>500)
{y+=15;x=0;}
HDC dc=GetDC(hwnd);
Rectangle(dc,x,y,x+30,y+30);
}
LRESULT CALLBACK WinSunProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{ static int x=0,y=0;
switch(uMsg)
{
case WM_CHAR:
{
if(wParam=='a') x-=10;
else if(wParam=='d') x+=10;
else if(wParam=='w') y-=10;
else if(wParam=='s') y+=10;
RECT rect;
GetClientRect(hwnd,&rect);
::InvalidateRect(hwnd,&rect,TRUE); //發送WM_PAINT消息,刷新窗口
}
break;
case WM_PAINT:
HDC dc;
PAINTSTRUCT ps;
dc=BeginPaint(hwnd,&ps); //BeginPaint只能在響應WM_PAINT,不能用GetDC獲取設備上下文
Rectangle(dc,x,y,x+30,y+30);
break;
case WM_CLOSE: //用戶關閉了窗口
DestroyWindow(hwnd); //銷燬窗口,併發送WM_DESTROY消息
break;
case WM_DESTROY: //若是窗口被銷燬
PostQuitMessage(0); //讓進程退出
break;
}
return DefWindowProc(hwnd,uMsg,wParam,lParam); //未處理的消息經過DefWindowProc函數交給系統處理
}
第六十九個RegisterHotKey註冊一個熱鍵
函數定義:
BOOL RegisterHotKey(
HWND hWnd ,
int id,
UINT fsModifiers,
UINT vk);
第一個參數hWnd代表熱鍵消息(HOT_KEY)發送給哪一個窗口,爲NULL代表直接把消息投遞給調用這個函數進程的消息隊列。
第二個參數能夠自定取值,取值範圍0xC000-0xFFFF,這個參數是爲了程序能同時擁有多個熱鍵而存在。
第三個參數fsModifiers的可選取值以下:MOD_ALT(Alt鍵),MOD_CONTROL(Ctrl鍵),MOD_SHIFT(Shift鍵),MOD_WIN(‘田’圖標鍵)
最一個參數是一個ASCII值,指明具體和哪一個非系統鍵組合。
如QQ的熱鍵ctrl+alt+z,註冊這個熱鍵的語句是RegisterHotKey(NULL,0x0001,MOD_CONTROL|MOD_ALT,‘Z’)
如QQ的截圖熱鍵 RegisterHotKey(NULL,0x0001,MOD_CONTROL|MOD_ALT,'A')
例子:按下ctrl+alt+x熱鍵,彈出消息提示框,詢問是否要退出。
//#include "stdafx.h" 新建空工程,不須要該頭文件
#include<windows.h>
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
RegisterHotKey(NULL,0x0001,MOD_CONTROL|MOD_ALT,'X'); //註冊熱鍵
MSG msg;
while(GetMessage(&msg,NULL,0,0)) //從消息隊伍獲取消息
{
if(msg.message==WM_HOTKEY) //熱鍵消息
{
if(MessageBox(NULL,"你肯定要退出程序嗎?","熱鍵提示",MB_YESNO|MB_SYSTEMMODAL)==IDYES)
{
UnregisterHotKey(NULL,0x0001); //卸載熱鍵
break;
}
}
}
return 0;
}
記得要退出程序
第七十個StretchBlt在窗口輸出一個位圖
這個函數比BitBlt多了兩個參數,那就是源目標DC的寬高,像BitBlt函數,只有目標DC的寬高。
有了這兩個參數的加入,StretchBlt函數功能就比BitBlt函數強大了許多,它能夠縮小或放大圖片,能夠把一張圖片上的任意矩形區域覆蓋到另外一張圖片上的任意區域。
函數語句:StretchBlt(hdc,0,0,bmInfo.bmWidth,bmInfo.bmHeight,memDC,0,0,50,50,SRCCOPY);
具體用法參考BitBlt函數。
第七十一個TextOut根據設備DC在窗口輸出文字
函數定義:
BOOL TextOut(HDC hdc, // 設備DC
int nXStart, // 窗口x座標
int nYStart, // 窗口y座標,字符串左上角是將是x,y
LPCTSTR lpString, // 字符串
int cbString // 字符串中字符的個數
);
例子:在窗口輸出文字,爲了方便,這裏依舊在"無標題.txt - 記事本",窗口裏輸出文字
#include<windows.h>
#include<string.h>
int main()
{
char Text[52]="從座標點50,50開始輸出文字";
HWND wnd=FindWindow(NULL,"無標題.txt - 記事本");
HDC dc=GetDC(wnd);
SetTextColor(dc,RGB(255,0,0)); //設置文字顏色
while(1)
{
TextOut(dc,50,50,Text,strlen(Text));
Sleep(200);
}
return 0;
}
第七十二個DrawText根據設備DC在窗口的一個矩形區輸出文字。
int DrawTextW(
HDC hDC, //設備DC
LPCWSTR lpString, //字符串
int nCount, //字符串的個數
LPRECT lpRect, //指明一個矩形區
UINT uFormat); //輸出格式
uFormat的經常使用取值
值 說明
DT_BOTTOM 將正文調整到矩形底部。此值必須和DT_SINGLELINE組合。
DT_CENTER 使正文在矩形中水平居中。
DT_VCENTER 使正文在矩形中垂直居中。
DT_END_ELLIPSIS 對於顯示的文本,若是結束的字符串的範圍不在矩形內,它會被截斷並以省略號標識。
若是一個字母不是在字符串的末尾處超出了矩形範圍,它不會被截斷並以省略號標識。
字符串不會被修改,除非指定了DT_MODIFYSTRING標誌。
T_WORD_ELLIPSIS, DT_PATH_ELLIPSIS和DT_END_ELLIPSIS不能和此參數一塊兒使用
DT_LEFT 正文左對齊。
T_RIGHT 正文右對齊。
DT_RTLREADING 當選擇進設備環境的字體是希伯來文或阿拉伯文字體時,爲雙向正文安排從右到左的閱讀順序都是從左到右的。
DT_TOP 正文頂端對齊(僅對單行)。
DT_WORDBREAK 斷開字。當一行中的字符將會延伸到由lpRect指定的矩形的邊框時,此行自動地在字之間斷開。一個回車一換行也能使行折斷。
DT_WORD_ELLIPSIS 截短不符合矩形的正文,並增長省略號。
第七十三個GetLogicalDriveStrings獲取系統分區信息
函數定義:
DWORD GetLogicalDriveStrings(
DWORD nBufferLength, //指明lpBuffer參數大小
LPSTR lpBuffer //分區信息將會存儲在這個參數,格式是「分區NULL分區NULL分區NULL NULL」兩個NULL結尾
//假設當前電腦有C,D,E分區,那字符串的存儲格式是 "C:\\\0D:\\\0E:\\\0\0"; ('\\'轉義字符\)
);
例子:枚舉當前磁盤全部分區
#include<windows.h>
#include<stdio.h>
int main()
{
char *szBuffer=new char[52];
GetLogicalDriveStrings(52,szBuffer);
while(*szBuffer!='\0')
{
printf("%s\n",szBuffer);
szBuffer+=strlen(szBuffer)+1;
}
return 0;
}
第七十四個GetDiskFreeSpaceEx獲取一個分區(盤符)的信息(已用空間,總大小,可用空間)
這個函數必須用ULARGE_INTEGER聯合類型來存儲磁盤使用信息。由於要獲取磁盤的已用空間,總大小,可用空間,因此咱們必須定義三個ULARGE_INTEGER類型變量來存儲這三個信息。而具體信息就存儲在ULARGE_INTEGER類型的QuadPart成員變量(該成員佔八位字節)
如獲取C盤的信息:ULARGE_INTEGER dwAvailable,dwFree,dwTotal;
GetDiskFreeSpaceEx("c:\\",&dwAvailable,&dwTotal,&dwFree);//獲取分區信息
下面結合GetLogicalDriveStrings舉個例子:獲取當前磁盤全部分區信息並輸出
#include<windows.h>
#include<stdio.h>
int GetSpace(char *P);
int main()
{
char *szBuffer=new char[52];
GetLogicalDriveStrings(52,szBuffer);
while(*szBuffer!='\0')
{
printf("%s ",szBuffer);
GetSpace(szBuffer);
szBuffer+=strlen(szBuffer)+1;
}
return 0;
}
int GetSpace(char *Par)
{
ULARGE_INTEGER dwAvailable,dwFree,dwTotal;
double fDwa,fDwf,fDwt;
char szSize[128]={0};
int Size;
GetDiskFreeSpaceEx(Par,&dwAvailable,&dwTotal,&dwFree); //獲取分區信息
Size=dwTotal.QuadPart/1024/1024; //獲取總大小
fDwt=Size/1024.0;
Size=dwAvailable.QuadPart/1024/1024; //獲取已用空間
fDwa=Size/1024.0;
Size=dwFree.QuadPart/1024/1024; //獲取可用空間
fDwf=Size/1024.0;
sprintf(szSize,"總大小:%0.2fGB 已用空間:%0.2fGB 可用空間:%0.2fGB",fDwt,fDwa,fDwf);
printf("%s\n",szSize);
return 0;
}
第七十五個WritePrivateProfileString修改或建立一個INI文件裏的數據
INI文件的內容通常由節名,鍵名,鍵值組成,先來看一下INI文件的結構,打開一個INI文件,咱們可能會看到如下內容
////////////////////////////////////////////////////////////////////////////////////
[gmy_p]
exist_p=0
linux_p=
[boot]
a20=0
a21=0
///////////////////////////////////////////////////////////////////////////////////
上面的內容中[gmy_p]和[boot]即是INI文件的節名,節名包含鍵名,和鍵值。一個INI文件能夠有多個節名.
那麼哪些是鍵名,那些是鍵值呢,在「=」左邊的是鍵名,而在右邊的就是鍵值,鍵值能夠爲NULL。
好了,看一下WritePrivateProfileString的函數的定義:
BOOL WritePrivateProfileString(
LPCWSTR lpAppName, //節名
LPCWSTR lpKeyName, //鍵名
LPCWSTR lpString, //修改的數據
LPCWSTR lpFileName //INI文件名
);
若是要修改鍵值,那麼要提供哪些信息呢,首先,必需要知道INI文件的路徑(lpFileName),要修改的鍵值是在哪一個節名下(lpAppName),以及具體是哪一個鍵名(lpKeyName),還有修改的鍵值數據(lpString).
好比我要把以前INI文件裏節名爲gmy_p下的鍵名exist_p的鍵值改成100(假設這個文件的路徑爲d:\gho.ini).
那麼就是語句:WritePrivateProfileString("gmy_p","exist_p","100","d:\\gho.ini");
WritePrivateProfileString函數功能不止於此,當函數提供的INI文件名,節名,鍵名不存在時,那麼函數就會建立他們。這樣,咱們就能夠用這個函數建立一個INI文件,或在一個INI文件裏建立一個節名,或在一個節名下建立一個鍵名。
如:WritePrivateProfileString("ZhengYong","QQ","***980073","d:\\Info.ini");
第七十六個GetPrivateProfileString獲取一個INI文件裏的數據
函數定義:
DWORD GetPrivateProfileStringW(
LPCWSTR lpAppName, //節名
LPCWSTR lpKeyName, //鍵名
LPCWSTR lpDefault, //默認值,填0既可
LPWSTR lpReturnedString, //接收數據的緩存區(字符串)
DWORD nSize, //指明緩存區的大小
LPCWSTR lpFileName //INI文件名
);
例子獲取一個鍵值:假設D盤下有一個名爲Info.ini文件,它的內容以下:
////////////////////////////////////////////////////////////////////////////
[ZhengYong]
QQ=***980073
Age=100
Weight=65kg
[LiYang]
QQ=***990129
Age=22
Weight=55kg
///////////////////////////////////////////////////////////////////////////
若是我想要獲取節名爲"ZhengYong"下的鍵名QQ的鍵值,那麼就是:
#include<windows.h>
#include<stdio.h>
int main()
{
char KeyValue[252];
::GetPrivateProfileString("ZhengYong","QQ",0,KeyValue,252,"d:\\Info.ini");
printf("%s\n",KeyValue);
return 0;
}
同WritePrivateProfileString相似,若是提供的節名,或鍵名爲NULL,則獲取當前全部的節名或鍵名。跟分區信息存儲格式同樣,字符串裏,多個節名,或鍵名以'\0'間隔,字符串最終以兩個'\0'結束。
例子:枚舉ZhengYong節名下的全部鍵名:
#include<windows.h>
#include<stdio.h>
int main()
{
char Data[252];
::GetPrivateProfileString("ZhengYong",NULL,0,Data,252,"d:\\Info.ini");
char *pKey=Data;
while(*pKey!='\0')
{
printf("%s\n",pKey);
pKey+=strlen(pKey)+1;
}
return 0;
}
那麼枚舉節名只要在上面的例子中,把函數的節名參數設爲NULL就好了,如:
GetPrivateProfileString(NULL,NULL,0,Data,252,"d:\\Info.ini");
你們能夠用這個函數編一個讀取INI文件內容的程序,以便更好的掌握這個函數。記得把接收數據的緩存區設置大一點。
第七十七個GetSystemMetrics得到特定窗口的高寬度
該函數只有一個參數,經常使用取值以下:
SM_CXSCREEN 屏幕寬度
SM_CYSCREEN屏幕高度
SM_CXFULLSCREEN窗口客戶區寬度
SM_CYFULLSCREEN窗口客戶區高度
SM_CYMENU菜單欄高度
SM_CYCAPTION//標題欄高度
SM_CXBORDER窗口邊框寬度
SM_CYBORDER窗口邊框高度
例子:獲取屏幕分辨率(桌面寬高度)
#include<windows.h>
#include<stdio.h>
int main()
{
int ScreenX=::GetSystemMetrics(SM_CXSCREEN);
int ScreenY=::GetSystemMetrics(SM_CYSCREEN);
printf("屏幕分辨率:%dX%d\n",ScreenX,ScreenY);
return 0;
}
第七十八個SetWindowPos設置一個窗口的大小和它的Z序
窗口的Z序是什麼意思呢?用過MFC的人應該都清楚,在對話框編輯區按CTRL+D就會顯示出每一個控件的順序。以下圖:
設置控件的順序有什麼用呢,你們看到上面兩個控件有什麼特別的嗎?對了,兩個控件正好有一部分重疊,這時候問題就來了,重疊的部分顯示的是那個窗口呢,或者說是以什麼來肯定顯示哪一個窗口,我想你們也應該猜到了,是以控件的順序來肯定的。順序較大的會被顯示。這個程序運行以下圖如示:
明白窗口的Z序了,咱們就來看一下這個函數的參數及其意思。
函數定義:
BOOL SetWindowPos(
HWND hWnd, //要設置的窗口句柄
HWND hWndInsertAfter,
int X,
int Y, //X,Y指明窗口左上角的位置
int cx, //窗口寬度
int cy, //窗口高度
UINT uFlags);
第二個參數hWndInsertAfter的經常使用取值:
HWND_BOTTOM:
將窗口置於Z序的底部.
HWND_NOTOPMOST:若是窗口在Z序頂部,則取消頂部位置,若是不是,則該參數無效
HWND_TOP:將窗口置於Z序的頂部。
HWND_TOPMOST:將窗口置於Z序的頂部。窗口當前未被激活,也依然是頂部位置
最後一個參數uFlags能夠是Z序中hWnd的前一個窗口句柄的或如下經常使用取值:
SWP_HIDEWINDOW;隱藏窗口
SWP_SHOWWINDOW:顯示窗口
SWP_NOMOVE指明X,Y參數無效
SWP_NOSIZE指明CX ,CY參數無效
SWP_NOZORDER指明hWndInsertAfter參數無效
例子:設置一個窗口像PPS和任務欄那樣,總在最前顯示。
以"無標題.txt - 記事本"窗口爲例
#include<windows.h>
int main()
{
HWND wnd=::FindWindow(NULL,"無標題.txt - 記事本");
::SetWindowPos(wnd,HWND_TOPMOST,0,0,0,0,SWP_SHOWWINDOW|SWP_NOMOVE|SWP_NOSIZE);
return 0;
}
例子2:全屏一個窗口
依舊以"無標題.txt - 記事本"窗口爲例:
#include<windows.h>
int main()
{
int ScreenX=::GetSystemMetrics(SM_CXSCREEN);
int ScreenY=::GetSystemMetrics(SM_CYSCREEN);
HWND wnd=::FindWindow(NULL,"無標題.txt - 記事本");
::SetWindowPos(wnd,HWND_TOPMOST,0,0,ScreenX,ScreenY,SWP_SHOWWINDOW);
return 0;
}
第七十九個CreateFile建立一個文件,或打開一個文件用於讀寫,函數返回文件句柄
函數定義:
HANDLE CreateFile(
LPCSTR lpFileName, //文件名
DWORD dwDesiredAccess, //指明對文件進行何種操做,是要讀它(GENERIC_READ)仍是要寫入(GENERIC_WRITE)
DWORD dwShareMode, //指明文件能夠同時被多個程序讀寫嗎?FILE_SHARE_READ能夠同時讀,FILE_SHARE_WRITED能夠同時寫
LPSECURITY_ATTRIBUTES lpSecurityAttributes, //指向一個SECURITY_ATTRIBUTES結構的指針,通常爲NULL
DWORD dwCreationDisposition, //安全屬性,指明以何種方式打開或建立文件
DWORD dwFlagsAndAttributes, //指明文件的屬性,隱藏?只讀?系統文件?爲NULL表示默認屬性
HANDLE hTemplateFile //若是不爲零,則指定一個文件句柄。新文件將從這個文件中複製擴展屬性
);
第五個參數dwCreationDisposition的經常使用取值及意思
TRUNCATE_EXISTING 將現有文件縮短爲零長度,清空文件的內容,文件必須已經存在
CREATE_ALWAYS 建立一個文件,若是文件已經存在,則覆蓋它
CREATE_NEW 建立文件,若是文件已經存在,則函數執行失敗
OPEN_ALWAYS打開文件,若是文件不存在,則建立它
OPEN_EXISTING 打開文件,文件必須存在。
第六個參數dwFlagsAndAttributes的經常使用取值及其意思
FILE_ATTRIBUTE_NORMAL 默認屬性
FILE_ATTRIBUTE_HIDDEN 隱藏
FILE_ATTRIBUTE_READONLY 只讀
FILE_ATTRIBUTE_SYSTEM 系統文件
第八十個ReadFile根據文件句柄,從文件中讀取一段數據
函數定義:
BOOL WINAPI ReadFile(
HANDLE hFile, //文件句柄
LPVOID lpBuffer, //接收文件數據的緩存區
DWORD nNumberOfBytesToRead, //指明讀取多少數據(字節)
LPDWORD lpNumberOfBytesRead, //實際讀取數據
LPOVERLAPPED lpOverlapped //通常爲NULL,如文件打開時指定了FILE_FLAG_OVERLAPPED,該參纔有具體取值。
);
例子:讀取txt文件的內容,假設E盤下有一個名a.txt的文件,文件內容爲123456789
#include<windows.h>
#include<stdio.h>
int main()
{
char Text[25]={0};
DWORD dwSize;
HANDLE FileHandle=CreateFile("e:\\a.txt",GENERIC_READ,0,NULL,OPEN_EXISTING
,FILE_ATTRIBUTE_NORMAL ,NULL); //獲取文件句柄
ReadFile(FileHandle,Text,15,&dwSize,NULL); //從文件中讀取15個字節
printf("內容:%s 實際讀入字節:%d\n",Text,dwSize);
return 0;
}
第八十一個WriteFile根據文件句柄,寫入一段數據到文件中
函數定義:
BOOL WriteFile(
HANDLE hFile, //文件句柄
LPCVOID lpBuffer, //該緩存區的數據將要寫入到文件裏
DWORD nNumberOfBytesToWrite, //指明寫入多少數據
LPDWORD lpNumberOfBytesWritten, //實際寫入數據
LPOVERLAPPED lpOverlapped //通常爲NULL
);
例子:在E盤建立一個名爲aa.txt的文件,並向其寫入數據
#include<windows.h>
#include<stdio.h>
int main()
{
char Text[25]="123456789";
DWORD dwSize;
HANDLE FileHandle=CreateFile("e:\\aa.txt",GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
::WriteFile(FileHandle,Text,9,&dwSize,0);
return 0;
}
第八十二個SetFilePointer移動一個文件指針的位置
移動一個文件指針的位置有什麼用呢,做用是讀取一個文件裏指定位置的數據,好比我只要讀取文件中第四個字節到第七個字節這一段的數據,用SetFilePointer函數就能夠完成。
函數定義:
DWORD SetFilePointer(
HANDLE hFile, //文件句柄
LONG lDistanceToMove, //移動字節,負數表示反向移動
PLONG lpDistanceToMoveHigh, //爲了支持超大文件而存在,通常爲NULL
DWORD dwMoveMethod //從哪裏開始移動,FILE_BEGIN 從文件開始處開始移動,FILE_CURRENT當前位置,FILE_END文件末尾
);
例子:假設E盤下有一個名爲a.txt的文件,內容爲"123456789",讀取該文件第四個字節到第七個字節的數據
#include<stdio.h>
int main()
{
char Text[25]={0};
DWORD dwSize;
HANDLE FileHandle=CreateFile("e:\\a.txt",GENERIC_READ,0,NULL,OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
SetFilePointer(FileHandle,3,NULL,FILE_BEGIN);
ReadFile(FileHandle,Text,4,&dwSize,NULL);
printf("%s\n",Text);
return 0;
例子2:從文件中第四個字節開始寫入數據,被新數據所佔位置的數據會被覆蓋掉,依舊以上面a.txt文件爲例子
#include<windows.h>
#include<stdio.h>
int main()
{
char Text[25]={"abcd"};
DWORD dwSize;
HANDLE FileHandle=CreateFile("e:\\a.txt",GENERIC_WRITE,0,NULL,OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
SetFilePointer(FileHandle,3,NULL,FILE_BEGIN);
WriteFile(FileHandle,Text,4,&dwSize,NULL);
return 0;
}
則寫入後a.txt文件裏的數據爲123abcd89
若是要在文件的末尾添加數據,就用這個語句:SetFilePointer(FileHandle,0,NULL,FILE_END);
第八十三個GetFileSize獲取一個文件的大小
函數定義:
DWORD GetFileSize(
HANDLE hFile, //文件句柄
LPDWORD lpFileSizeHigh //通常爲NULL
);
如獲取a.txt文件的大小:
#include<windows.h>
#include<stdio.h>
int main()
{
DWORD FileSize;
HANDLE FileHandle=CreateFile("e:\\a.txt",GENERIC_WRITE,0,NULL,OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,NULL);
FileSize=GetFileSize(FileHandle,NULL);
printf("%d字節\n",FileSize);
return 0;
}
第八十四個SetTextColor根據窗口輸出文本顏色
第一個參數是設備DC,第二個參數是一個COLORREF類型的顏色值,可用RGB進行轉換。
第八十五個SetBkColor設置背景顏色
參數跟SetTextColor函數同樣,第一個DC,第二個COLORREF
第八十六個GetWindowDC獲取整個窗口設備上下文DC
像GetDC獲取的只是客戶區DC,不能對窗口標題欄,狀態欄等進行操做。該函數用法跟GetDC同樣,僅區域不同。
例子:在一個窗口的標題欄輸出文字
#include<windows.h>
int main()
{
HWND wnd=FindWindow(NULL,"無標題.txt - 記事本");
HDC dc=GetWindowDC(wnd);
SetTextColor(dc,RGB(255,0,0)); //文本顏色設置爲紅色
::SetBkColor(dc,RGB(0,0,255)); //文本背景顏色設置爲藍色
while(1)
{
TextOut(dc,0,0,"123456",6);
Sleep(200);
}
return 0;
}
第八十七個GetDesktopWindow獲取桌面窗口句柄
該函數沒有參數,調用返回桌面窗口句柄
例子:
#include<windows.h>
int main()
{
HWND DeskWnd=GetDesktopWindow(); //獲取桌面窗口句柄
HDC DeskDC=GetWindowDC(DeskWnd); //獲取DC
HBRUSH brush=::CreateSolidBrush(RGB(255,0,0)); //紅色畫刷
SelectObject(DeskDC,brush); //選入畫刷
while(1)
{
::Rectangle(DeskDC,50,50,200,200);
Sleep(200);
}
return 0;
}
第八十八個CreateCompatibleBitmap根據DC創造一個兼容的位圖
該函數需與CreateCompatibleDC函數配合使用
函數第一個參數是窗口DC,第二,三參數指明窗口寬高,函數返回位圖句柄(HBITMAP)
建立一個兼容的位圖是什麼意思呢?就比如給HBITMAP分配內存以及指定這位圖相關的一些信息(跟DC相關的信息),如位圖的寬高,數據大小,但此時數據沒有具體取值。就比如一個字符串,我已經知道字符串大小了,但殊不知道字符串具體是什麼:
如:
char *p;
p=new char[15]; //知道字符串大小爲15
但此時p所指向的緩存區,沒有具體取值。
而用CreateCompatibleBitmap函數,建立的位圖,只是一個空殼子。數據沒有賦值,那要怎樣給數據賦值呢?
首先得把這個位圖句柄選入一個DC(該DC必須爲CreateCompatibleDC函數建立的)裏,而後再用BitBlt函數具體給數據賦值就好了。
例子:實時獲取屏幕圖像
爲了方便,在記事本窗口輸出圖像,本身就不建立窗口了(打開"無標題.txt - 記事本")
#include<windows.h>
int main()
{
HWND TextWnd=FindWindow(NULL,"無標題.txt - 記事本");
HDC TextDC=GetDC(TextWnd);
HWND DeskWnd=::GetDesktopWindow();
RECT DeskRC;
::GetClientRect(DeskWnd,&DeskRC);
HDC DeskDC=GetDC(DeskWnd);
HBITMAP DeskBmp=::CreateCompatibleBitmap(DeskDC,DeskRC.right,DeskRC.bottom);
HDC memDC;
memDC=::CreateCompatibleDC(DeskDC);
SelectObject(memDC,DeskBmp);
while(1)
{
StretchBlt(memDC,0,0,DeskRC.right,DeskRC.bottom,DeskDC,0,0,DeskRC.right,DeskRC.bottom,SRCCOPY);
RECT TextRC;
GetClientRect(TextWnd,&TextRC);
::StretchBlt(TextDC,0,0,TextRC.right,TextRC.bottom,memDC,0,0,DeskRC.right,
DeskRC.bottom,SRCCOPY);
Sleep(300);
}
return 0;
}
第八十九個GetDIBits從一個兼容位圖裏獲取位圖數據
先來分析一下位圖文件信息結構,一個位圖由如下四部分組成:
位圖文件頭(BITMAPFILEHEADER)//佔14字節
位圖信息頭(BITMAPINFOHEADER)//佔40字節
調色板(LOGPALLETE)//若是真彩位圖,那該部分沒有,直接是位圖數據
實際位圖數據
而GetDIBits函數獲取的就是實際位圖數據這一部分了。
接來看一下BITMAPFILEHEADER這個結構以及它成員的意思和取值
typedef struct tagBITMAPFILEHEADER {
WORD bfType; //表示文件類型,值必須爲0x4d42
DWORD bfSize; //文件的大小
WORD bfReserved1; //保留,必須爲0
WORD bfReserved2; //保留,必須爲0
DWORD bfOffBits; //位圖前三部分所佔的字節,真彩色位圖爲54
} BITMAPFILEHEADER;
上面的成員,只有bfSize的取值不肯定,其它都同樣,也就是說,每一個真彩位圖,這幾個成員取值都是同樣的.下面的例子能夠說明。
讀取一個真彩位圖的文件信息頭。
#include<windows.h>
#include<stdio.h>
int main()
{
BITMAPFILEHEADER bfh;
HANDLE hFile=CreateFile("e:\\aa.bmp",GENERIC_READ,0,NULL,OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,0);
DWORD dwSize;
ReadFile(hFile,(void *)&bfh,sizeof(BITMAPFILEHEADER),&dwSize,0);
printf("bfType:%x\n",bfh.bfType);
printf("bfSize:%d\n",bfh.bfSize);
printf("bfReserved1:%d\n",bfh.bfReserved1);
printf("bfReserved2:%d\n",bfh.bfReserved2);
printf("bfOffbits:%d\n",bfh.bfOffBits);
return 0;
}
再來看一下BITMAPINFOHEADER這個結構以及它成員的意思和取值
typedef struct tagBITMAPINFOHEADER{
DWORD biSize; //本結構大小,爲40
LONG biWidth; //位圖的寬度,以像素爲單位
LONG biHeight; //位圖的高度,以像素爲單位
WORD biPlanes; //目標設備的級別,必須是1
WORD biBitCount; //每一個像素所佔的位數,24表示真彩位圖
DWORD biCompression; //位圖壓縮類型,通常爲BI_RGB(未經壓縮)
DWORD biSizeImage; //實際位圖數據這部分的所佔用的字節數
LONG biXPelsPerMeter; //指定目標設備水平分辨率,單位像素/米,爲0
LONG biYPelsPerMeter; //指定目標垂直分辨率真,單位像素/米,爲0
DWORD biClrUsed; //指定目標設備實際用到的顏色數,若是該值爲0,則用到的顏色數爲2的biBitCount方
DWORD biClrImportant; //表示圖像中重要的顏色數,若是爲0,則全部顏色都是重要的。
} BITMAPINFOHEADER;
調色板(LOGPALLETE)因爲大部分都是針對真彩位圖操做,此部分略過
GetDIBits函數定義:
int GetDIBits(
HDC hdc, //位圖兼容的DC
HBITMAP hbmp, //位圖句柄
UINT uStartScan, //從哪行開始掃描
UINT cScanLines, //掃描多少行數據
LPVOID lpvBits, //接收數據的緩存區
LPBITMAPINFO lpbi, //真彩位圖,此處填文件信息頭就好了
UINT uUsage //真彩位圖填DIB_RGB_COLORS,表示由R,G,B三色直接構成
);
例子:截屏,並把屏幕圖片保存成位圖
#include<windows.h>
void ScreenSnap(HBITMAP hBitmap,char *bmpPath,HDC dc);
int main()
{
HWND DeskWnd=::GetDesktopWindow(); //獲取桌面窗口句柄
RECT DeskRC;
::GetClientRect(DeskWnd,&DeskRC); //獲取窗口大小
HDC DeskDC=GetDC(DeskWnd); //獲取窗口DC
HBITMAP DeskBmp=::CreateCompatibleBitmap(DeskDC,DeskRC.right,DeskRC.bottom); //兼容位圖
HDC memDC=::CreateCompatibleDC(DeskDC); //兼容DC
SelectObject(memDC,DeskBmp); //把兼容位圖選入兼容DC中
BitBlt(memDC,0,0,DeskRC.right,DeskRC.bottom,DeskDC,0,0,SRCCOPY); //拷貝DC
ScreenSnap(DeskBmp,"d:\\Screen.bmp",DeskDC);
return 0;
}
void ScreenSnap(HBITMAP hBitmap,char *bmpPath,HDC dc)
{
BITMAP bmInfo;
DWORD bmDataSize;
char *bmData; //位圖數據
GetObject(hBitmap,sizeof(BITMAP),&bmInfo); //根據位圖句柄,獲取位圖信息
bmDataSize=bmInfo.bmWidthBytes*bmInfo.bmHeight; //計算位圖數據大小
bmData=new char[bmDataSize]; //分配數據
BITMAPFILEHEADER bfh; //位圖文件頭
bfh.bfType=0x4d42;
bfh.bfSize=bmDataSize+54;
bfh.bfReserved1=0;
bfh.bfReserved2=0;
bfh.bfOffBits=54;
BITMAPINFOHEADER bih; //位圖信息頭
bih.biSize=40;
bih.biWidth=bmInfo.bmWidth;
bih.biHeight=bmInfo.bmHeight;
bih.biPlanes=1;
bih.biBitCount=24;
bih.biCompression=BI_RGB;
bih.biSizeImage=bmDataSize;
bih.biXPelsPerMeter=0;
bih.biYPelsPerMeter=0;
bih.biClrUsed=0;
bih.biClrImportant=0;
::GetDIBits(dc,hBitmap,0,bmInfo.bmHeight,bmData,(BITMAPINFO *)&bih,DIB_RGB_COLORS);//獲取位圖數據部分
HANDLE hFile=CreateFile(bmpPath,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,0); //建立文件
DWORD dwSize;
WriteFile(hFile,(void *)&bfh,sizeof(BITMAPFILEHEADER),&dwSize,0); //寫入位圖文件頭
WriteFile(hFile,(void *)&bih,sizeof(BITMAPINFOHEADER),&dwSize,0); //寫入位圖信息頭
WriteFile(hFile,(void *)bmData,bmDataSize,&dwSize,0); //寫入位圖數據
::CloseHandle(hFile); //關閉文件句柄
}
原文地址: http://www.cnblogs.com/yiciyun/archive/2012/02/24/2365984.html
此文章源自於【https://www.cnblogs.com/LyShark/p/9094348.html】