Delphi 10.2: 32bit: форма не обрабатывает сообщения SHELLHOOK, почему бы и нет?

Проблема решена, обратитесь к моему ответу, однако не можете принять ее прямо сейчас, поскольку правило 2 дневного переполнения стека. Спасибо за вклад!

edit: ответ удаляется, ответ заключается в удалении строки:

function  registerWindowMessage( lpString : PChar ) : integer; stdcall; external user32 name 'RegisterWindowMessageA'; 

Из проекта, потому что он уже определен в файлах api delphi windows, вот и все. Не нужно переопределять его, а переопределение не соответствует новой версии.


Я пытаюсь оживить / перенести некоторые старые проекты Delphi 5 Enterprise (32bit) в новую / современную версию Delphi (Delphi 10.2, 32bit), однако старые версии компилируются и работают нормально на любой ОС. В целом, довольно совместимо.

Теперь я столкнулся с этой странной проблемой, форма Delphi 10.2 не любит обрабатывать SHELLHOOKсообщения, более старая скомпилированная версия Delphi 5. Потому что у меня нет источника Delphi 10.2 (free edition) forms.pas. Я не вижу, что происходит на самом деле (не так) и не может понять, почему он не работает. Не удалось отладить его.

Регистрация крюка кажется прекрасной, на writelnэкране FormCreateотображаются следующие значения (в дополнительном окне консоли):

что видим

Однако overrided WndProcпроцедура не обрабатывает никакие сообщения в shellhook. Я сделал демоверсию, поэтому вы можете попробовать ее самостоятельно, создав новый проект, дважды щелкните по форме onCreateи onDestroyсобытию и замените код формы следующим:


unit main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

const
  // Constant for shell hook events
 HSHELL_WINDOWCREATED = 1;
 HSHELL_WINDOWDESTROYED = 2;
 HSHELL_ACTIVATESHELLWINDOW = 3;
 HSHELL_WINDOWACTIVATED = 4;
 HSHELL_GETMINRECT = 5;
 HSHELL_REDRAW = 6;
 HSHELL_TASKMAN = 7;
 HSHELL_LANGUAGE = 8;
 HSHELL_ACCESSIBILITYSTATE = 11;


type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormActivate(Sender: TObject);
  private
    { Private declarations }
    FHookMsg : integer;

    procedure WMShellHook(var Msg: TMessage );


  protected
    procedure WndProc(var Msg : TMessage); override;

  public
    { Public declarations }
  end;

var
  Form1: TForm1;


 // Not implemented Windows API functions, available at WinXP and later
function  registerWindowMessage( lpString : PChar ) : integer; stdcall; external user32 name 'RegisterWindowMessageA';
function  registerShellHookWindow( hWnd : THandle ) : bool;    stdcall; external user32 name 'RegisterShellHookWindow';
function  deregisterShellHookWindow( hWnd : THandle ) : bool;  stdcall; external user32 name 'DeregisterShellHookWindow';


implementation

{$R *.dfm}

procedure TForm1.FormActivate(Sender: TObject);
begin
   // send a message
  sendMessage( handle, WM_USER+$40, 1, 2 );
  postMessage( handle, WM_USER+$40, 3, 4 );
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 writeln( handle );
 FHookMsg:=registerWindowMessage('SHELLHOOK'+#0 );
 writeln( FHookMsg );
 writeln( registerShellHookWindow( handle ) );
 writeln( handle );  // handle still the same
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  deregisterShellHookWindow( handle );
  writeln( handle ); // set breakpoint here, handle still the same
end;

procedure TForm1.FormShow(Sender: TObject);
begin
 writeln( handle ); // handle still the same
end;

procedure TForm1.WndProc(var Msg : TMessage);
begin
 // writeln( handle );  even when i showed this, handle is still the same
 if( Msg.Msg = WM_USER+$40 ) then
  begin
   writeln( 'wParam is: ', Msg.wParam );
   writeln( 'lParam is: ', Msg.lParam );
   exit;
  end;

 if( Msg.Msg = FHookMsg ) then
  begin
     // Not executed in Delphi 10.2 generated exe
    writeln( 'wParam is: ', Msg.wParam );
    WMShellHook( Msg );
    exit;
  end;

  inherited; // call this for default behaviour
end;

procedure TForm1.WMShellHook( var Msg: TMessage );
begin
 // Simple however effective way to detect window changes at low costs.
  if( Msg.wparam = HSHELL_WINDOWCREATED )
    or ( Msg.wparam = HSHELL_WINDOWDESTROYED )
     or ( Msg.wparam = HSHELL_WINDOWACTIVATED ) then
      begin
        // Not executed in Delphi 10.2 generated exe
       writeln('here' );
      end;
end;



end.

PS: Не забудьте переключить вариант компоновщика «сгенерировать консольное приложение», чтобы избежать ошибок writeln во время выполнения этой демонстрации.


Может кто-нибудь сказать, что происходит, и почему это не работает?



EDIT: Пример с allocateHwndи deallocateHwnd, ничего не получает. Почему бы и нет? Далее следует этот пример.

unit unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

const
  // Constant for shell hook events
 HSHELL_WINDOWCREATED = 1;
 HSHELL_WINDOWDESTROYED = 2;
 HSHELL_ACTIVATESHELLWINDOW = 3;
 HSHELL_WINDOWACTIVATED = 4;
 HSHELL_GETMINRECT = 5;
 HSHELL_REDRAW = 6;
 HSHELL_TASKMAN = 7;
 HSHELL_LANGUAGE = 8;
 HSHELL_ACCESSIBILITYSTATE = 11;


type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    FHookWndHandle : THandle;
    FHookMsg       : integer;

    procedure WMShellHook(var Msg: TMessage );

  protected
    procedure WndMethod(var Msg: TMessage);

  public
    { Public declarations }
  end;

var
  Form1: TForm1;


 // Not implemented Windows API functions, available at WinXP and later
function  registerWindowMessage( lpString : PChar ) : integer; stdcall; external user32 name 'RegisterWindowMessageA';
function  registerShellHookWindow( hWnd : THandle ) : bool;    stdcall; external user32 name 'RegisterShellHookWindow';
function  deregisterShellHookWindow( hWnd : THandle ) : bool;  stdcall; external user32 name 'DeregisterShellHookWindow';


implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
 FHookWndHandle:=allocateHWnd(WndMethod);
 FHookMsg:=registerWindowMessage('SHELLHOOK'+#0 );
 writeln( FHookMsg );
 writeln( registerShellHookWindow( FHookWndHandle ) );
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  deregisterShellHookWindow( FHookWndHandle );
  deallocateHWnd( FHookWndHandle );
end;


procedure TForm1.WndMethod(var Msg: TMessage);
begin
 if( Msg.Msg = FHookMsg ) then
  begin
     // Not executed in Delphi 10.2 generated exe
    writeln( 'wParam is: ', Msg.wParam );
    WMShellHook( Msg );
    exit;
  end;
end;

procedure TForm1.WMShellHook( var Msg: TMessage );
begin
 // Simple however effective way to detect window changes at low costs.
  if( Msg.wparam = HSHELL_WINDOWCREATED )
    or ( Msg.wparam = HSHELL_WINDOWDESTROYED )
     or ( Msg.wparam = HSHELL_WINDOWACTIVATED ) then
      begin
        // Not executed in Delphi 10.2 generated exe
       writeln('here' );
      end;
end;



end.

windows,forms,delphi,winapi,delphi-10.2-tokyo,

-3

Ответов: 2


Я пытаюсь оживить / перенести некоторые старые проекты Delphi 5 Enterprise (32bit) в новую / современную версию Delphi (Delphi 10.2, 32bit), однако старые версии компилируются и работают нормально на любой ОС. В целом, довольно совместимо.

Теперь я столкнулся с этой странной проблемой, форма Delphi 10.2 не любит обрабатывать SHELLHOOKсообщения, более старая скомпилированная версия Delphi 5. Потому что у меня нет источника Delphi 10.2 (free edition) forms.pas. Я не вижу, что происходит на самом деле (не так) и не может понять, почему он не работает. Не удалось отладить его.

Регистрация крюка кажется прекрасной, на writelnэкране FormCreateотображаются следующие значения (в дополнительном окне консоли):

что видим

Однако overrided WndProcпроцедура не обрабатывает никакие сообщения в shellhook. Я сделал демоверсию, поэтому вы можете попробовать ее самостоятельно, создав новый проект, дважды щелкните по форме onCreateи onDestroyсобытию и замените код формы следующим:


unit main;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

const
  // Constant for shell hook events
 HSHELL_WINDOWCREATED = 1;
 HSHELL_WINDOWDESTROYED = 2;
 HSHELL_ACTIVATESHELLWINDOW = 3;
 HSHELL_WINDOWACTIVATED = 4;
 HSHELL_GETMINRECT = 5;
 HSHELL_REDRAW = 6;
 HSHELL_TASKMAN = 7;
 HSHELL_LANGUAGE = 8;
 HSHELL_ACCESSIBILITYSTATE = 11;


type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
    procedure FormShow(Sender: TObject);
    procedure FormActivate(Sender: TObject);
  private
    { Private declarations }
    FHookMsg : integer;

    procedure WMShellHook(var Msg: TMessage );


  protected
    procedure WndProc(var Msg : TMessage); override;

  public
    { Public declarations }
  end;

var
  Form1: TForm1;


 // Not implemented Windows API functions, available at WinXP and later
function  registerWindowMessage( lpString : PChar ) : integer; stdcall; external user32 name 'RegisterWindowMessageA';
function  registerShellHookWindow( hWnd : THandle ) : bool;    stdcall; external user32 name 'RegisterShellHookWindow';
function  deregisterShellHookWindow( hWnd : THandle ) : bool;  stdcall; external user32 name 'DeregisterShellHookWindow';


implementation

{$R *.dfm}

procedure TForm1.FormActivate(Sender: TObject);
begin
   // send a message
  sendMessage( handle, WM_USER+$40, 1, 2 );
  postMessage( handle, WM_USER+$40, 3, 4 );
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
 writeln( handle );
 FHookMsg:=registerWindowMessage('SHELLHOOK'+#0 );
 writeln( FHookMsg );
 writeln( registerShellHookWindow( handle ) );
 writeln( handle );  // handle still the same
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  deregisterShellHookWindow( handle );
  writeln( handle ); // set breakpoint here, handle still the same
end;

procedure TForm1.FormShow(Sender: TObject);
begin
 writeln( handle ); // handle still the same
end;

procedure TForm1.WndProc(var Msg : TMessage);
begin
 // writeln( handle );  even when i showed this, handle is still the same
 if( Msg.Msg = WM_USER+$40 ) then
  begin
   writeln( 'wParam is: ', Msg.wParam );
   writeln( 'lParam is: ', Msg.lParam );
   exit;
  end;

 if( Msg.Msg = FHookMsg ) then
  begin
     // Not executed in Delphi 10.2 generated exe
    writeln( 'wParam is: ', Msg.wParam );
    WMShellHook( Msg );
    exit;
  end;

  inherited; // call this for default behaviour
end;

procedure TForm1.WMShellHook( var Msg: TMessage );
begin
 // Simple however effective way to detect window changes at low costs.
  if( Msg.wparam = HSHELL_WINDOWCREATED )
    or ( Msg.wparam = HSHELL_WINDOWDESTROYED )
     or ( Msg.wparam = HSHELL_WINDOWACTIVATED ) then
      begin
        // Not executed in Delphi 10.2 generated exe
       writeln('here' );
      end;
end;



end.

PS: Не забудьте переключить вариант компоновщика «сгенерировать консольное приложение», чтобы избежать ошибок writeln во время выполнения этой демонстрации.


Может кто-нибудь сказать, что происходит, и почему это не работает?



EDIT: Пример с allocateHwndи deallocateHwnd, ничего не получает. Почему бы и нет? Далее следует этот пример.

unit unit1;

interface

uses
  Winapi.Windows, Winapi.Messages, System.SysUtils, System.Classes, Vcl.Graphics,
  Vcl.Controls, Vcl.Forms, Vcl.Dialogs;

const
  // Constant for shell hook events
 HSHELL_WINDOWCREATED = 1;
 HSHELL_WINDOWDESTROYED = 2;
 HSHELL_ACTIVATESHELLWINDOW = 3;
 HSHELL_WINDOWACTIVATED = 4;
 HSHELL_GETMINRECT = 5;
 HSHELL_REDRAW = 6;
 HSHELL_TASKMAN = 7;
 HSHELL_LANGUAGE = 8;
 HSHELL_ACCESSIBILITYSTATE = 11;


type
  TForm1 = class(TForm)
    procedure FormCreate(Sender: TObject);
    procedure FormDestroy(Sender: TObject);
  private
    { Private declarations }
    FHookWndHandle : THandle;
    FHookMsg       : integer;

    procedure WMShellHook(var Msg: TMessage );

  protected
    procedure WndMethod(var Msg: TMessage);

  public
    { Public declarations }
  end;

var
  Form1: TForm1;


 // Not implemented Windows API functions, available at WinXP and later
function  registerWindowMessage( lpString : PChar ) : integer; stdcall; external user32 name 'RegisterWindowMessageA';
function  registerShellHookWindow( hWnd : THandle ) : bool;    stdcall; external user32 name 'RegisterShellHookWindow';
function  deregisterShellHookWindow( hWnd : THandle ) : bool;  stdcall; external user32 name 'DeregisterShellHookWindow';


implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
 FHookWndHandle:=allocateHWnd(WndMethod);
 FHookMsg:=registerWindowMessage('SHELLHOOK'+#0 );
 writeln( FHookMsg );
 writeln( registerShellHookWindow( FHookWndHandle ) );
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  deregisterShellHookWindow( FHookWndHandle );
  deallocateHWnd( FHookWndHandle );
end;


procedure TForm1.WndMethod(var Msg: TMessage);
begin
 if( Msg.Msg = FHookMsg ) then
  begin
     // Not executed in Delphi 10.2 generated exe
    writeln( 'wParam is: ', Msg.wParam );
    WMShellHook( Msg );
    exit;
  end;
end;

procedure TForm1.WMShellHook( var Msg: TMessage );
begin
 // Simple however effective way to detect window changes at low costs.
  if( Msg.wparam = HSHELL_WINDOWCREATED )
    or ( Msg.wparam = HSHELL_WINDOWDESTROYED )
     or ( Msg.wparam = HSHELL_WINDOWACTIVATED ) then
      begin
        // Not executed in Delphi 10.2 generated exe
       writeln('here' );
      end;
end;



end.
2-3
4
function  registerWindowMessage( lpString : PChar ) : integer; stdcall;
  external user32 name 'RegisterWindowMessageA';

Это утверждение верно в версиях Delphi в ANSI, но неверно в Unicode Delphi. В Unicode Delphi вы должны использовать версию W функции. В его версии ваша версия отправляет текст UTF16 функции, ожидающей ANSI, и это несоответствие означает, что функция будет получать неправильное имя сообщения. Исправьте это так:

function  registerWindowMessage( lpString : PChar ) : integer; stdcall; 
  external user32 name 'RegisterWindowMessageW';

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

Также обратите внимание, что тип возврата должен быть UINT. Вы должны изменить это, и тип FHookMsg, хотя это не изменит поведение.


Элементы управления окном VCL подлежат восстановлению окна. Существует множество причин, по которым это может произойти, но дескриптор окна за формой может быть уничтожен и воссоздан в любой момент жизни формы.

Ваш код всегда был неправильным, но, похоже, у вас это получилось. Существует два решения:

  1. Зарегистрируйте и отмените регистрацию крюка в переопределенном CreateWndили DestroyWnd.
  2. Используйте окно без VCL для обработки крючка. Используйте AllocateHWndи DeallocateHWnd.

Лично я считаю, что второй вариант предпочтительнее.


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


1

Измените свое заявление RegisterWindowMessageна следующее:

function RegisterWindowMessage( lpString : PChar ) : integer; stdcall; external user32 name 'RegisterWindowMessageW';
окна, форма, Дельфы, WinAPI, Дельфы-10,2-Токио,
Похожие вопросы