使用消息处理程序将 c++ dll 调用转换为 delphi
Convert c++ dll call to delphi with message handler
我需要在我的delphi XE3应用程序中使用DLL,我收到了一个用c ++编写的演示应用程序,展示了如何调用DLL。
我已经成功地调用了 DLL 和 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 },
初始化方法的定义:
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);
}
调用初始化方法:
res=::Initialize('com3', WM_POSSTATE , &m_hWnd);
if( res )
{
MessageBox( "Error opening comms",NULL,MB_OK);
FreeLibrary( hPosDll );
exit(1);
}
初始化调用后,从 DLL 到应用程序的消息由 OnPos 方法处理:
void CPosDemoDlg::OnPOS(UINT Result, LPARAM Param )
{
DoStuff;
}
在德尔福,我已经走了这么远:
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的设备,但消息处理程序永远不会触发。
我认为在 c++ 中使用 Initialize 方法发送的&m_hWnd
与我在 Delphi 中发送的Self.Handle
不同。
谁能帮我?提前谢谢。
C++函数声明为:
typedef int (INITIALIZE)( char * cPort , UINT Msg, HWND *hWnd_p );
您的德尔福等价物声明为:
TInitialize = function(cport: PAnsiChar; Msg: Integer;
Handle: HWND): Integer; stdcall;
我可以在您的德尔菲声明中看到以下差异:
Msg
参数是无符号的,但德尔菲翻译是有符号的。hWnd_p
参数是指向HWND
的指针。在 Delphi 中,按值传递窗口句柄。- C++码的调用约定是
cdecl
,但你声明了德尔菲码stdcall
。
所以在德尔斐,宣言应该是:
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'))
.
一目了然,它看起来像你的声明
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;
看看你能走多远。
在Hanno和David的帮助下,我改变了我的代码,它现在可以工作了。
我所做的更改:
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);
根据 David Hefferman 的建议,我将在最终应用程序中创建一个单独的组件,这样我将使用 AllocateHwnd
创建一个固定句柄。 ...
- Rcpp并行无匹配函数,用于调用"转换"
- 为什么在std::for_each()返回时调用转换运算符
- 将多个非原始递归调用转换为迭代解决方案
- 在重载解析期间调用转换运算符,而不是在 c++17 中转换构造函数
- 非调用转换函数上的模板中的编译错误
- 为什么在没有赋值运算符的情况下调用转换构造函数
- 调用转换操作员
- 将C++中涉及数组的函数调用转换为C#时出现问题
- 操作员重载内部调用转换
- 为什么在使用初始化语法时不调用转换运算符,为什么 clang 错误消息看起来是错误的
- 使用消息处理程序将 c++ dll 调用转换为 delphi
- Delphi - 从 dll 调用转换(读取)C++ NULL 终止数组
- 如何直接调用转换运算符
- c++.将定义了2个点的绘图贝塞尔曲线调用转换为定义了3个点的调用
- 调用转换操作符不能用于静态const变量
- 在osx/win7/linux上不同的c++调用转换
- 为什么在这里调用转换构造函数
- 将C API调用转换为delphi
- 为move调用转换构造函数,但未为copy调用转换构造函数
- 没有可行的std::weak_ptr到std::shared_ptr的方法调用转换