-2

I'm totally new in messages handling using the winapi, I have a DLL which will post custom messages under the ID WM_GAS_AP I would like to receive these messages and print them in a memo.

I tried several examples but without success, the currently I have implemented in my code is from this site http://users.telenet.be/ws36637/delphihook.html

I created the unit and added it to my uses, and declared this constant

const
  WM_GAS_AP = WM_USER + 1;

Added this procedure

procedure ProcessHookMessage(Sender: TObject;
  var Message: TMessage; var Handled: Boolean);


procedure TForm1.ProcessHookMessage(Sender: TObject;
  var Message: TMessage; var Handled: Boolean);
begin
  if Message.Msg = WM_GAS_AP then
   begin
     memo1.lines.add('I received a message!');
   end;
end;

But it doesn't catch anything.

EDIT:

This comes with a C++ sample which unafortunately I wasn't be able to compile. In that example I found a section which shows this:

Check the end please.

private:    // ユーザー宣言
    // 変数
    HINSTANCE hDLL;
    UINT iComPort;
    CHAR RCV_BUFF[512];
    TCHAR ALARM_BUFF[256];
    TLaserFalconTimer* pLF_Timer;

    // 初期化関連関数
    int __fastcall TLaserFalconInitialize(void);
    int __fastcall TLaserFalconIniLoad(void);

    // DLL関連関数
    int __fastcall TLaserFalconGasViewDllMeasStart(void);
    int __fastcall TLaserFalconGasViewDllMeasStop(void);

    // 表示関連関数
    void __fastcall TLaserFalconSetAlarmLevel(void);

    // イベント受信用関数
    void __fastcall MsgFromThrCommGasView(TMessage Message);

    BEGIN_MESSAGE_MAP
        MESSAGE_HANDLER(WM_GAS_AP,  TMessage, MsgFromThrCommGasView)
    END_MESSAGE_MAP(TForm)

From the DLL documentation it says:

DLL Docs

So at the first time when attempting to stablish the connection with the DLL:

Declared this function:

function GasViewInit(HWnd:HWND;DataLen:Byte;BaudRate:Byte;Parity:Byte;ComPort:Byte): Integer; stdcall; external 'GasView.dll';

And passed the windows handle as param to the DLL. It succesful connect.

procedure TForm1.Button1Click(Sender: TObject);
var
  ret:Integer;
  iComPort:Integer;
begin
  iComPort:=13;
  ret:=-100;
  ret:=GasViewInit(Handle,1,3,0,iComPort);
  if ret=0 then
    begin
      ret:=-100;
      ret:=GasViewNego();
      if ret=0 then
        begin
          Button2.Enabled:=True;
          Memo1.Lines.Add('Connected');
        end
      else
        Memo1.Lines.Add('Sensor error '+ IntToStr(ret));
    end
  else
    Memo1.Lines.Add('Library error '+IntToStr(ret));

end;
  • Comments are not for extended discussion; this conversation has been [moved to chat](https://chat.stackoverflow.com/rooms/198696/discussion-on-question-by-martin-ocando-corleone-how-to-handle-custom-messages-c). – Samuel Liew Aug 30 '19 at 02:30

1 Answers1

4

Your ProcessHookMessage() is not called because you have not linked it to anything that is responsible for dispatching messages to it. Also, your definition of the WM_GAS_AP message ID is wrong anyway, it is WM_USER + 100 instead of WM_USER + 1.

In the C++ code, the MESSAGE_MAP handles that linkage, by overriding the Form's virtual Dispatch() method to look for specific messages that the Form's WndProc() method doesn't handle.

In Delphi, you can do the equivalent by using the message specifier on the method declaration itself:

const
  WM_GAS_AP = WM_USER + 100;

procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ProcessDllMessage(var Message: TMessage); message WM_GAS_AP;

...

procedure TForm1.FormCreate(Sender: TObject);
begin
  GasViewInit(Handle, ...);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  GasViewFin;
end;

procedure TForm1.ProcessDllMessage(var Message: TMessage);
begin
  Memo1.Lines.Add('I received a message!');
end;

Or, your can override the Form's virtual WndProc() method:

procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ProcessDllMessage(var Message: TMessage);
procedure WndProc(var Message: TMessage); override;

...

procedure TForm1.FormCreate(Sender: TObject);
begin
  GasViewInit(Handle, ...);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  GasViewFin;
end;

procedure TForm1.ProcessDllMessage(var Message: TMessage);
begin
  Memo1.Lines.Add('I received a message!');
end;

procedure TForm1.WndProc(var Message: TMessage);
const
  WM_GAS_AP = WM_USER + 100;
begin
  if Message.Msg = WM_GAS_AP then
    ProcessDllMessage(Message)
  else
    inherited;
end;

That being said, the Form's Handle is not a good choice of HWND to use, as its lifetime is not constant. It can be recreated dynamically at runtime for a number of reasons, and if that happens then you would have to reinitialize the DLL each time a new HWND is created:

procedure CreateWnd; override;
procedure DestroyWindowHandle; override;

procedure TForm1.CreateWnd;
begin
  inherited;
  GasViewInit(Handle, ...);
end;

procedure TForm1.DestroyWindowHandle;
begin
  GasViewFin;
  inherited;
end;

A better option is use the RTL' s AllocateHWnd() function to create a dedicated window that you control the lifetime of just for the DLL to use:

DllWnd: HWND;
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
procedure ProcessDllMessage(var Message: TMessage);

...

procedure TForm1.FormCreate(Sender: TObject);
begin
  DllWnd := AllocateHWnd(ProcessDllMessage);
  GasViewInit(DllWnd, ...);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  GasViewFin;
  DeallocateHWnd(DllWnd);
end;

procedure TForm1.ProcessDllMessage(var Message: TMessage);
const
  WM_GAS_AP = WM_USER + 100;
begin
  if Message.Msg = WM_GAS_AP then
    Memo1.Lines.Add('I received a message!')
  else
    Message.Result := DefWindowProc(DllWnd, Message.Msg, Message.WParam, Message.LParam);
end;
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • 1
    I know the code in the question names its procedure "ProcessHookMessage", but that's really an unfortunate name and originates from the article linked in the question which use Application.HookMainWindow (the article is not even related to OP's problem really - it searches for a way of communication between two different applications). – Sertac Akyuz Aug 29 '19 at 21:25
  • 1
    @SertacAkyuz I renamed `ProcessHookMessage` to `ProcessDllMessage` – Remy Lebeau Aug 29 '19 at 21:42
  • @RemyLebeau I implemented the last part of your answer, but seems like the dll is not sending the message, instead of, I can get a 6hz output while calling the function `GasViewMeasStart(@buffer)` – Martin Ocando Corleone Aug 29 '19 at 22:43
  • The documentation says: "*While Laser Falcon is measuring, if GasViewMeasStart is run measured data is transferred from Laser Falcon. MsgFromThrCommGasView that is defined on a message map gives the measured data. Measured data is stored in a buffer defined by Start getting measured data API.*" So, that implies that you give the DLL a buffer to store data in, and then the DLL notifies your HWND whenever that buffer has been filled with new data. Are you checking to make sure that `GasViewMeasStart()` is not failing? Are you calling `GasViewInit()` with the allocated HWND? – Remy Lebeau Aug 29 '19 at 22:58