初識API函數


    我之前是一個只會編寫數值計算的程序的OIer,但我並不甘於這種現狀,於是編寫了我的第一個使用API函數的C++程序,開發平台是VS2012:

// ConsoleApplication.cpp : 定義控制台應用程序的入口點。 //  #include<stdio.h> #include"stdafx.h" #include<Windows.h>

 

int _tmain(int argc, _TCHAR* argv[]) { HWND wnd; wnd=FindWindowA(NULL,"無標題 - 記事本"); SendMessage(wnd,WM_CLOSE,0,0); system("pause"); return 0; }
View Code

    之前有在看谷夕寫的HACK編程實例精講,第一節有介紹API函數,他的書上用的是hDesk=OpenDesktop(lpszDesktop,0,FALSE,DESKTOP_ENUMERATE);在這之前我連如何調用API函數都不知道,嘗試過#include<stdafx.h>等等亂七八糟的東西,結果都不行.后來百度了一下,才發現原來是要加#include<windows.h>

    明白如何才能可以調用API函數之后,嘗試使用書上介紹的OpenDesktop函數,因為我什么都不知道,於是程序我就這么寫了:

// ConsoleApplication.cpp : 定義控制台應用程序的入口點。 //  #include<stdio.h> #include"stdafx.h" #include<Windows.h>

 

int _tmain(int argc, _TCHAR* argv[]) { int hDesk=OpenDesktop(lpszDesktop,0,FALSE,DESKTOP_ENUMERATE); system("pause"); return 0; }
View Code

  結果是關於lpszDesktop發生了某種我理解不了的錯誤.於是再次百度API函數應用實例,就找到了第一個程序.

一開始仍然編譯不過,編譯器提示錯誤.

我也不知道LPCWSTR是什么,不過看網上關於FindWindow函數的介紹是這么說的:函數的定義:HWND WINAPI FindWindow(LPCSTR lpClassName ,LPCSTR lpWindowName);這上邊居然是LPCSTR,於是我試了試類似的函數,發現FindWindowA函數上是LPCWSTR,於是改成FindWindowA終於編譯過了,實現的功能就是把名為無標題 - 記事本的窗口關閉.

我學習這類東西一向喜歡先弄出來一個能正確運行的樣例,然后再弄明白有問題的地方,這次也不例外.關於這個程序我主要有這幾個方面不太明白:1.句柄是個啥玩意;2.LPCSTR和LPCWSTR又是什么東西;3.FindWindow函數還有啥其他調用方式.

先搞明白句柄是什么東西,當然還要靠百度.

句柄,是整個windows編程的基礎.一個句柄是指使用的一個唯一的整數值,即一個四字節長的數值,來標志應用程序中的不同對象和同類對象中的不同的實例,諸如,一個窗口,按鈕,圖標,滾動條,輸出設備,控件或者文件等.應用程序能夠通過句柄訪問相應的對象的信息,但是句柄不是一個指針,程序不能利用句柄來直接閱讀文件中的信息.如果句柄不用在I/O文件中,它是毫無用處的.句柄是windows用來標志應用程序中建立的或是使用的唯一整數,windows使用了大量的句柄來標志很多對象.

關於句柄我是這樣理解的:它有點類似於算法中的哈希值,對於一個對象系統分配給它一個唯一的值來標記存儲該對象變動情況的位置.

LPCWSTR是一個指向unicode編碼字符串的32位指針,所指向字符串是wchar型,而不是char型.

原來LPCWSTR是unicode字符串指針,那unicode又是什么東西呢(請原諒我的無知,我真的連unicode是啥都不知道)?看過這篇文章之后才大概有了個了解.比較特別之處在於定義unicode字符串時要在左邊加一個L,就像這樣:

 

wchar_t wStr[] = L"中文";

 

除此之外,我又了解到在VS2005以后,編碼方式默認為unicode,部分函數在使用時默認調用unicode方式(函數名加W),而非ansi方式(函數名加A).之前那個程序由於"無標題 - 記事本"是編碼為ansi的字符串,所以調用默認的unicode方式時出現錯誤,而使用FindWindowA之后采用ansi方式才編譯通過.然后我又把剛才的調用方式改為wnd=FindWindow(NULL,L"無標題 - 記事本");終於編譯通過.wchar_t的輸出:一般說來有兩種方式,一個是wcout,還有是wprintf()之類的函數,悲劇的是無論哪種方法都沒編譯過.后來看一個博客里說要在main函數里加上_tsetlocale(LC_ALL, L"CHS");於是wcout能用了,wprintf仍然不行.先不管它,繼續往后看.

第二個API函數:SendMessage函數.SendMessage函數能向指定句柄所對應的對象發送信息,示例程序:

POINT curpos; while (1) { GetCursorPos(&curpos); HWND hWnd=WindowFromPoint(curpos); SendMessage(hWnd,WM_CHAR,WPARAM('g'),0); Sleep(300); }
View Code

