寻找&使用Skype客户端中当前活动的Chatbox通过WinAPI&德尔福?

时间:2011-04-19 09:51:15

标签: windows delphi winapi skype handles

在Delphi中,通过使用Skype API,我可以非常轻松地向联系人发送消息。但是,我正在尝试做的是在当前关注的联系人的聊天框中输入消息,而不发送消息。

通过使用Winspector,我发现Chatbox的Classname是TChatRichEdit,它放在TChatEntryControl上,放在TConversationForm上,最后放在tSkMainForm上。 (显然Skype客户端是用Delphi编写的;))

通过使用Win API,我如何找到正确的 tSkMainForm> TConversationForm> TChatEntryControl> TChatRichEdit ,然后在其中输入消息?

最好的方法是什么?

此外,TConversationForm还包含联系人的姓名,所以我想这会让它更容易一些?

编辑:这是Windspector Spy的截图,显示了TChatRichEdit:

Winspector Spy

这是我目前的代码:

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在调试模式下的屏幕截图:

IDE in Debug Mode

我在Abort Line添加了一个断点。

有什么想法吗?

2 个答案:

答案 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;