Конференция "WinAPI" » Как заставить GUI thread создать нужное мне окно?
 
  • SPeller © (25.11.09 02:38) [20]
    Спасибо за то, что про хуки напомнили, как же я о них забыл-то? :)

    Накидал вот такой код:

    function CreateWndInThread(idTarget: Cardinal; WndProc: Pointer): HWND;

    implementation

    var
     HookCS: TRTLCriticalSection;
     HHK: HHOOK;
     TargetTHreadID: Cardinal;
     NewWnd: HWND;
     NewWndCreating: Boolean;

    function EnumThreadWndProc(hwnd: HWND; lParam: PInteger): BOOL; stdcall;
    begin
     lParam^ := hwnd;
     Result := False;
    end;

    function HookWndProc(nCode, wParam, lParam: Integer): Integer; stdcall;
    begin
     if (nCode >= HC_ACTION) then
     begin
       if (NewWnd = 0) and not NewWndCreating and (GetCurrentThreadId = TargetTHreadID) then
       begin
         NewWndCreating := True;
         NewWnd :=
           CreateWindow(
             'STATIC', 'Test window', WS_VISIBLE, 0, 0, 200, 200, 0, 0, HInstance, nil);
         NewWndCreating := False;
       end;
       Result := CallNextHookEx(HHK, nCode, wParam, lParam);
     end
     else
       Result := CallNextHookEx(HHK, nCode, wParam, lParam);
    end;

    function CreateWndInThread(idTarget: Cardinal; WndProc: Pointer): HWND;
    var
     enumResult: HWND;
     hTarget: THandle;
    begin
     Result := 0;
     hTarget := OpenThread(THREAD_ALL_ACCESS, False, idTarget);
     HookCS.Enter;
     TargetTHreadID := idTarget;
     try
       try
         try
           SuspendThread(hTarget);
           try
             enumResult := 0;
             EnumThreadWindows(idTarget, @EnumThreadWndProc, Integer(@enumResult));
             if (enumResult = 0) then
               raise Exception.Create('No windows in the thread');
             HHK := SetWindowsHookEx(WH_CALLWNDPROC, @HookWndProc, HInstance, idTarget);
           finally
             ResumeThread(hTarget);
           end;
           SendMessage(enumResult, WM_USER + 1, 0, 0);
           if (NewWnd = 0) then
             raise Exception.Create('Failed to create window');
           Result := NewWnd;
           NewWnd := 0;
           SetWindowLong(Result, GWL_WNDPROC, Integer(WndProc));
         finally
           TargetTHreadID := 0;
           UnhookWindowsHookEx(HHK);
           HHK := 0;
         end;
       except
         DestroyWindow(NewWnd);
         NewWnd := 0;
         raise;
       end;
     finally
       HookCS.Leave;
       CloseHandle(hTarget);
     end;
    end;

    initialization
     HookCS.Initialize;

    finalization
     HookCS.Destroy;



    Тут, конечно, если нет еще окон в потоке, то ничего и не выйдет, но и и предыдущий мой вариант тоже оказался бы бессилен. Окно бы он создал, но без цикла выборки очереди сообщений смысл окна теряется.
  • SPeller © (25.11.09 05:24) [21]
    Немного поправил, сделал перечисление всех окон и в цикле потом слать им сообщения пока окно не создастся. Еще мания у меня какая-то суспендить поток. Тут это сделал для того, чтобы немного снизить шансы изменения списка окон во время перечисления. Может, я опять всё усложняю? :) Наверное, хотябы одно, главное окно в потоке будет и на нем SendMessage отработает.
  • SPeller © (25.11.09 05:39) [22]
    На данный момент вот такое получилось

    function CreateWndInThread(idTarget: Cardinal; WndProc: Pointer): HWND;

    implementation

    var
     HookCS: TRTLCriticalSection;
     HHK: HHOOK;
     TargetThreadID: Cardinal;
     NewWnd: HWND;
     NewWndCreating: Boolean;

    type
     TMyArray = array of HWND;
     PMyArray = ^TMyArray;

    function EnumThreadWndProc(hwnd: HWND; lParam: PMyArray): BOOL; stdcall;
    var
     l: Integer;
    begin
     l := Length(lParam^);
     SetLength(lParam^, l + 1);
     lParam^[l] := hwnd;
     Result := True;
    end;

    function HookWndProc(nCode, wParam, lParam: Integer): Integer; stdcall;
    begin
    //  if (TargetThreadID <> 0) and (GetCurrentThreadId <> TargetThreadID) then
    //    asm int 3; end;
     if
       (nCode >= HC_ACTION) and (NewWnd = 0) and not NewWndCreating and
       (TargetThreadID <> 0)
     then
     begin
       NewWndCreating := True;
       NewWnd :=
         CreateWindow(
           'STATIC', 'Test window', WS_VISIBLE, 0, 0, 200, 200, 0, 0, HInstance, nil);
       NewWndCreating := False;
     end;
     Result := CallNextHookEx(HHK, nCode, wParam, lParam);
    end;

    function CreateWndInThread(idTarget: Cardinal; WndProc: Pointer): HWND;
    var
     threadWindows: TMyArray;
     i, l: Integer;
    begin
     Result := 0;
     HookCS.Enter;
     TargetThreadID := idTarget;
     try
       try
         try
           EnumThreadWindows(idTarget, @EnumThreadWndProc, Integer(@threadWindows));
           l := Length(threadWindows);
           if (l = 0) then
             raise Exception.Create('No windows in the thread');
           HHK := SetWindowsHookEx(WH_CALLWNDPROC, @HookWndProc, 0, idTarget);
           i := 0;
           repeat
             SendMessage(threadWindows[i], WM_USER + 1, 0, 0);
             Inc(i);
           until (NewWnd <> 0) or (i >= l);
           if (NewWnd = 0) then
             raise Exception.Create('Failed to create window');
           Result := NewWnd;
           SetWindowLong(Result, GWL_WNDPROC, Integer(WndProc));
         finally
           TargetThreadID := 0;
           UnhookWindowsHookEx(HHK);
           HHK := 0;
         end;
       except
         DestroyWindow(NewWnd);
         raise;
       end;
     finally
       NewWnd := 0;
       HookCS.Leave;
     end;
    end;

  • Игорь Шевченко © (25.11.09 10:15) [23]
    Саша, извини за глупый вопрос - а зачем оно надо ? Может, есть какие-то более прямые пути к цели ?
  • SPeller © (25.11.09 11:17) [24]
    Я в первом топике описал, зачем оно надо. Для организации апартмента и маршалинга ком-вызовов. Без окна в гуи-потоке у меня нет других идей как это осуществить для случаев, когда ком-сервер должен взаимодействовать с этим гуем.
  • Leonid Troyanovsky © (25.11.09 13:40) [25]

    > SPeller ©   (25.11.09 02:38) [20]

    > Тут, конечно, если нет еще окон в потоке, то ничего и не
    > выйдет,

    Останавливать целевой поток смысла нет, EnumWindows
    перечислит все окна, созданные во время ее вызова.
    Во-первых, искать окна имеет смысл после WaitForInputIdle.
    Во-вторых, хук нужен на WH_GETMESSAGE, посылаем PostThreadMessage. Если очереди сообщений нет, то он обломится.
    А если все нормально, можно создавать свое окно.

    --
    Regards, LVT.
  • Leonid Troyanovsky © (25.11.09 13:43) [26]

    > SPeller ©   (25.11.09 02:38) [20]

    И еще, SetWindowLong должна вызываться, по-крайней мере,
    из того же процесса, где создано окно, иначе обломится.
    Т.е., из хуковой процедуры.

    --
    Regards, LVT.
  • Leonid Troyanovsky © (25.11.09 13:51) [27]

    > SPeller ©   (25.11.09 05:39) [22]

    Что есть WndProc?
    Кста, вызов DestroyWindow д.б. из потока, создавшего окно.

    --
    Regards, LVT.
  • Игорь Шевченко © (25.11.09 15:49) [28]
    SPeller ©   (25.11.09 11:17) [24]


    > Я в первом топике описал, зачем оно надо. Для организации
    > апартмента и маршалинга ком-вызовов


    Прочитал. На мой взгляд ты хочешь странного, соответственно, граблей на этом пути у тебя будет в изобилии
  • SPeller © (25.11.09 16:21) [29]

    > Leonid Troyanovsky ©   (25.11.09 13:40) [25]


    > Leonid Troyanovsky ©   (25.11.09 13:43) [26]

    Ага, учту.


    > Leonid Troyanovsky ©   (25.11.09 13:51) [27]
    > Что есть WndProc?

    Указатель на оконную процедуру. В ней, и будет маршалинг.


    > Кста, вызов DestroyWindow д.б. из потока, создавшего окно.

    Действительно, верно... Не знал. Слать руками WM_DESTROY?


    > Игорь Шевченко ©   (25.11.09 15:49) [28]
    > Прочитал. На мой взгляд ты хочешь странного, соответственно,
    >  граблей на этом пути у тебя будет в изобилии

    Хочу очень простого - работать с удаленными интерфейсами как это позволяет ком, взяв его плюсы и обойдя его минусы. Основные грабли были с организацией сетевого взаимодействия, особенно со вложенными вызовами (когда клиент вызывает синхронный метод сервера, а сервер при этом вызывает синхронный метод клиента и т.д.). Но это уже побеждено и нуждается лишь в шлифовке. Даже маршалинг интерфейсов, ради которого пришлось перехватывать системные вызовы Api, оказался не столь сложной задачей (спасибо исходникам ReactOS), поскольку времени на отладку многопоточного клиент-сервера ушло гораздо больше. Реализовать различные потоковые модели и инстансинг и будет почти полный аналог стандартного кома. Сейчас все удаленные вызовы у меня идут через tcp, нужно реализовать еще транспорт на пайпах чтобы внутри одной машины работать, а внутри одного процесса так вообще делать обычные вызовы в апартмент или прямо в объект. Главное - что это интересно и практически полезно, и уже применяется.

    Да и вообще, грабли это не повод не решать задачу. Зато опыт будет :)
  • Игорь Шевченко © (25.11.09 16:33) [30]

    > Хочу очень простого - работать с удаленными интерфейсами
    > как это позволяет ком, взяв его плюсы и обойдя его минусы


    ком позволяет работать очень просто - запускает при обращении к объекту заглушку на клиенте, которая обращается к заглушке сервера на удаленном компьютере. При этом никаких внедрений в чужие неизвестно кем написаные потоки он не производит, ну и маршаллинг, собственно, производится исключительно между заглушками. Мне кажется, в твоем случае все решается правкой таблиц импорта (вместо ole32 на spl32) и реализацией заглушек.

    Кроме того, на rsdn был ряд статей про перехват вызовов COM

    Я бы так делал.
  • Leonid Troyanovsky © (25.11.09 23:07) [31]

    > SPeller ©   (25.11.09 16:21) [29]

    > Ага, учту.

    Надеюсь.

    > > Что есть WndProc?
    > Указатель на оконную процедуру. В ней, и будет маршалинг.

    Надо бы посмотреть.

    > Действительно, верно... Не знал. Слать руками WM_DESTROY?

    Слать особого смысла не вижу.
    Лучше, чтобы ненужное рушил хозяин.

    --
    Regards, LVT.
  • SPeller © (26.11.09 01:49) [32]

    > Игорь Шевченко ©   (25.11.09 16:33) [30]
    rsdn читал, всё не то :) Заглушки используются не только когда клиент и сервер разнесены, но и когда клиент и сервер в разных апартментах. Через заглушки сейчас всё и реализовано. С прокси/стабами рабозрался :) Перехваты апи для маршалинга интерфейсов, передаваемых в параметрах вызовов, решаю через правку таблиц импорта. А внедряться ком-у никуда не надо только потому, что он имеет свой коллбэк в системной очереди сообщений потока, а мне чтобы это повторить нужно извернуться.


    > Надо бы посмотреть
    120 кб выложить? :)


    > Лучше, чтобы ненужное рушил хозяин
    Это понятно, просто вдруг ошибка, чтобы окно не висело.
  • SPeller © (26.11.09 05:01) [33]
    Где у винды в системной очереди выход на ком - достоверно не известно, но в ReactOS есть отдельное оконное сообщение, отсылаемое окну апартмента, при получении которого система без обращения к пользовательскому коду вызывает процедуру маршалинга. Думаю, что в виндах аналогично. В моем случае будет то же самое, только цепочка между приходом сообщения и началом маршалинга будет немного длиннее из-за цикла выборки сообщений в приложении, но разница будет не существенна.
  • SPeller © (26.11.09 05:31) [34]
    От перечисления окон вообще отказался, можно ведь иметь очередь и не иметь окон.

    function CreateWndInThread(idTarget: Cardinal; WndProc: Pointer): HWND;

    implementation

    var
     HookCS: TRTLCriticalSection;
     HHK: HHOOK;
     TargetThreadID: Cardinal;
     NewWnd: HWND;
     NewWndCreating: Boolean;
     Event: THandle;

    function HookMsgProc(nCode, wParam, lParam: Integer): Integer; stdcall;
    begin
     if
       (nCode >= HC_ACTION) and (NewWnd = 0) and not NewWndCreating and
       (TargetThreadID <> 0) and (Event <> 0) and (GetCurrentThreadId = TargetThreadID)
     then
     begin
       NewWndCreating := True;
       NewWnd :=
         CreateWindow(
           'STATIC', 'Test window', WS_VISIBLE, 0, 0, 200, 200, 0, 0, HInstance, nil);
       NewWndCreating := False;
       SetEvent(Event);
     end;
     Result := CallNextHookEx(HHK, nCode, wParam, lParam);
    end;

    function CreateWndInThread(idTarget: Cardinal; WndProc: Pointer): HWND;
    var
     r: Integer;
    begin
     if (WaitForInputIdle(GetCurrentProcess, INFINITE) = WAIT_FAILED) then
       raise Exception.Create('F*ck');
     Result := 0;
     HookCS.Enter;
     TargetThreadID := idTarget;
     try
       try
         try
           Event := CreateEvent(nil, False, False, nil);
           HHK := SetWindowsHookEx(WH_GETMESSAGE, @HookMsgProc, 0, idTarget);
           r := Integer(PostThreadMessage(idTarget, WM_USER + 1, 0, 0));
           if (r = 0) or (r = ERROR_INVALID_THREAD_ID) then
             raise Exception.Create('Target thread has no message queue or invalid thread ID specified');
           WaitForSingleObject(Event, 30000);
           if (NewWnd = 0) then
             raise Exception.Create('Failed to create window');
           Result := NewWnd;
           SetWindowLong(Result, GWL_WNDPROC, Integer(WndProc));
         finally
           CloseHandle(Event);
           Event := 0;
           TargetThreadID := 0;
           UnhookWindowsHookEx(HHK);
           HHK := 0;
         end;
       except
         SendMessage(NewWnd, WM_DESTROY, 0, 0);
         raise;
       end;
     finally
       NewWnd := 0;
       HookCS.Leave;
     end;
    end;




    > Leonid Troyanovsky ©   (25.11.09 23:07) [31]
    > > SPeller ©   (25.11.09 16:21) [29]
    > > > Что есть WndProc?
    > > Указатель на оконную процедуру.
    > Надо бы посмотреть.
    Пока простая тестовая заглушка:

    function MyWndProc(hwnd: HWND; uMsg, wParam, lParam: Integer): Integer; stdcall;
    begin
     if (tmp = 0) and (GetCurrentThreadId = MainThreadID) then
     begin
       tmp := 1;
       MessageBox(Form1.Handle, 'Work!!!', nil, 0);
     end;
     Result := DefWindowProc(hwnd, uMsg, wParam, lParam);
    end;

  • Игорь Шевченко © (26.11.09 14:06) [35]
    SPeller ©   (26.11.09 01:49) [32]


    > А внедряться ком-у никуда не надо только потому, что он
    > имеет свой коллбэк в системной очереди сообщений потока


    Что за Callback "в системной очереди сообщений потока" ?

    Какое отношение к очереди сообщений имеют вообще какие-то callback-и ?
    очередь сообщений потока - это то, откуда ты выбираешь сообщения по GetMessage,PeekMessage, также на выборку сообщений можно ставить хук.


    > Заглушки используются не только когда клиент и сервер разнесены,
    >  но и когда клиент и сервер в разных апартментах


    Не вижу ничего принципиального - ком через границы процессов не ходит, а на одном компьютере эти процессы или на разных - несущественно, разве что протокол обмена может изменяться (а может и не изменяться)
  • SPeller © (26.11.09 16:28) [36]

    > очередь сообщений потока - это то, откуда ты выбираешь сообщения
    Система при появлении в очереди сообщения маршалинга смотрит есть ли апартмент, если есть - отдает ему сообщение и не тревожит пользовательский цикл выборки, т.е. WaitMessage и GetMessage продолжают ожидать сообщения, поэтому хуки ставить туда бесполезно. Жить же без своего окна с одним хуком и фильтровать весь поток для всех окон считаю лишним и потенциально опасным. Слать сообщения конкретному окну апартмента, имхо, идеальный вариант.


    > ком через границы процессов не ходит
    Это уже дком и рпц, отдельная песня, их задача пенедать некий буфер данных на другой конец. Стандартные при этом обязательно попортят нервы с безопасностью.
  • Игорь Шевченко © (26.11.09 16:35) [37]

    > Система при появлении в очереди сообщения маршалинга смотрит
    > есть ли апартмент, если есть - отдает ему сообщение и не
    > тревожит пользовательский цикл выборки


    подробности где-то есть ?
  • Leonid Troyanovsky © (26.11.09 19:32) [38]
    Приветствую всех!

    Вот обещанное:
    --mydll.dpr--

    library mydll;

    // Запускаем путем rundll32.exe "mydll.dll",Main
    // возможно, что с полными путями. Проще из IDE - Run parameters.
    // В эдит - имя окна целевого процесса (должен быть запущен)
    // Для разрушения окна нужно в листбоксе выделить хендл.

    {$R 'mydll.res' 'mydll.rc'}

    uses
     Windows,
     Messages,
     myconsts in 'myconsts.pas',
     myhook in 'myhook.pas',
     mywnd in 'mywnd.pas';

    function DlgProc(Dialog: HWnd; msg:UINT; aWParam: WParam; aLParam: LParam):
     Bool; stdcall;
    var
     wnd, lbwnd: HWND;
     buf: array [0..255] of Char;
     idx, code: Longint;
     s: String;
     tid: DWORD;
    begin
       Result := False;
       case msg of
         WM_SYSCOMMAND:
           if (aWParam and $FFF0 = SC_CLOSE) then
             DestroyWindow(Dialog);
         WM_COMMAND:
           case LoWord(aWParam) of
             IDBtnDone1:
               begin
                 Result := True;
                 if GetDlgItemText(Dialog, IdEdit, buf,SizeOf(buf)) = 0 then
                   begin
                     MessageBox(Dialog, 'Empty name', nil, 0);
                     Exit;
                   end;
                 wnd := FindWindow(nil, buf);
                 if wnd = 0 then
                   begin
                     MessageBox(Dialog, 'Invalid window name', nil, 0);
                     Exit;
                   end;
                 tid := GetWindowThreadProcessId(wnd, nil);
                 CreateWndInThread(tid, Dialog);
                end;
             IDBtnDone2:
               begin
                 Result := True;
                 lbwnd := GetDlgItem(Dialog, IdList);
                 idx := SendMessage(lbwnd, LB_GETCURSEL, 0, 0);
                 SendMessage(lbwnd, LB_GETTEXT, idx, LParam(@buf));
                 Val(buf, wnd, code);
                 if code <> 0 then
                   begin
                     MessageBox(Dialog, 'Invalid value', nil, 0);
                     Exit;
                   end;
                 DestroyWnd(wnd);
                 SendMessage(lbwnd, LB_DELETESTRING, idx, 0);
               end;
           end;
         WM_DESTROY:
           PostQuitMessage(0);
         WM_USER+1:
           begin
             Result := True;
             lbwnd := GetDlgItem(Dialog, IdList);
             Str(aLParam, s);
             SendMessage(lbwnd, LB_ADDSTRING, 0, LPARAM(s));
           end;
       end;
    end;

    procedure Main( wnd: HWND;
                   hinst: THandle;
                   CmdLine: PChar;
                   nCmdShow: Longint); stdcall;
    var
     dlg: HWND;
     msg: TMsg;
    begin
     dlg := CreateDialog ( Hinstance,
                           'Dialog_1',
                           0,
                           @DlgProc);
     if dlg <> 0 then
       begin
         while GetMessage(msg, 0, 0, 0) do
           if not IsDialogMessage(dlg, msg) then
             begin
               TranslateMessage(msg);
               DispatchMessage(msg);
             end;
       end;
    end;

    exports
     Main;

    begin
    end.


    --EOF mydll.dpr--

    --myconsts.pas--

    unit myconsts;

    interface

    const
     IdBtnDone1    = 20001;
     IdBtnDone2    = 20002;
     IdEdit        = 20003;
     IdList        = 20004;

    implementation

    end.


    --EOF myconsts.pas--

    --mydllres.rc--

    #include "myconsts.pas"
    DIALOG_1 DIALOG 6, 15, 184, 163
    STYLE WS_POPUP|WS_VISIBLE|WS_CAPTION|WS_SYSMENU|WS_EX_STATICEDGE
    CAPTION "Create window"
    FONT 8, "Tahoma"
    {
     PUSHBUTTON "Create", IDBtnDone1, 11, 139, 50, 14
     PUSHBUTTON "Destroy", IDBtnDone2, 124, 139, 50, 14
     CONTROL "Калькулятор", IDEdit, EDIT, WS_CHILD|WS_BORDER|WS_TABSTOP|ES_NOHIDESEL,10, 10,165,12
     CONTROL "", IDList, LISTBOX, WS_CHILD|WS_BORDER|WS_TABSTOP, 10, 24, 165, 100
    }



    --EOF mydllres.rc--

    Продолжение следует.

    --
    Regards, LVT.
  • Leonid Troyanovsky © (26.11.09 19:33) [39]
    Окончание.

    --mywnd.pas--
    unit mywnd;

    interface

    uses
     Windows,
     Messages;

    const
     AppName = 'PascalWindowClass';

    function ShowWnd: HWND;

    implementation

    function WindowProc( Wnd: HWND; AMessage: UINT; aWParam: WPARAM;
                        aLParam: LPARAM): LRESULT; stdcall;
    var
     buffer : array [0..MAX_PATH] of Char;
    begin
     case AMessage of
       WM_DESTROY:
         begin
           Result := 0;
           Exit;
         end;
       WM_USER+1:
         begin
           GetModuleFileName(0, buffer, SizeOf(buffer));
           SetWindowText(wnd, buffer);
         end;
     end;
     Result := DefWindowProc(Wnd, AMessage, aWParam, aLParam);
    end;

    function WinRegister: Boolean;
    var
     WindowClass: TWndClass;
    begin
     WindowClass.Style := CS_HREDRAW or CS_VREDRAW;
     WindowClass.lpfnWndProc := @WindowProc;
     WindowClass.cbClsExtra := 0;
     WindowClass.cbWndExtra := 0;
     WindowClass.hInstance := HInstance;
     WindowClass.hIcon := LoadIcon(0, IDI_APPLICATION);
     WindowClass.hCursor := LoadCursor(0, IDC_ARROW);
     WindowClass.hbrBackground := HBrush(Color_Window);
     WindowClass.lpszMenuName := nil;
     WindowClass.lpszClassName := AppName;
     Result := RegisterClass(WindowClass) <> 0;
    end;

    function ShowWnd: HWND;
    begin
     Result := CreateWindow( AppName,
                             'PlainWindow',
                             WS_OVERLAPPEDWINDOW,
                             100, 100, 300, 200,
                             0,
                             0,
                             HInstance,
                             nil);
     if Result = 0 then
       begin
         MessageBox(0, 'CreateWindow failed', nil, mb_Ok);
         Exit;
       end;
     ShowWindow(Result, SW_SHOW);
    end;

    initialization
     if not WinRegister then
       MessageBox(0, 'Register failed', nil, mb_Ok);
    finalization
     UnregisterClass(appname, Hinstance);
    end.


    --EOF mywnd.pas--

    --myhook.pas--

    unit myhook;

    interface

    uses
     Windows,
     Messages,
     mywnd;

    procedure CreateWndInThread(tid: DWORD; Caller: HWND);
    procedure DestroyWnd(wnd: HWND);

    implementation

    function Done( nCode: Integer; wprm: WParam; lprm: LParam):LResult;
            stdcall;
    type
     PMsg = ^TMsg;
    var
     buffer : array [0..MAX_PATH] of Char;
     msg : PMsg;
     Caller : HWND;
     AHook: HHook;
     wnd: HWND;
     lib: THandle;
    begin
     Result := 0;
     msg := PMsg(lprm);
     if (msg.Message = 0) and (msg.LParam <> 0) then
       begin
         AHook := msg.lParam;
         Caller := msg.wParam;
         if (Caller <> 0) then
           begin
             GetModuleFileName(Hinstance, buffer, SizeOf(buffer));
             LoadLibrary(buffer);
             wnd := ShowWnd;
             SendMessage(wnd, WM_USER+1, 0, 0);
             SendMessage(Caller, WM_USER+1, 0, wnd);
           end
         else
           begin
             wnd := msg.hwnd;
             if wnd <> 0 then
               begin
                 DestroyWindow(wnd);
                 GetModuleFileName(Hinstance, buffer, SizeOf(buffer));
                 lib := GetModuleHandle(buffer);
                 if lib <> 0 then
                   FreeLibrary(lib);
               end;
           end;
         UnHookWindowsHookEx(AHook);
         PostThreadMessage(GetCurrentThreadID, 0, 0, 0);
       end;
    end;

    procedure DestroyWnd(wnd: HWND);
    var
     AHook: HHOOK;
     tid: DWORD;
    begin
     tid := GetWindowThreadProcessId(wnd);
     AHook := SetWindowsHookEx(WH_GETMESSAGE, Done, Hinstance, tid);
     if AHook <> 0 then
       PostMessage(wnd, 0, 0, AHook);
    end;

    procedure CreateWndInThread(tid: DWord; Caller: HWND);
    var
     AHook: HHOOK;
    begin
     AHook := SetWindowsHookEx(WH_GETMESSAGE, Done, Hinstance, tid);
     if AHook <> 0 then
       PostThreadMessage(tid, 0, Caller, AHook);
    end;

    end.


    --EOF myhook.pas--

    Всего 5 файлов.

    --
    Regards, LVT.
 
Конференция "WinAPI" » Как заставить GUI thread создать нужное мне окно?
Есть новые Нет новых   [134431   +15][b:0][p:0.01]