寻找与通过 WinAPI 和 Skype 客户端使用当前活动的聊天框德尔福?
在 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;
我从未达到显示消息。
以下是我的 IDE 在调试模式下的屏幕截图:
我在中止行添加了一个断点。
有什么想法吗?
In Delphi, by using the Skype API, I can send a message to a contact fairly easy. However, what I am trying to do, is enter the message in the Chat Box of the currently focused Contact, without sending the message.
By using Winspector, I found that the Classname of the Chatbox is TChatRichEdit, which is placed on a TChatEntryControl, which is placed on a TConversationForm, and finally, which is placed on the tSkMainForm. (Obviously the Skype Client is coded in Delphi ;) )
By using the Win API, how can I find the correct tSkMainForm>TConversationForm>TChatEntryControl>TChatRichEdit, and then enter a message into it?
What would be the best way to go about this?
Also, the TConversationForm contains the name of the contact aswell, so I guess that makes it a bit easier?
EDIT: Here is a screenshot of Windspector Spy, showing the TChatRichEdit:
Here is my current code:
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;
I never reach the ShowMessage.
Here is a screenshot of my IDE in Debug Mode:
I added a breakpoint at the Abort Line.
Any ideas?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
像这样:
然后你就拥有了该窗口的句柄。您可以使用WM_SETTEXT或其他东西来输入文本。
但Skype使用WM_COPYDATA与其他程序通信,反之亦然。
您应该在 StackOverflow 中搜索该内容。
Something like this:
Then you have a handle of that window. You can use WM_SETTEXT or something to input text.
But Skype uses WM_COPYDATA to communicate with other programs, and vice versa.
You should search StackOverflow for that.
我猜
TConversationForm
是一个顶级窗口。使用EnumWindows
来找到它。 (暂时不用费心FindWindow;它总是返回它找到的第一个窗口,因此,如果有多个活动对话,您无法控制将获得哪些对话。)该函数会检查几件事以确保它正在查看所需的窗口。它检查窗口是否属于 Skype 进程、是否具有预期的窗口类以及其标题是否是目标联系人的姓名。如果 Skype 在窗口标题中添加了其他文本,您需要确保它看起来“足够接近”。不要只是调用
Pos
来查看联系人姓名是否出现在标题中的某个位置;如果任何联系人的姓名是对话窗口标题的子字符串,您可能会无意中找到不应该找到的匹配项。进程 ID 并不是严格必需的,因此如果您不知道进程 ID,可以忽略该部分。
EnumWindows
函数将为每个顶级窗口调用一次上述函数。如果该窗口是您要查找的窗口,GetConversationWindow
将返回 False,表示“我已找到我想要的内容,因此请停止再询问”。否则,它返回True:“那个不是,所以请给我另一个。”如果GetConversationWindow
返回 False,则EnumWindows
也将返回 False 和Param.Result 字段将保存您正在查找的窗口的句柄。获得后,使用 FindWindowEx 来导航窗口层次结构的其余部分:
I guess
TConversationForm
is a top-level window. UseEnumWindows
to find that. (Don't bother withFindWindow
yet; it always returns the first window it finds, so if there are multiple conversations active, you have no control over which you'll get.)That function checks several things to make sure it's looking at the desired window. It checks that the window belongs to the Skype process, that it has the expected window class, and that its title is the name of the target contact. If Skype puts additional text in the window title, you'll need to make sure it looks "close enough." Don't just call
Pos
to see whether the contact name appears somewhere in the title; if any contact has a name that's a substring of the a conversation window's title, you might inadvertently find a match when you shouldn't.The process ID isn't strictly required, so you can omit that part if you don't know the process ID.
The
EnumWindows
function will call the above function once for each top-level window. If the window is the one you're looking for,GetConversationWindow
returns False to say, "I've found what I want, so please stop asking about any more." Otherwise, it returns True: "That one wasn't it, so please give me another." IfGetConversationWindow
ever returns False, thenEnumWindows
will also return False and theParam.Result
field will hold the handle of the window you were looking for. Once you have it, useFindWindowEx
to navigate the rest of the window hierarchy: