Autor Téma: Detekce messageboxu jiné aplikace  (Přečteno 2341 krát)

Offline am2

  • Příspěvků: 3
  • Karma: 0
Detekce messageboxu jiné aplikace
« kdy: 10-12-2012, 13:15:26 »
Nevíte někdo, jak napsat program (nejlépe v Delphi), který

- čeká a detekuje, zda jiná aplikace (jedna konkrétní, psaná v Delphi) vyhodí MessageBox

- přečte obsah toho MessageBoxu (má to jeden řádek), na základě toho program udělá nějakou určitou akci

- a "odklepne" to dialogové okno té aplikace (tento krok bych i oželel, pokud by to bylo těžké), jinak souřadnice umístění mesageboxu jsou známy, kdyby se to mělo odklepávat myší

(Je to na dodatečnou úpravu staršího programu, od kterého nejsou k dispozici zdrojáky (nepsalo se to u nás), takže to nemůžeme upravit přímo v původním programu.)

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 2329
  • Karma: 102
    • Verze Delphi: D5,D2007, DXE, DXE2 + 2 poslední (Tokyo)
    • O Delphi v češtině
Re:Detekce messageboxu jiné aplikace
« Odpověď #1 kdy: 10-12-2012, 17:28:59 »
- přečte obsah toho MessageBoxu (má to jeden řádek), na základě toho program udělá nějakou určitou akci

Napadlo mne poslat mu klavesovou zkratku CTRL+C - v zavislosti na verzi Delphi toho programu, nebo jak to naprogramoval ten clovek by mohlo zafungovat vlozeni do schranky.

Zkus prvni zobrazit ten message box a zda se ti to pres CTRL+C vlozi do schranky, pokud ne - tak se reseni hooodne zkomplikuje.
Embarcadero MVP - Czech republic

Offline TLama

  • Guru
  • *****
  • Příspěvků: 597
  • Karma: 31
    • Verze Delphi: 7, 2009, XE3
Re:Detekce messageboxu jiné aplikace
« Odpověď #2 kdy: 11-12-2012, 03:10:15 »
- čeká a detekuje, zda jiná aplikace (jedna konkrétní, psaná v Delphi) vyhodí MessageBox

To bohužel neporadím :-[
Pokud nejde o nic kritického, asi by stačilo hledat v procesu periodicky okno s třídou #32770

http://msdn.microsoft.com/en-us/library/ms633574%28VS.85%29.aspx#system

Kdyby ten message box byl popup oknem formu, ke kterému znáš handle, tedy znal bys de-facto
první parametr volaného MessageBox, mohl bys tento handle dosadit do podobné funkce:

Kód: Delphi [Vybrat]
  1. function MsgBoxGetWnd(ParentWnd: HWND): HWND;
  2. var
  3.   PopupWnd: HWND;
  4.   ClassName: array[0..255] of Char;
  5. const
  6.   GW_ENABLEDPOPUP = 6;
  7. begin
  8.   Result := 0;
  9.   PopupWnd := GetWindow(ParentWnd, GW_ENABLEDPOPUP);
  10.   if PopupWnd <> 0 then
  11.   begin
  12.     if GetClassName(PopupWnd, @ClassName, Length(ClassName)) <> 0 then
  13.       if ClassName = '#32770' then
  14.         Result := PopupWnd;
  15.   end;
  16. end;

- přečte obsah toho MessageBoxu (má to jeden řádek), na základě toho program udělá nějakou určitou akci

Parametr MsgBoxWnd je handle na message box, ze kterého chceš dostat text:

Kód: Delphi [Vybrat]
  1. function MsgBoxGetText(MsgBoxWnd: HWND): string;
  2. var
  3.   LabelWnd: HWND;
  4.   LabelText: string;
  5.   LabelTextLen: Integer;
  6. begin
  7.   Result := '';
  8.   LabelWnd := GetDlgItem(MsgBoxWnd, $FFFF);
  9.   if LabelWnd <> 0 then
  10.   begin
  11.     LabelTextLen := GetWindowTextLength(LabelWnd);
  12.     SetLength(LabelText, LabelTextLen);
  13.     if GetWindowText(LabelWnd, PChar(LabelText), LabelTextLen + 1) <> 0 then
  14.       Result := LabelText;
  15.   end;
  16. end;

- a "odklepne" to dialogové okno té aplikace (tento krok bych i oželel, pokud by to bylo těžké), jinak souřadnice umístění mesageboxu jsou známy, kdyby se to mělo odklepávat myší

Parametr MsgBoxWnd je handle na message box, ve kterém chceš stisk tlačítka simulovat a
BtnId je ID, které je současně výsledkem volání funkce (např. IDYES, IDNO, IDCANCEL atd.).
Pokud je message box, který chceš "odkliknout" typu MB_OK, tedy pouze s jediným tlačítkem
Ok, je potřeba použít pro parametr BtnId konstantu IDCANCEL, ne IDOK, jak by dalo očekávat.

Kód: Delphi [Vybrat]
  1. function MsgBoxBtnClick(MsgBoxWnd: HWND; BtnId: Integer): Boolean;
  2. var
  3.   BtnWnd: HWND;
  4. begin
  5.   Result := False;
  6.   BtnWnd := GetDlgItem(MsgBoxWnd, BtnId);
  7.   if BtnWnd <> 0 then
  8.   begin
  9.     Result := PostMessage(MsgBoxWnd, WM_COMMAND, MAKEWPARAM(BtnId, BN_CLICKED),
  10.       LPARAM(BtnWnd));
  11.   end;
  12. end;

Celek z výše uvedených funkcí by pak mohl být nějak takto (příklad ukazuje, jak by se dalo "najít"
okno MessageBox za pomocí hadle okna, které ho vlastní, získat z něj text a pak ho "odkliknout":

Kód: Delphi [Vybrat]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. var
  3.   MainForm: HWND;
  4.   MsgBoxWnd: HWND;
  5.   MsgBoxText: string;
  6. begin
  7.   MainForm := <-- tady by se dosadil handle formuláře té "cizí" aplikace, ze kterého message box vyskočí
  8.   MsgBoxWnd := MsgBoxGetWnd(MainForm);
  9.   if MsgBoxWnd <> 0 then
  10.   begin
  11.     MsgBoxText := MsgBoxGetText(MsgBoxWnd);
  12.     MsgBoxBtnClick(MsgBoxWnd, IDCANCEL);
  13.     ShowMessage('Text message box: ' + sLineBreak + MsgBoxText);
  14.   end;
  15. end;
« Poslední změna: 11-12-2012, 15:49:17 od TLama »

Offline Radek Červinka

  • Administrátoři
  • Padawan
  • *****
  • Příspěvků: 2329
  • Karma: 102
    • Verze Delphi: D5,D2007, DXE, DXE2 + 2 poslední (Tokyo)
    • O Delphi v češtině
Re:Detekce messageboxu jiné aplikace
« Odpověď #3 kdy: 11-12-2012, 08:58:39 »
TLamo, to vše ale předpokládá, že je jedná o std. MessageBox. Uplně nejstarší Delphi, včetně 5 ale mají implementováno ShowMessage jako instanci TForm (resp. TMessageForm) a tam to IMHO nebude fungovat. Hodně lidí, ale std. ShowMessage nepoužilo a volalo MessageBox z API windows.

Proto jsem se první ptal, zda mu funguje to s tím CTRL+C, protože to tam nefungovalo pokud se použil ShowMessage. Tím pádem ani pak nefunguje enumerace okna,  ani čtení z okna a takže jsem se nechtěl pouštět do žádných větších akcí, zvláště když řekl, že je jedná o starší Delphi aplikaci.
Embarcadero MVP - Czech republic

Offline am2

  • Příspěvků: 3
  • Karma: 0
Re:Detekce messageboxu jiné aplikace
« Odpověď #4 kdy: 11-12-2012, 11:26:20 »
Zkus prvni zobrazit ten message box a zda se ti to pres CTRL+C vlozi do schranky, pokud ne - tak se reseni hooodne zkomplikuje.

Pokud je MessageBox aktivní a dám Ctrl+C, tak pak do txt souboru lze vložit:

---------------------------
Confirm
---------------------------
Text hlasky
---------------------------
OK   Cancel   
---------------------------

Offline TLama

  • Guru
  • *****
  • Příspěvků: 597
  • Karma: 31
    • Verze Delphi: 7, 2009, XE3
Re:Detekce messageboxu jiné aplikace
« Odpověď #5 kdy: 11-12-2012, 12:08:30 »
TLamo, to vše ale předpokládá, že je jedná o std. MessageBox.

To rozhodně, ale mě na dotaz zmiňující MessageBox holt nic jiného nenapadlo ;D

Napadlo mne poslat mu klavesovou zkratku CTRL+C - v zavislosti na verzi Delphi toho programu, nebo jak to naprogramoval ten clovek by mohlo zafungovat vlozeni do schranky.

A post jsem poslal hlavně proto, že mi z téhle věty tak trochu vyznělo, že bys chtěl kopírovat text z WinAPI MessageBox přes schránku. Ale asi jsi to jenom nějak špatně formuloval...

Zkus prvni zobrazit ten message box a zda se ti to pres CTRL+C vlozi do schranky, pokud ne - tak se reseni hooodne zkomplikuje.
Pokud je MessageBox aktivní a dám Ctrl+C, tak pak do txt souboru lze vložit:

---------------------------
Confirm
---------------------------
Text hlasky
---------------------------
OK   Cancel   
---------------------------

Nakonec to dobře dopadlo, ta aplikace skutečně používá WinAPí MessageBox...
« Poslední změna: 11-12-2012, 12:10:22 od TLama »

Offline am2

  • Příspěvků: 3
  • Karma: 0
Re:Detekce messageboxu jiné aplikace
« Odpověď #6 kdy: 14-12-2012, 12:58:55 »
Díky moc! Až budu mít chvilku, zkusím to.
To jsem nečekal, že to bude půjde udělat na 10 řádků  :o

Offline TLama

  • Guru
  • *****
  • Příspěvků: 597
  • Karma: 31
    • Verze Delphi: 7, 2009, XE3
Re:Detekce messageboxu jiné aplikace
« Odpověď #7 kdy: 14-12-2012, 16:59:19 »
Díky moc! Až budu mít chvilku, zkusím to.
To jsem nečekal, že to bude půjde udělat na 10 řádků  :o

Není zač! Hlavně pozor, že tohle bude fungovat pouze pokud je MessageBox "modální", přesněji pokud
jej okno, jehož handle předáváš do parametru ParentWnd funkce MsgBoxGetWnd vlastní, tzn. pokud
ta cizí aplikace zobrazuje MessageBox tímto způsobem:

Kód: Delphi [Vybrat]
  1. procedure TForm1.Button1Click(Sender: TObject);
  2. begin
  3.   // form musí message box vlastnit, aby funkce MsgBoxGetWnd fungovala správně; k tomu je určený první
  4.   // parametr, který musí být nastavený na handle formu, který se pak předává do funkce MsgBoxGetWnd;
  5.   // čili nějak takhle by měla ta cizí aplikace zobrazovat message box
  6.   //              |
  7.   //              ▼
  8.   if MessageBox(Handle, 'Text', 'Caption', MB_OK) = IDOK then
  9.   ...
  10. end;
« Poslední změna: 14-12-2012, 17:15:15 od TLama »