Преобразование вызова C ++ DLL в Delphi с помощью обработчика сообщений

Мне нужно использовать DLL в моем приложении delphi XE3, я получил демонстрационное приложение, написанное на c ++, которое показывает, как вызывать DLL.
Мне удалось вызвать DLL и метод Initialize библиотеки DLL, но я не получаю никаких сообщений от DLL.
Это источник C ++:
Создание обработчика сообщений:

#define WM_POSSTATE  WM_APP+1
#define ON_WM_POSSTATE() \
{ WM_POSSTATE, 0, 0, 0, AfxSig_vwl, \
(AFX_PMSG)(AFX_PMSGW)(void (AFX_MSG_CALL CWnd::*)(UINT, LPARAM))&OnPOS },

Определение метода Initialize:

typedef int (INITIALIZE)( char * cPort , UINT Msg, HWND *hWnd_p );
INITIALIZE *Initialize;

HMODULE         hPosDll;

Загрузка DLL:

if( (hPosDll = LoadLibrary( "posdll.dll" ) ) == NULL )
{
MessageBox( "Error: can not open posdll.dll",NULL,MB_OK);
exit(1);
}

if( (::Initialize = (INITIALIZE*)GetProcAddress( hPosDll, "Initialize" )) == NULL )
{
MessageBox( "Error: can not find function",NULL,MB_OK);
FreeLibrary( hPosDll );
exit(1);
}

Вызов метода Initialize:

res=::Initialize('com3', WM_POSSTATE , &m_hWnd);

if( res )
{
MessageBox( "Error opening comms",NULL,MB_OK);
FreeLibrary( hPosDll );
exit(1);
}

После вызова Initialize сообщения из DLL в приложение обрабатываются методами OnPos:

void CPosDemoDlg::OnPOS(UINT Result, LPARAM Param )
{
DoStuff;
}

В Delphi я зашел так далеко:

unit UFrmMain;

interface

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

const
WM_POSSTATE = WM_APP + 1;
MCDLL = 'posdll.dll';

type
TInitialize = function(cport: PAnsiChar; Msg: Integer; Handle: HWND):Integer; stdcall;

TFrmMain = class(TForm)
EdCOMPort: TEdit;
BtnConnect: TButton;
LblCOMPort: TLabel;
procedure ON_WM_POSSTATE(var Msg: TMessage); message WM_POSSTATE;
procedure BtnConnectClick(Sender: TObject);
private
DLLHandle: THandle;
public
{ Public declarations }
end;

var
FrmMain: TFrmMain;

implementation

{$R *.dfm}

{-------------------------------------------------------------------------------}
procedure TFrmMain.BtnConnectClick(Sender: TObject);
var
MCInitialize: TInitialize;
Res: Integer;
begin
DLLHandle := LoadLibrary(PChar(MCDLL));

if DLLHandle <> 0 then
begin
@MCInitialize := getProcAddress(DLLHandle, 'Initialize');

if @MCInitialize <> NIL then
begin
Res := MCInitialize(PAnsiChar('com3'), WM_POSSTATE, Self.Handle);

if Res <> 0 then
begin
MessageDlg('Error opening comms', mtWarning, [mbOK], 0);
end;
end;
end
else
begin
MessageDlg('posdll.dll could not be located.', mtWarning, [mbOK], 0);
end;
end;

{-------------------------------------------------------------------------------}
procedure TFrmMain.ON_WM_POSSTATE(var Msg: TMessage);
begin
showmessage('Message received');
end;

end.

С помощью этого решения я могу активировать устройство, подключенное к com3, но обработчик сообщений никогда не запускается.
я думаю &m_hWnd то, что отправлено с помощью метода Initialize в c ++, отличается от Self.Handle Я отправляю в Delphi.
Может кто-нибудь мне помочь? Заранее спасибо.

1

Решение

На первый взгляд, это выглядит как ваша декларация

TInitialize = function(cport: PAnsiChar; Msg: Integer; Handle: HWND):Integer;

не совпадает

typedef int (INITIALIZE)( char * cPort , UINT Msg, HWND *hWnd_p );

Обратите внимание, что объявление C ожидает указатель к переменной, удерживающей ручку.

Пытаться

TInitialize = function(cport: PAnsiChar; Msg: Integer; var Handle: HWND):Integer;

и посмотрим, как далеко вы доберетесь.

1

Другие решения

Функция C ++ объявлена ​​как:

typedef int (INITIALIZE)( char * cPort , UINT Msg, HWND *hWnd_p );

Ваш Delphi-эквивалент объявлен как:

TInitialize = function(cport: PAnsiChar; Msg: Integer;
Handle: HWND): Integer; stdcall;

Я вижу следующие различия в вашей декларации Delphi:

  1. Msg параметр не подписан, но перевод Delphi подписан.
  2. hWnd_p параметр является указателем на HWND, В Delphi вы передаете дескриптор окна по значению.
  3. Соглашение о вызовах кода C ++ cdecl, но вы объявили код Delphi как stdcall,

Таким образом, в Delphi объявление должно быть:

TInitialize = function(cport: PAnsiChar; Msg: Cardinal;
Handle: PHWND): Integer; cdecl;

где PHWND тип объявлен как ^HWND, Я подозреваю, что такой тип объявлен в Windows блок, но если нет, то вы можете легко объявить об этом.

Или, возможно, более простой перевод будет:

TInitialize = function(cport: PAnsiChar; Msg: Cardinal;
var Handle: HWND): Integer; cdecl;

Конечно, вполне вероятно, что вы пропустили соглашение о вызовах при расшифровке кода C ++. Возможно, функция действительно stdcall хотя в заявлении, которое вы поместили в вопросе, ничего не сказано.

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

Способ обойти это — использовать дескриптор окна, который VCL не будет создавать заново. Получить один из тех, позвонив AllocateHwnd,

Будьте осторожны при звонке Initialize, Вы передаете PAnsiChar('com3') как имя порта. Вы должны быть уверены, что буквальное 'com3' действительно в кодировке ANSI. В идеале вы должны пройти 'com3' прямо к Initialize, Если это не скомпилируется (я не уверен, если это будет или нет), тогда передайте PAnsiChar(AnsiString('com3')),

3

С помощью Ханно и Дэвида я изменил свой код, и теперь он работает.

Изменения, которые я сделал:

type
PHWND = ^HWnd;
TInitialize = function(cport: PAnsiChar; Msg: Cardinal; Handle: PHWND):Integer; stdcall;

а также

var
TmpHandle
begin
...
TmpHandle := Self.Handle;
Res := MCInitialize(PAnsiChar(AnsiString('com3')), WM_POSSTATE, @TmpHandle);

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

0
По вопросам рекламы [email protected]