2

Delphiでは、Skype APIを使用することで、連絡先にメッセージを簡単に送信できます。しかし、私がやろうとしているのは、メッセージを送信せずに、現在フォーカスされている連絡先のチャットボックスにメッセージを入力することです。

Winspectorを使用すると、チャットボックスのクラス名はTChatRichEditであり、TChatEntryControlに配置され、TConversationFormに配置され、最後にtSkMainFormに配置されることがわかりました。(明らかに、SkypeクライアントはDelphiでコーディングされています;))

Win APIを使用して、正しいtSkMainForm> TConversationForm> TChatEntryControl> TChatRichEditを見つけて、それにメッセージを入力するにはどうすればよいですか?

これについて行くための最良の方法は何でしょうか?

また、TConversationFormには連絡先の名前も含まれているので、少し簡単になると思いますか?

編集:これは、Windspector Spyのスクリーンショットで、TChatRichEditを示しています。

Winspectorスパイ

これが私の現在のコードです:

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

中止行にブレークポイントを追加しました。

何か案は?

4

2 に答える 2

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で検索する必要があります。

于 2011-04-19T11:20:46.160 に答える
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関数は、トップレベルウィンドウごとに上記の関数を1回呼び出します。ウィンドウが探しているものである場合は、FalseGetConversationWindowを返し、「必要なものが見つかったので、これ以上質問するのをやめてください」と言います。それ以外の場合は、 Trueを返します。「それはそうではなかったので、もう1つください。」Falseを返す場合は、Falseも返し、フィールドは探していたウィンドウのハンドルを保持します。取得したら、を使用してウィンドウ階層の残りの部分をナビゲートします。GetConversationWindowEnumWindowsParam.ResultFindWindowEx

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;
于 2011-04-19T13:34:37.237 に答える