在Delphi中,通过使用Skype API,我可以非常轻松地向联系人发送消息。但是,我正在尝试做的是在当前关注的联系人的聊天框中输入消息,而不发送消息。
通过使用Winspector,我发现Chatbox的Classname是TChatRichEdit,它放在TChatEntryControl上,放在TConversationForm上,最后放在tSkMainForm上。 (显然Skype客户端是用Delphi编写的;))
通过使用Win API,我如何找到正确的 tSkMainForm> TConversationForm> TChatEntryControl> TChatRichEdit ,然后在其中输入消息?
最好的方法是什么?
此外,TConversationForm还包含联系人的姓名,所以我想这会让它更容易一些?
编辑:这是Windspector Spy的截图,显示了TChatRichEdit:
这是我目前的代码:
function GetConversationWindow(Wnd: HWnd; P: LParam): Bool; stdcall;
var
Param: PGetConversationParam;
ProcID: DWord;
// WndClass docs say maximum class-name length is 256.
ClassName: array[0..256] of Char;
WindowTitle: array[0..256] of Char;
begin
Result := True; // assume it doesn't match; keep searching
Param := PGetConversationParam(P);
GetWindowThreadProcessID(Wnd, @ProcID);
if ProcID <> Param.ProcID then
Exit;
if GetClassName(Wnd, ClassName, Length(ClassName)) = 0 then
Exit;
if StrComp(ClassName, 'TConversationForm') <> 0 then
Exit;
if SendMessage(Wnd, wm_GetText, Length(WindowTitle), LParam(@WindowTitle[0])) = 0 then
Exit;
if Param.ContactName = WindowTitle then begin
Param.Result := Wnd;
Result := False;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Param: TGetConversationParam;
RichEditWnd, ControlWnd : HWND;
ParentWnd : HWND;
begin
//Param.ProcID := GetSkypeProcessID;
Param.ContactName := 'xSky Admin';
ParentWnd := FindWindowEx(0,0,'tSkMainForm',nil);
if EnumChildWindows(ParentWnd,@GetConversationWindow, LParam(@Param)) then
Abort; // Didn't find it.
// Param.Result holds the conversation window's handle. Now walk its children.
ControlWnd := FindWindowEx(Param.Result, 0, 'TChatEntryControl', nil);
if ControlWnd = 0 then
Abort; // Conversation doesn't have an entry control
RichEditWnd := FindWindowEx(ControlWnd, 0, 'TChatRichEdit', nil);
if RichEditWnd = 0 then
Abort;
ShowMessage('Got it!');
end;
我从未到过ShowMessage。
以下是我的IDE在调试模式下的屏幕截图:
我在Abort Line添加了一个断点。
有什么想法吗?
答案 0 :(得分:4)
这样的事情:
var
aHandle : cardinal;
begin
aHandle := FindWindow(PWideChar('TChatRichEdit'), nil);
result := aHandle <> 0;
if result then
PostMessage(aHandle, WM_...);
然后你有一个窗口的句柄。您可以使用WM_SETTEXT或其他东西来输入文本。 但Skype使用WM_COPYDATA与其他程序通信,反之亦然。 您应该搜索StackOverflow。
答案 1 :(得分:1)
我猜TConversationForm
是一个顶级窗口。使用EnumWindows
查找。 (不要打扰FindWindow
;它总是会返回它找到的第一个窗口,因此如果有多个会话处于活动状态,则无法控制您将获得哪个会话。)
type
PGetConversationParam = ^TGetConversationParam;
TGetConversationParam = record
ProcID: DWord;
ContactName: string;
Result: HWnd;
end;
function GetConversationWindow(Wnd: HWnd; P: LParam): Bool; stdcall;
var
Param: PGetConversationParam;
ProcID: DWord;
// WndClass docs say maximum class-name length is 256.
ClassName: array[0..256] of Char;
WindowTitle: array[0..256] of Char;
begin
Result := True; // assume it doesn't match; keep searching
Param := PGetConversationParam(P);
GetWindowThreadProcessID(Wnd, @ProcID);
if ProcID <> Param.ProcID then
Exit;
if GetClassName(Wnd, ClassName, Length(ClassName)) = 0 then
Exit;
if StrComp(ClassName, 'TConversationForm') <> 0 then
Exit;
if SendMessage(Wnd, wm_GetText, Length(WindowTitle), LParam(@WindowTitle[0])) = 0 then
Exit;
if Param.ContactName = WindowTitle then begin
Param.Result := Wnd;
Result := False;
end;
end;
该功能检查几件事以确保它正在查看所需的窗口。它检查窗口是否属于Skype进程,它是否具有预期的窗口类,并且其标题是目标联系人的名称。如果Skype在窗口标题中添加了其他文本,则需要确保它看起来“足够接近”。不要只是致电Pos
来查看联系人名称是否出现在标题的某个位置;如果任何联系人的名称是对话窗口标题的子字符串,则可能会在不应该的情况下无意中找到匹配项。
不严格要求进程ID,因此如果您不知道进程ID,则可以省略该部分。
EnumWindows
函数将为每个顶级窗口调用上述函数一次。如果窗口是您正在查找的窗口,GetConversationWindow
会返回 False 说“我找到了我想要的内容,所以请停止询问。”否则,它返回 True :“那个不是它,所以请再给我一个。”如果GetConversationWindow
曾返回 False ,则EnumWindows
也会返回 False ,而Param.Result
字段将保留窗口的句柄正在寻找。获得后,使用FindWindowEx
导航窗口层次结构的其余部分:
var
Param: TGetConversationParam;
begin
Param.ProcID := GetSkypeProcessID;
Param.ContactName := GetSkypeContactName;
if EnumWindows(@GetConversationWindow, LParam(@Param)) then
Abort; // Didn't find it.
// Param.Result holds the conversation window's handle. Now walk its children.
ControlWnd := FindWindowEx(Param.Result, 0, 'TChatEntryControl', nil);
if ControlWnd = 0 then
Abort; // Conversation doesn't have an entry control
RichEditWnd := FindWindowEx(ControlWnd, 0, 'TChatRichEdit', nil);
if RichEditWnd = 0 then
Abort;
// Voila!
end;