windows - Finding & bruger den aktuelt aktive chatboks i Skype-klienten via WinAPI & Delphi?

Indlæg af Hanne Mølgaard Plasc

Problem



I Delphi, ved at bruge Skype API, kan jeg sende en besked til en kontakt forholdsvis let. Men det jeg forsøger at gøre er at indtaste beskeden i Chat Box i den aktuelt fokuserede Kontakt, uden at sende beskeden.


Ved at bruge Winspector fandt jeg, at Chatboxens navn er TChatRichEdit, som er placeret på en TChatEntryControl, som er placeret på en TConversationForm, og endelig, som er placeret på tSkMainForm. (Det er klart, at Skype-klienten er kodet i Delphi;))


Hvordan kan jeg finde den korrekte tSkMainForm> TConversationForm> TChatEntryControl> TChatRichEdit , og derefter indtaste en besked i den?


Hvad ville være den bedste måde at gå over til dette?


TConversationForm indeholder også navnet på kontakten, så jeg tror det gør det lidt lettere?


EDIT: Her er et screenshot af Windspector Spy, der viser TChatRichEdit:


Winspector Spy


Her er min nuværende kode:


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;


Jeg når aldrig ShowMessage.


Her er et skærmbillede af min IDE i fejlfindingstilstand:


IDE i fejlfindingstilstand


Jeg tilføjede et breakpoint på Abort Line.


Nogle ideer?

Bedste reference


Jeg gætter TConversationForm er et topniveau vindue. Brug EnumWindows for at finde det. (Lad dig ikke glemme med FindWindow endnu; den vender altid tilbage til det første vindue, den finder, så hvis der er flere samtaler aktive, har du ingen kontrol over, hvad du får.)


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;


Denne funktion kontrollerer flere ting for at sikre, at den ser på det ønskede vindue. Det kontrollerer, at vinduet tilhører Skype-processen, at den har den forventede vinduesklasse, og at dens titel er navnet på kontaktpersonen. sætter yderligere tekst i vinduetitel, du skal sørge for at det ser ud til at være tæt nok. Du skal bare ringe Pos for at se, om kontaktnavnet vises et sted i titlen; hvis nogen kontakt har et navn som 'sa substring i en samtale vindues titel, kan du måske udilsigtet finde en kamp, ​​når du ikke skal.


Process ID er ikke strengt nødvendigt, så du kan udelade den del, hvis du ikke kender proces ID.


Funktionen EnumWindows kalder ovenstående funktion en gang for hvert topniveau vindue. Hvis vinduet er det, du søger efter, returnerer [[<10]] False for at sige, 'Jeg har fundet det, jeg vil have, så stop med at spørge om mere.' Ellers returneres det True : 'Det var det ikke, så giv mig en anden.' Hvis GetConversationWindow nogensinde vender tilbage False , så EnumWindows]] vil også returnere False , og feltet Param.Result vil holde håndtaget i det vindue, du ledte efter. Når du har det, skal du bruge FindWindowEx til at navigere resten af ​​vinduet hierarki:


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;

Andre referencer 1


Noget sådan:


var
  aHandle   : cardinal;
begin
   aHandle := FindWindow(PWideChar('TChatRichEdit'), nil);
   result  := aHandle <> 0;
   if result then
      PostMessage(aHandle, WM\_...); 


Så har du et håndtag af vinduet. Du kan bruge WM\_SETTEXT eller noget til at indtaste tekst.
Men Skype bruger WM\_COPYDATA til at kommunikere med andre programmer og omvendt.
Du skal søge StackOverflow for det.