您的位置:首页 > 编程语言 > Delphi

C/C++调用Delphi制作的dll时发现的一些问题

2016-12-09 08:31 465 查看
做了一个网络业务逻辑实体,是用C++编写的dll,内部有一个全局的业务实体,外部准备用Delphi编写的界面程序来控制并显示状态......
 
1. C++的dll中不能用C++风格的导出方式:_declspec(dllexport)来导出函数,要用C风格的导出方式:extern "C" _declspec(dllexport),否则Delphi在加载C++的dll时报错:无法定位函数xxxFun()于xxxDll.dll上。(这里假设只讨论这两种导出方式)不知道delphi能不能调用其他C风格的dll...?
例子:
C++中要这样声明函数,
extern "C" _declspec(dllexport)int Initialize();
Delphi中声明要这样,
Function Initialize():Integer; cdecl; external 'c_test.dll' name 'Initialize';
 
注意,这里的cdecl 是必须的,其他的(如stdcall,pascal...)貌似都不行......似乎它是delphi调用c编写的dll的唯一合法标识,因为这由C++的dll中的_declspec方式决定了,然而在C++中为了能给delphi调用,又不允许使用其他的(如WINAPI,CALLBACK,PASCAL...)导出方式,所以就只能这样使用_declspec了。
 
但是,有人说过这样的对应方式:
C++的参数调用方式          对应的DELPHI的参数调用方式
    _declspec                           cdecl
WINAPI,CALLBACK                 stdcall
      PASCAL                            pascal
附注:C++中的char *对应PASCAL中的PChar。
不是没有试过,只是没有调试成功过,所以最终还是用的以上方式,即在C++中用extern "C" _declspec(dllexport)声明函数,在Delphi中用cdecl声明函数,然后Delphi应用程序调用C++的dll。
 
2.在1中的调用为主动调用,即delphi主动调用C++的dll,倘若反过来,用C++或者C来调用Delphi的函数,问题有些不一致了,这里又要分为两种情形:
1)C++的应用程序,调用Delphi编写的dll。
2)Delphi在调用C++的dll时,传递函数指针给dll,在dll内部根据业务逻辑,回叫这个delphi函数。
(其实1)和2)的情形似乎都一样...)
因为pascal和c是两种不同风格的语言,它们的参数列表的出栈进栈方式不一样,所以,最容易出问题的就是函数的参数列表了。不光参数列表中参数顺序会乱,而且乱得没有规律可循,当只涉及到一个整型参数时,没有问题,正常调用,但是,这只是碰巧没有问题而已......
首先,对于C++应用程序调用Delphi编写的dll,
可以这样,在C++程序里,
#include <Windows.h>
#include <iostream>
#include <stdio.h>
#include <string>
using namespace std;
 
typedef int(*myFun)(int arg1,int arg2,int arg3,int arg4);
 
int main(int argi,char* argv[])
{
HINSTANCE hDll;
 hDll=LoadLibrary("delphi_dll.dll");
 if (hDll!=NULL)
 {
myFun fun;
 fun=(myFun)GetProcAddress(hDll, "c_exe_test2");
 if(fun!=NULL)
 {
       for(int i=0;i<5;i++)
{
              printf("%d,%d,%d,%d\n",i,i+1,i+2,i+3);
fun(i,i+1,i+2,i+3);
}
 }
 FreeLibrary(hDll);
}
 
system("pause");
return 0;
}
 
然后在Delphi编写的dl内部,
library delphi_dll;
 
uses
  SysUtils,
  Classes,
  Dialogs;
 
Function c_exe_test2(arg1,arg2,arg3,arg4: Integer): Integer; cdecl;
Begin
  ShowMessage(
'arg1='+IntToStr(arg1)+'    '+
'arg2='+IntToStr(arg2)+'    '+
'arg3='+IntToStr(arg3)+'    '+
'arg4='+IntToStr(arg4));
end;
 
exports
c_exe_test2 index 1;
 
begin
  //todo...dll被加载时执行一次...
end.
注意了,在Delphi的dll内部,cdecl 声明是必不可少的,否则就会出现参数列表的混乱...
 
接下来,就去调试下C++的dll内部回叫Delphi程序的函数指针,再次检查参数列表是否正确,
在C++的dll中,
//--------------------------------------------------------------------------------
//xxx.h:
typedef int(*OnTest)(int arg1,int arg2,int arg3,int arg4);
extern "C" _declspec(dllexport)int setcb_on_test(OnTest pFun);
 
//--------------------------------------------------------------------------------
//xxx.cpp:
#include<Windows.h>
 
static OnTest CallBackOnTest = NULL;
extern "C" _declspec(dllexport)int setcb_on_test(OnTest pFun)
{
 CallBackOnTest=pFun;
       return 0;
}
 
void fun(void* arg)
{
       int i=0,arg1=1,arg2=2,arg3=3,arg4=4;
       while(1)
{
              i++;
              if(i%5==0)
if(CallBackOnTest)
{
              CallBackOnTest(arg1,arg2,arg3,arg4);
       arg1++;arg2++;arg3++;arg4++
}
              Sleep(1000);
              if(i>=100) break;
       }
}
 
BOOLAPIENTRY DllMain(HANDLE hModule,DWORD ul_reason_for_call,LPVOID lpReserved )
{
 switch(ul_reason_for_call)
 {
 case DLL_PROCESS_ATTACH:
      {
             HANDLE threadHandle; 
             DWORD  threadID;
             threadHandle = CreateThread(
             NULL,
             0,
           (LPTHREAD_START_ROUTINE)fun,
           NULL,
           0,
           &threadID);
      }
  break;
 case DLL_THREAD_ATTACH:
 case DLL_THREAD_DETACH:
 case DLL_PROCESS_DETACH:
  break;
 }
 return TRUE;
}
 
在Delphi应用程序中,
unit UnitMain;
 
interface
 
uses
  Windows, Messages, SysUtils, Variants, Classes, Dialogs;
 
Type
  TObjMain = class(TObject)
  private
{ Private declarations }
  Public
    { Public declarations }
    
Constructor Create(owner: TObject);
Destructor  Free();
  end;
 
var
  myObj: TObjMain ;
 
type
TOnTest = Function(arg1,arg2,arg3,arg4: Integer): Integer; cdecl;
 
Function SetCB_OnTest(pFun: TOnTest): Integer; cdecl; external 'c_test.dll' name 'setcb_on_test';
Function OnTest(arg1,arg2,arg3,arg4: Integer): Integer; cdecl;
implementation
 
{$R *.pas}
 
Function OnTest(arg1,arg2,arg3,arg4: Integer): Integer;
begin
  ShowMessage(
'arg1='+IntToStr(arg1)+'    '+
'arg2='+IntToStr(arg2)+'    '+
'arg3='+IntToStr(arg3)+'    '+
'arg4='+IntToStr(arg4  );
  result:=0;
end;
 
constructor myObj.Create(owner: TObject);
begin
  inherited;
  
  //todo...
  SetCB_OnTest(OnTest);
end;
 
destructor myObj.Free();
begin
  //todo...
  
  inherited;
end;
再在工程文档中,
programd_test;
 
uses
  UnitMain in 'UnitMain.pas' {myObj};
{$R *.res}
Begin
  Application.Initialize;
  myObj:=TObjMain.Create(Application);
  Application.Run;
end.
 
最后编译运行,终于解决了......
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: