Top.Mail.Ru
Мастера DELPHI, Delphi programming community Титульная страница Поиск, карта сайта Написать письмо 
| Новости | Поиск | FAQ | Статьи | Книги | Новости VCL |
| Форумы ЧАТ | Орешник | KOL и MCK |
Форумы -» Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / RSDN.ORG

« предыдущая ветвь | форум | следующая ветвь »

Получение handle-а активного компонента (вне рабочей формы) [D6, WinXP]


Delphi5.01 ©   (2006-05-05 13:16:00) 

Доброе время суток,
У меня возник вот такой вопрос, как можно вставить текст в компонент который на данный момент активен. Речь идет не только о компонентах рабочей формы но например если на данный момент активно приложение MS Word то текст должен вставляться на место курсора и т.д.
В интернете уже искал, похожей задачи к сожалению не нашел, может не правильно поиск давал? Помню были API функции для работы с буфером, типа копирования и вставки но для начала нужно как то получить handle компонента который на данный момент активен.
Заранее благодарю всех за уделенное время и желание помочь


почти_что_решение   (2006-05-06 10:21:00) [1]

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, OleCtrls,
 mshtml, activeX, Shdocvw_tlb;

type
 TForm1 = class(TForm)
   Edit1: TEdit;
   Memo1: TMemo;
   procedure FormCreate(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
   procedure findActiveWindow();
   procedure hotykey(var msg:TMessage); message WM_HOTKEY;
 end;

var
 Form1: TForm1;
 id,id2:Integer;

implementation

{$R *.dfm}

type
 TObjectFromLResult = function(LRESULT: lResult; const IID: TIID; WPARAM: wParam; out pObject): HRESULT; stdcall;

function GetIEFromHWND(WHandle: HWND; var IE: IWebbrowser2): HRESULT;
var
 hInst: HWND;
 lRes: Cardinal;
 MSG: Integer;
 pDoc: IHTMLDocument2;
 ObjectFromLresult: TObjectFromLresult;
begin
 hInst := LoadLibrary('Oleacc.dll');
 @ObjectFromLresult := GetProcAddress(hInst, 'ObjectFromLresult');
 if @ObjectFromLresult nil then begin
   try
     MSG := RegisterWindowMessage('WM_HTML_GETOBJECT');
     SendMessageTimeOut(WHandle, MSG, 0, 0, SMTO_ABORTIFHUNG, 1000, lRes);
     Result := ObjectFromLresult(lRes, IHTMLDocument2, 0, pDoc);
     if Result = S_OK then
       (pDoc.parentWindow as IServiceprovider).QueryService(IWebbrowserApp, IWebbrowser2, IE);
   finally
     FreeLibrary(hInst);
   end;
 end;
end;

procedure FillInGMXForms(IE: IWebbrowser2);
var
 i: Integer;
 m: Integer;
 ovElements: OleVariant;
 Document: Variant;
begin
 Document := IE.Document;
 //Poisk vsekh form na strnichke
 for m := 0 to Document.forms.Length - 1 do
 begin
   ovElements := Document.forms.Item(m).elements;

   //Perebor vsekh plov v zadannoi forme
   for i := ovElements.Length - 1 downto 0 do
   begin
     try
       if (ovElements.item(i).name = 'username') then
         ovElements.item(i).Value := 'myTest';
         
       form1.Memo1.Lines.Add(ovElements.item(i).id + ':' + ovElements.item(i).name + ':' +
         ovElements.item(i).tagName + ':' + ovElements.item(i).type);
     except
     end;
   end;
 end;
end;

procedure TForm1.findActiveWindow();
 var
   Wnd: HWnd;
   GuiInfo:TGUIThreadInfo;
   IE: IWebBrowser2;
   x: Olevariant;
begin
 Wnd := GetForegroundWindow;
 GuiInfo.cbSize:=SizeOf(TGUIThreadInfo);
 GetGUIThreadInfo(GetWindowThreadProcessId(Wnd),GuiInfo);
 GetIEFromHWnd(GuiInfo.hwndFocus, IE);
 FillInGMXForms(IE);
end;

procedure TForm1.hotykey(var msg:TMessage);
var
 IDoc1: IHTMLDocument2;
 Web: ShDocVW_TLB.IWebBrowser2;
begin
   if (msg.LParamLo=MOD_CONTROL) and (msg.LParamHi=ord('Q')) then begin
       findActiveWindow();
       //ShowMessage('Ctrl + Q wurde gedrückt !');
   end;

   if (msg.LParamLo=MOD_CONTROL) and (msg.LParamHi=ord('R')) then begin
       ShowMessage('Ctrl + R wurde gedrückt !');
   end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   id:=GlobalAddAtom('hotkey');
   RegisterHotKey(handle,id,mod_control,ord('Q'));

   id2:=GlobalAddAtom('hotkey2');
   RegisterHotKey(handle,id2,mod_control,ord('R'));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
   UnRegisterHotKey(handle,id);
   UnRegisterHotKey(handle,id2);
end;

end.

Данный пример полно функциональный, но к сожалению он может находить в IE нужное поле с нужным именем и заполнять. Мне же желательно найти сразу ктивное поле
Надеюсь комуто данный пример поможет, вот дополнительные ссылки которые будут интерестны и конечно в любое время можете мылить :-)

http://www.swissdelphicenter.ch/torry/showcode.php?id=1732
http://msdn.microsoft.com/workshop/author/dhtml/reference/collections/children.asp


Delphi5.01   (2006-05-06 09:38:00) [2]

unit Unit1;

interface

uses
 Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
 Dialogs, StdCtrls, OleCtrls,
 mshtml, activeX, Shdocvw_tlb;

type
 TForm1 = class(TForm)
   Edit1: TEdit;
   procedure FormCreate(Sender: TObject);
   procedure FormDestroy(Sender: TObject);
 private
   { Private declarations }
 public
   { Public declarations }
   procedure findActiveWindow();
   procedure hotykey(var msg:TMessage); message WM_HOTKEY;
 end;

var
 Form1: TForm1;
 id,id2:Integer;

implementation

{$R *.dfm}

type
 TObjectFromLResult = function(LRESULT: lResult; const IID: TIID; WPARAM: wParam; out pObject): HRESULT; stdcall;

function GetIEFromHWND(WHandle: HWND; var IE: IWebbrowser2): HRESULT;
var
 hInst: HWND;
 lRes: Cardinal;
 MSG: Integer;
 pDoc: IHTMLDocument2;
 ObjectFromLresult: TObjectFromLresult;
begin
 hInst := LoadLibrary('Oleacc.dll');
 @ObjectFromLresult := GetProcAddress(hInst, 'ObjectFromLresult');
 if @ObjectFromLresult nil then begin
   try
     MSG := RegisterWindowMessage('WM_HTML_GETOBJECT');
     SendMessageTimeOut(WHandle, MSG, 0, 0, SMTO_ABORTIFHUNG, 1000, lRes);
     Result := ObjectFromLresult(lRes, IHTMLDocument2, 0, pDoc);
     if Result = S_OK then
       (pDoc.parentWindow as IServiceprovider).QueryService(IWebbrowserApp, IWebbrowser2, IE);
   finally
     FreeLibrary(hInst);
   end;
 end;
end;

procedure TForm1.findActiveWindow();
 var
   Wnd: HWnd;
   GuiInfo:TGUIThreadInfo;
   IE: iwebbrowser2;
   x: Olevariant;
begin
 Wnd := GetForegroundWindow;
 GuiInfo.cbSize:=SizeOf(TGUIThreadInfo);
 GetGUIThreadInfo(GetWindowThreadProcessId(Wnd),GuiInfo);
 GetIEFromHWnd(GuiInfo.hwndFocus, IE);
 IE.Navigate(' http://google.com', x, x, x, x);
end;

procedure TForm1.hotykey(var msg:TMessage);
var
 IDoc1: IHTMLDocument2;
 Web: ShDocVW_TLB.IWebBrowser2;
begin
   if (msg.LParamLo=MOD_CONTROL) and (msg.LParamHi=ord('Q')) then begin
       findActiveWindow();
       //ShowMessage('Ctrl + Q wurde gedrückt !');
   end;

   if (msg.LParamLo=MOD_CONTROL) and (msg.LParamHi=ord('R')) then begin
       ShowMessage('Ctrl + R wurde gedrückt !');
   end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
   id:=GlobalAddAtom('hotkey');
   RegisterHotKey(handle,id,mod_control,ord('Q'));

   id2:=GlobalAddAtom('hotkey2');
   RegisterHotKey(handle,id2,mod_control,ord('R'));
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
   UnRegisterHotKey(handle,id);
   UnRegisterHotKey(handle,id2);
end;

Вот рабочи вариант который перенаправляет активную страничку браузера на адресс google.com, браузер в этот момент должен иметь фокус. Надо заметить что при разработке была использованна утилита SypXX от MS. Теперь осталось малое, найти нужный филд в iwebbrowser2 и сменить значение :-)


Delphi5.01   (2006-05-06 08:26:00) [3]

Вот только что сообразил что приме на выше указанной страничке не подходит, так как он работает с компонентом WebBrowser1: TWebBrowser; который находиться на рабочей форме а не на внешней программе :-(
...
FillInGMXForms(Web, iDoc1, Webbrowser1.Document, 'user@gmx.net', 'pswd');
...


Delphi5.01   (2006-05-06 03:42:00) [4]

Вот рабочий вариант программы, но как всегда возникли проблемы с продуктами MS, например данный код не работает для полей IE
При получении активного контрола в IE процедура получает handle всей странички (а не отдельного edit-а)

procedure TForm1.findActiveWindow();
 var
   Wnd: HWnd;
   GuiInfo:TGUIThreadInfo;
begin
 Wnd:= GetForegroundWindow;
 GuiInfo.cbSize:=SizeOf(TGUIThreadInfo);
 GetGUIThreadInfo(GetWindowThreadProcessId(Wnd),GuiInfo);
 Edit1.CopyToClipboard;
 //ShowWindow(GuiInfo.hwndFocus, SW_HIDE);
 //SendMessage(GuiInfo.hwndFocus, WM_SETTEXT, 0, integer(pChar(Edit1.Text)));
 //PostMessage(GuiInfo.hwndFocus, WM_PASTE, 0, 0);
 Application.ProcessMessages;
end;

Вот рабочий пример но для IE нужно работать через IShellWindows, IWebbrowser2 и т.д. Но получаеться что, для простой вставки нужно разбирать довольно обемную документацию :-(
http://www.swissdelphicenter.ch/torry/showcode.php?id=1732

Что вы посоветуюете почитать на счет работы с IE через OLE, только пожалйста не давайте документацию на MSDN или похожую сухую доку. Желательно с примером и в пошагвом режиме :-)

Заранее благодарю :-)


Leonid Troyanovsky   (2006-05-06 01:43:00) [5]


> Delphi5.01 ©   (05.05.06 13:16)  

>  может не правильно поиск давал? Помню были API функции
> для работы с буфером, типа копирования и вставки но для


Про буфер можно забыть, это для юзера.
Вставлять текст можно, например, путем WM_SETTEXT.

Только, например, для Word это может и не пройти.
Возможно, что для него это EM_SETSEL (который, в отличии
от WM_SETTEXT, должен посылаться из потока, принадлежащего
целевому приложению). И, скорее всего, путем SendMessageW.

Ну, а, вообще-то, с такими монстрами как Word, IE & etc давно уж
положено общаться через COM.

--
Regards, LVT.


Leonid Troyanovsky   (2006-05-06 01:32:00) [6]


> доп_инф   (05.05.06 21:55) [4]

> в нем среди чайлд виндоу найти активное (должно соответствовать
> окну где находиться курсор).


AttachThreadInput & GetFocus.

Пример я недавно, IMHO, приводил.

--
Regards, LVT.


Leonid Troyanovsky   (2006-05-06 01:05:00) [7]


> Delphi5.01 ©   (05.05.06 23:28) [8]


>  GetWindowThreadProcessId(activeHWND, activeThreadID);

activeThreadID := GetWindowThreadProcessId(activeHWND, nil);

> Результат как и ожидалось Failure :-( что делать, кто посоветует?

GetLastError? В смысле, Win32Check.

--
Regards, LVT.


Мефисто   (2006-05-06 00:49:00) [8]

GetWindowThreadProcessId  - получение ID потока некоего процесса, вроде сразу не то.

GetForegroundWindow - возвращает хендл активного окна (конкретно то, что подразумевается под формой).


Delphi5.01   (2006-05-05 23:28:00) [9]

procedure TForm1.findActiveWindow();
 var
   activeHWND: HWND;
   activeThreadID: DWORD;
   GUIThreadInfo: tagGUITHREADINFO;
begin
 activeHWND := GetForegroundWindow;
 GetWindowThreadProcessId(activeHWND, activeThreadID);
 if GetGUIThreadInfo(activeThreadID, GUIThreadInfo) then
   ShowMessage('Success') else
   ShowMessage('Failure');
end;


Результат как и ожидалось Failure:-( что делать, кто посоветует?


Delphi5.01   (2006-05-05 23:22:00) [10]

procedure TForm1.findActiveWindow();
 var
   activeHWND: HWND;
   activeThreadID: DWORD;
   GUIThreadInfo: tagGUITHREADINFO;
   i: integer;
begin
 activeHWND := GetForegroundWindow;
 GetWindowThreadProcessId(activeHWND, activeThreadID);
 GetGUIThreadInfo(activeThreadID, GUIThreadInfo);
 ShowWindow(GUIThreadInfo.hwndActive, SW_HIDE);

 for i:=0 to Form1.ComponentCount-1 do begin
   if (GUIThreadInfo.hwndActive = (Form1.Components[i] as TWinControl).Handle) then
     ShowMessage((Form1.Components[i] as TWinControl).Name);
 end;
end;


Выше приведенный пример доказывает что не правильно определяется activeThreadID, так как в противном случае данная функция должна отобразить имя компонента (button-а) на нажатие которого вызывается данная функция (он и является активным)


Delphi5.01   (2006-05-05 23:15:00) [11]

Типа думаю надо тему в раздел Win32 переносить, вот что я раскопал пока :-)

procedure TForm1.findActiveWindow();
 var
   activeHWND: HWND;
   activeThreadID: DWORD;
   GUIThreadInfo: tagGUITHREADINFO;
begin
 activeHWND := GetForegroundWindow;
 GetWindowThreadProcessId(activeHWND, activeThreadID);
 GetGUIThreadInfo(activeThreadID, GUIThreadInfo);
 ShowWindow(GUIThreadInfo.hwndActive, SW_HIDE)
 ShowMessage(inttostr(GUIThreadInfo.hwndActive));
end;

Думаю я не правильно получаю activeThreadID :-(
Вся дока вот здесь по теме:
1) http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowfunctions/getguithreadinfo.asp
2) http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winui/winui/windowsuserinterface/windowing/windows/windowreference/windowstructures/guithreadinfo.asp

Что думаешь Мефисто?


Мефисто   (2006-05-05 22:03:00) [12]

Фиг знает :) А WinSight32 в поставке с дельфей чего говорит? Сделай два простых тестовых приложения, поэксперементируй с ними, поизучай какие сообщения пролетают. Может в теории сколько-то поможет.


доп_инф   (2006-05-05 21:55:00) [13]

Спасибо но вставку я буду делать на шорткатах, и с помощью хуков, так что мое окно вообще не будет активным а будет работать в фоновом режиме.
Есть подозрения что нужно будет найти активное окно и потом в нем среди чайлд виндоу найти активное (должно соответствовать окну где находиться курсор).
Что вы думаете насчет такого размышления?


VEZ   (2006-05-05 18:26:00) [14]

Оффтопик, но MS Office и вообще все продукты майкрософт не используют стандартные компоненты винды, интересно почему:).


ANB   (2006-05-05 17:27:00) [15]

1) Если ты кликнешь по своей кнопке, то активное приложение им быть перестанет
2) keybd_event печатает именно на активном окне. Так же, как на клавиатуре. писать много.


доп_инф   (2006-05-05 13:59:00) [16]

Хотел бы уточнить что я знаю функцию которая возвращает handle активного окна GetForegroundWindow но как я догадываюсь теперь, продеться проверить все его дочерние окна, так как все компоненты, в том числе все edit, reachedit и т.д. на самом деле являются "окнами".


Наверх

Титульная страница Поиск, карта сайта Написать письмо