前幾日,看到雜志上有一篇關於開發QQ聊天機器人的文章。談到了對QQ循環發送消息內容,感覺倒也很好玩,於是拿起Delphi開始了我的QQ聊天機器人之路。
首先要明白自己要做什麼,大家都用過QQ,知道給別人發送消息的整個過程吧!要實現循環發送消息的功能該有以下幾個條件:
1.必須是在聊天模式裡進行。這樣發送完一條消息後,QQ窗體還存在。
2.其次是要找到QQ文本窗體的句柄。
3.向QQ文本窗體中貼上你想說的話。然後自己點擊發送按鈕。
思路很簡單,接著我們就要開始實施了。
首先要找到QQ文本窗體的句柄。這時我用到了SPY來查看QQ的窗體。結果如下圖:這樣思路就出來了。要找到QQ文本窗體的句柄就得先找到它的父類即:標志為00620252 Class Name:AfxWnd42 Control ID:00000000。而要找到它就必須找到QQ消息對話框即:006B294“冷問梅 - 發送消息”#32770〔Dialog〕的句柄。
這時要用到幾個Api函數:
1.FindWindowEx(
hWnd1:Long, //在其中查找子的父窗口,如設為0,表示使用桌面窗口(通常說的頂級窗口都認為是桌面的子窗口)
hWnd2:Long, //從這個窗口後開始查找。如設為0,表示對第一個子窗口開始搜索。
Lpsz1:String, //欲搜索的類名,0表示忽略。
Lpsz2:String //欲搜索的類名,0表示忽略。
);
2.GetWindow(
hWnd:Long, //源窗口。
wCmd:Long //指定結果窗口與源窗口的關系(這裡用GW_CHILD)表示尋找源窗口的第一個子窗口。
);
3. GetDlgItem(
hWnd:Long, //源窗口的句柄。
Int: nIDDlgItem //要在其中查找的窗口的ID號
);
其實剛開始找QQ對話窗口時,我先想到的是FindWindow(),這個函數可以直接通過窗口標題名來查找窗體句柄。
我是這樣找的:
var hparent:HWND;
hparent:=FindWindow(nil, '發送信息'); //這對2003以前的版本還是很有效的^_^
結果卻是返回錯誤。Why?
後來仔細一看,發現每次QQ2003的標題都在變:如上圖是:冷問梅 - 發送消息,要是你又對一個人發信息就會變成:藍色的風 – 發送消息(舉個例子)。
這也許是QQ2003采取的一種安全措施吧!呵呵!你們也許會看見網上針對QQ2003發送消息炸彈的工具有時要是輸入對方的昵稱的原因。(便於通過昵稱得到窗體句柄)。
不過有沒有更好的方法呢!有!這時就要用到FindWindowEx()了。仔細看一下它的參數,關鍵是第二個hWnd2——我們可以通過它來多次調用FindWindowEx找到符合條件的子窗口。以下是我的代碼:
var hparent:HWND; //定義為全局變量。來記錄每次調用FindWindowEx()後找到的窗體的句柄。
procedure TForm1.FormCreate(Sender: TObject);
begin
hparent:=0; //初始化,查找桌面所有的頂級窗口開始。
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var hbutton,hbutton1:HWND;
begin
repeat
hparent:=findwindowex(0,hparent,'#32770',nil); //QQ對話框的類為#32770,這樣循環調用FindWindowEx()就能每次時鐘生效時更新hparent的值。從已查找的句柄為hparent的窗體後查找符合要求的窗體。
hbutton:=findwindowEX(hparent,0,nil,'發送(&S)'); //每次判斷找到的窗口的句柄,看這個窗體中是否存在'發送(&S)'按鈕。存在即找到了正確的QQ對話框。
until hbutton<>0; //找到QQ對話框後跳出循環。
hbutton1:=findwindowex(hparent,0,nil,'聊天模式(&T)') ; //找到QQ對話窗體後,查找聊天模式按鈕句柄。
if hbutton1<>0 then //若此時存在聊天模式按鈕即現在QQ窗體處於消息模式狀態。
sendmessage(hbutton1,BM_CLICK,0,0); //給聊天模式按鈕發送單擊消息。將窗體轉換為聊天模式。
end;
這樣我們就成功的找到的QQ對話框。和成功設置對話框為聊天模式了。任務總算先完成了一些,呵呵!更郁悶我的還在後頭了。
接著,就要開始查找QQ輸入文本的那個窗體的句柄了。這時我用到的是GetDlgItem()大家知道一個窗體中某一類控件的Control ID在這個窗體類中是不變的(除去一些靜態文體外)過SPY我獲知QQ輸入文本的那個窗體的Control ID為0000037E。於是我寫了下面的語句。
Var hmemo:HWND;
hmemo:=GetDlgItem(hparent,$0000037E);
結果發現寫得東東完全沒有自己預想的效果。呵呵!還是拿起SPY,哈!發現竟然不止一個控件的ID為0000037E。並且我們想要得到QQ輸入文本的那個窗體的位置也不是最前一個(要是最前一個,通過上面的語句也是可以找到的^_^)。郁悶了不是。沒辦法還是從它的父類開始吧!可不能在一步求成了。仔細看看上圖。發現了吧!
標志為00620252 Class Name:AfxWnd42 Control ID:00000000的即是QQ輸入文本的那個窗體的父類,並且它是所有Class Name:AfxWnd42中位置最前的那一個。於是我們就可以找到它的句柄。跑不掉了。有了它,QQ輸入文本的那個窗體的句柄也就很容易找到了,哈哈!以下是我的代碼:
Var hmemo,hmemo1:HWND;
hmemo=GetDlgItem(hparent,$00000000); //找到父類。
hmemo1=GetWindow(hmemo1,GW_CHILD); //得到父類下的第一個子窗口句柄(hmemo1即QQ輸入文本的那個窗體的句柄^_^大功告成)
這裡順便說一下GetWindow()用法:
GetWindow(
Hwnd:Long, //源窗口句柄。
wCnd:Long //指定結果窗口與源窗口的關系。(GW_CHILD為得到源窗體下的第一個子窗口句柄)
)
更多的常數關系你們就去查看MSDN吧!這裡就不必占用寒泉的空間了。哈!
到此,QQ對話框和QQ輸入文本窗口的句柄我們都已經得到了,以下的步驟就是把你要寫的話,貼進QQ輸入文本窗口,點一下發送,就郁悶別人吧!
現在貼出我的一段代碼以供大家參考:
procedure TForm1.FormCreate(Sender: TObject);
begin
i:=0;
//導入文件內容到combobox控件。
combobox1.Items.LoadFromFile(extractfilepath(application.ExeName)+'text.txt');
combobox1.Text:=combobox1.Items.Strings[0];
end;
procedure TForm1.Timer1Timer(Sender: TObject);
var hmemo1:HWND; //hmemo1為找到的QQ文本輸入框句柄
begin
if checkbox1.Checked then //點擊了循環發送復選框。
begin
if i>combobox1.Items.Count-1 then
i:=0;
edit1.Text:=combobox1.Items.Strings[i];
edit1.SelectAll;
edit1.CopyToClipboard; //拷貝到剪切板
sendmessage(hmemo1,WM_PASTE,0,0); //對QQ輸入文本窗體發送粘貼消息。
sendmessage(hbutton,BM_CLICK,0,0); //點擊發送按鈕
i:=i+1;
end;
if checkbox1.Checked=false then //沒有點擊了循環發送復選框。
begin
edit1.Text:=combobox1.Text;
edit1.SelectAll;
edit1.CopyToClipboard;
sendmessage(hmemo1,WM_PASTE,0,0);
sendmessage(hbutton,BM_CLICK,0,0);
end;
end;
附上簡要說明:由於本人所知有限,不太會用剪切板函數對將已知字串拷貝到剪切板的方法還不知道。所以只能借道於控件上。因為所有文本類控件都有一個方法即——edit1.CopyToClipboard,所以只能先將Edit1變為不可見控件。每次先將要發送的內容傳給Edit1,而後在將Edit1的內容CopyToClipboard。哈!這只是一個取巧的法子,大家要是知道有什麼更好的方法還望告知在下,呵!
後記:
以上代碼是針對QQ2003版本。雖然網上有如:飄葉千夫指的好工具。不過作為一個小小菜鳥。但又喜歡編程的人來說。自己DIY(do it youtself)一個也是很爽的一件事吧!我自己也參照飄葉千夫指做了一個,感覺具備了它的使用功能吧!還不錯。其實也沒有太多技術性的東西,只是運用了幾個API函數而已。只希望對剛剛學Delphi的朋友有所幫助,當然高手是用不著的了。
注明:QQ2004有些改動.為了方便我把新改的代碼帖上來:
procedure TForm1.Timer1Timer(Sender: TObject);
var hbutton,hbutton1,hmemo,hmemo1,hparent1:HWND;
begin
repeat
hparent:=findwindowex(0,hparent,'#32770',nil);
//QQ2004就是多了下面這句,Memo上又多了一層窗口
hparent1:=findwindowex(hparent,0,'#32770',nil);
until hparent1<>0;
hbutton:=findwindowEX(hparent1,0,nil,'發送(&S)');
hbutton1:=findwindowex(hparent1,0,nil,'聊天模式(&T)');
if hbutton1<>0 then
sendmessage(hbutton1,BM_CLICK,0,0);
hmemo1:=GetDlgItem(hparent1,$00000000);
hmemo:=getwindow(hmemo1,GW_CHILD);
if hmemo<>0 then
begin
if checkbox1.Checked then
begin
if i>combobox1.Items.Count-1 then
i:=0;
edit1.Text:=combobox1.Items.Strings[i];
edit1.SelectAll;
edit1.CopyToClipboard;
sendmessage(hmemo,WM_SETTEXT,0,0);
sendmessage(hmemo,WM_PASTE,0,0);
sendmessage(hbutton,BM_CLICK,0,0);
i:=i+1;
end;
if checkbox1.Checked=false then
begin
edit1.Text:=combobox1.Text;
edit1.SelectAll;
edit1.CopyToClipboard;
sendmessage(hmemo,WM_SETTEXT,0,0);
sendmessage(hmemo,WM_PASTE,0,0);
sendmessage(hbutton,BM_CLICK,0,0);
end;
end;
end;
有需要的朋友請到我的網站去下載(位於我的作品裡):
作者網站:http://ASP.itdrp.com/hottey