Search code examples
delphidll-injection

Non-focusable child window


I have simple VCL form, this form located in injected dll. Dll injected to process with window. I need to make my form to be non-focusable and always before parent window (main window of the injected process).

Form creation:

procedure CreateForm;
var
  hWindow: THandle;
  Rect: TRect;
begin
  if GetProcessWindowHandle(GetCurrentProcessId, hWindow) then begin
    FormButtons := TFormButtons.Create(nil);
    GetWindowRect(hWindow, Rect);
    FormButtons.Left := Rect.Left + 50;
    FormButtons.Top  := Rect.Top;
    FormButtons.ShowModal;
    FormButtons.Free;
  end;
end;

procedure DLLEntryPoint(dwReason: DWORD);
begin
  case dwReason of
    DLL_PROCESS_ATTACH: begin
                          CreateForm;
                        end;
    DLL_PROCESS_DETACH: begin
                        end;
  end;
end;

begin
  DLLProc := @DLLEntryPoint;
  DLLEntryPoint(DLL_PROCESS_ATTACH);
end.

CreateParams for form:

procedure TFormButtons.CreateParams(var Params: TCreateParams);
var
  hWindow: THandle;
begin
  inherited CreateParams(Params);
  Params.ExStyle := Params.ExStyle or WS_EX_NOACTIVATE;
  if GetProcessWindowHandle(GetCurrentProcessId, hWindow) then
    Params.WndParent := hWindow;
end;

What I have:

  1. With WS_EX_NOACTIVATE and without Params.WndParent := hWindow I have non-focusable my window (TFormButtons), but this is not child window for main window and when main window activated than my window stay under main window. Stay on top is a bad idea, my window should be only before main window.

  2. With WS_EX_NOACTIVATE and wit Params.WndParent := hWindow I have good z-ordering for my child VCL window, this is always before main window, but main window always lost focus when activated my window

  3. And another question: how to show my VCL window without ShowModal but with Show. Without ShowModal this is invisible


Solution

  • Finally I found solution. My form never got focus, placed every time over main form and moved with main form:

    Injected dll:

    var
      hWndHook: HHOOK = 0;
      hWndMain: THandle = 0;
      ProcessId: DWORD = 0;
      ThreadId: DWORD = 0;
    
    function CallWndProc(Code: Integer; wParam: WPARAM; CWPStruct: PCWPStruct): LRESULT; stdcall;
    var
      Rect: TRect;
    begin
      Result := CallNextHookEx(hWndHook, Code, wParam, LPARAM(CWPStruct));
      if (Code = HC_ACTION) then begin
        case CWPStruct.message of
          WM_MOVE: if hWndMain > 0 then begin
            if Assigned(FormButtons) then begin
              GetWindowRect(hWndMain, Rect);
              SetWindowPos(FormButtons.Handle, HWND_TOPMOST, Rect.Left, Rect.Top - 10, 0, 0, SWP_NOACTIVATE or SWP_NOSIZE);
              SetWindowPos(FormButtons.Handle, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE or SWP_NOSIZE or SWP_NOMOVE);
            end;
          end;
          WM_ACTIVATE: if (hWndMain > 0) then begin
            if Assigned(FormButtons) then begin
              SetWindowPos(FormButtons.Handle, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOACTIVATE or SWP_NOSIZE or SWP_NOMOVE);
              SetWindowPos(FormButtons.Handle, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE or SWP_NOSIZE or SWP_NOMOVE);
            end;
          end;
          WM_CLOSE: if (hWndMain > 0) and (hWndMain = CWPStruct.hwnd) then begin
            try
                UnhookWindowsHookEx(hWndHook);
                hWndMain := 0;
            except
              MessageBox(0, 'Error: WM_CLOSE', '', MB_OK);
            end;
          end;
        end;
      end;
    end;
    
    function ThreadProc(Params: Pointer): Integer;
    begin
      Result := 0;
      try
        ProcessId := GetCurrentProcessId;
        if GetProcessWindowHandle(ProcessId, hWndMain) then
          ThreadId := GetWindowThreadProcessId(hWndMain);
    
        if hWndHook = 0 then
          hWndHook := SetWindowsHookEx(WH_CALLWNDPROC, @CallWndProc, HInstance, ThreadId);
    //    MessageBox(0, PChar('Hook installed!' + sLineBreak +
    //                        'Process Id: ' + IntToStr(ProcessId) + sLineBreak +
    //                        'Thread Id: ' + IntToStr(ThreadId) + sLineBreak +
    //                        'hWndMain: ' + IntToStr(hWndMain) + sLineBreak +
    //                        'hWndHook: ' + IntToStr(hWndHook)), '', MB_OK);
    
        FormButtons := TFormButtons.Create(nil);
        FormButtons.ShowModal;
    
        Result := 0;
      except
    //    Result := ERROR_GEN_FAILURE;
      end;
    end;
    
    procedure DLLEntryPoint(dwReason: DWORD);
    var
      hThread: THandle;
      ThreadId: UInt32;
    begin
      case dwReason of
        DLL_PROCESS_DETACH: begin
    //                          MessageBox(0, PChar('DLL_PROCESS_DETACH: ' + IntToStr(ProcessId)), '', MB_OK);
                            end;
        DLL_PROCESS_ATTACH: begin
                              hThread := BeginThread(nil, 0, ThreadProc, nil, 0, ThreadId);
                              if hThread <> 0 then
                                CloseHandle(hThread);
    //                          MessageBox(0, PChar('DLL_PROCESS_ATTACH: ' + IntToStr(GetCurrentProcessId)), '', MB_OK);
                            end;
      end;
    end;
    
    begin
      DLLProc := @DLLEntryPoint;
      DLLEntryPoint(DLL_PROCESS_ATTACH);
    end.
    

    CreateParams for form:

    procedure TFormButtons.CreateParams(var Params: TCreateParams);
    var
      hWindow: THandle;
    begin
      inherited CreateParams(Params);
      Params.ExStyle := Params.ExStyle or WS_EX_NOACTIVATE;
    end;