該程序實現的功能是向鼠標所指向的編輯窗口輸入g.

POINT就是表示坐標的數據類型.GetCursorPos(&curpos)表示取當前鼠標的坐標.WindowFromPoint(curpos)表示獲得curpos處的對象的句柄.

WM_CHAR表示輸出字符.

接着,我想寫一個程序給自己發送標准輸入數據:

while (1) { GetCursorPos(&curpos); HWND hWnd=WindowFromPoint(curpos); SendMessage(hWnd,WM_CHAR,WPARAM('g'),0); SendMessage(hWnd,WM_CHAR,WPARAM('\r'),0); Sleep(300); char ch; cerr<<"waiting for reading"<<endl; cin>>ch; if (ch=='g') break; }
View Code

  意思就是當通過SendMessage函數向程序窗口發送一個g,如果被成功讀入就跳出循環,一開始我第2個SendMessage里發送的是\n,結果沒法觸發讀入,接着改成\0,還是不行,又改成\r,這次終於成功了,原來回車鍵輸入的是一個\r啊,之前一直以為是\n呢...

再往下又看到一個更有趣的:模擬鼠標動作

void RightClick(HWND wnd) { SendMessage(wnd,WM_RBUTTONDOWN,0,0); Sleep(100); SendMessage(wnd,WM_RBUTTONUP,0,0); }
View Code

其中,WM_RBUTTONDOWN表示鼠標右鍵按下,WM_RBUTTONUP表示鼠標右鍵抬起.測試了一下運行沒什么大問題,不過看起開必須等我再單擊一下鼠標左鍵才能讓程序繼續執行.這樣感覺用起來很不舒服,而且也沒有什么應用價值.后來有看到專門有一個函數mouse_event能模擬鼠標的點擊.關於這個函數的詳細信息我看不太懂,就只用了一個最簡單的應用:鼠標左擊.再結合剛才的SendMessage函數,做了一個刷屏軟件,雖然功能很弱,但做完的時候還是感覺很激動.弱弱地貼一下我的代碼:

// 刷屏軟件.cpp : 定義控制台應用程序的入口點。 // #include<math.h> #include<stdio.h> #include<stdlib.h> #include<string.h> #include"stdafx.h" #include<Windows.h> #include<iostream> #include<cstdio> #include<cstdlib> #include<cmath> #include<locale> #include<cstring>
using namespace std; void Home_Page() { printf("1.使用說明.\n"); printf("2.開始運行.\n"); printf("3.退出程序.\n"); } void Left_Click() { mouse_event(MOUSEEVENTF_LEFTDOWN,0,0,0,0); mouse_event(MOUSEEVENTF_LEFTUP,0,0,0,0); } void Software_Description() { printf("本軟件使用很簡單,只要按提示一步一步操作即可.\n"); printf("也許有些使用者覺得軟件寫的太腦殘,請原諒,我剛接觸這方面,這個程序純屬練手.\n"); printf("特別提示:由於作者水平問題,請您在所有信息發送完成之前不要移動您的鼠標,否則可能會產生難以預料的結果.\n"); printf("同樣由於作者的水平問題,目前只支持英文文本(Ansi字符集)的發送.\n"); printf("如果發送內容中包含多個單詞,建議用下划線\'_\'連接\n"); printf("聯系作者:winifred.oier@gmail.com\n"); system("pause"); } void Main_Program() { printf("請輸入您希望發送的內容(一行):\n"); string TexttobeSend; cin>>TexttobeSend; printf("請輸入您希望每隔多長時間發送一次(單位:毫秒):\n"); int DelayTime; cin>>DelayTime; printf("您希望將該信息發送多少遍:\n"); int SendTime; cin>>SendTime; printf("請在5秒內將鼠標放在發送信息的編輯框里\n"); HWND InputBox; POINT curpos; Sleep(5000); GetCursorPos(&curpos); InputBox=WindowFromPoint(curpos); cerr<<"The Input Box has been engaged"<<endl; printf("請在5秒內將鼠標放在發送按鈕上.再次提示:由於作者水平太爛,執行完之前千萬別亂動鼠標.\n"); Sleep(5000); for (int i=1;i<=SendTime;i++) { for (int j=0;j<TexttobeSend.length();j++) SendMessage(InputBox,WM_CHAR,WPARAM(TexttobeSend[j]),0); Left_Click(); Sleep(DelayTime); } } int _tmain(int argc, _TCHAR* argv[]) { int Command_Code; while (true) { Home_Page(); cin>>Command_Code; switch(Command_Code) { case 1: Software_Description(); break; case 2: Main_Program(); break; case 3: return 0; } system("cls"); } return 0; }
View Code

不過在寫代碼的時候發現一個問題,不同的頭文件包含順序可能會影響能否編譯,還有就是編譯出來的可執行文件只能在我的電腦上執行,我用同學的電腦測試發現缺少一個MS...的動態鏈接庫,不明白為什么.


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM