Delphi写的DLL回调C#

C#的调用Delphi的DLL没有问题,DLL回调时遇到了麻烦,网上找了个方法,解决了这个问题

Delphi部分,列举了三种回调函数定义

  1. library test;  
  2.   
  3. uses  
  4.   SysUtils;  
  5.   
  6. {$R *.res}  
  7.   
  8. type  
  9.   TCallback = procedure (P: PChar); stdcall;  
  10.   TMethodCallback = procedure (P: PChar) of object; stdcall;  
  11.   
  12. procedure DllTest1(Callback: TCallback; P: PChar; I: Integer); stdcall;  
  13. var  
  14.   S: string;  
  15. begin  
  16.   S := Format('DllTest1 ''%s'' %d', [P, I]);  
  17.   if Assigned(Callback) then  
  18.     Callback(PChar(S));  
  19. end;  
  20.   
  21. procedure DllTest2(_Callback: Pointer; P: PChar; I: Integer); stdcall;  
  22. var  
  23.   Callback: TMethodCallback absolute _Callback;  
  24.   S: string;  
  25. begin  
  26.   S := Format('DllTest2 ''%s'' %d', [P, I]);  
  27.   if Assigned(Callback) then  
  28.     Callback(PChar(S));  
  29. end;  
  30.   
  31. procedure DllTest3(Callback: TMethodCallback; P: PChar; I: Integer); stdcall;  
  32. var  
  33.   S: string;  
  34. begin  
  35.   S := Format('DllTest3 ''%s'' %d', [P, I]);  
  36.   if Assigned(Callback) then  
  37.     Callback(PChar(S));  
  38. end;  
  39.   
  40. exports  
  41.   DllTest1,  
  42.   DllTest2,  
  43.   DllTest3;  
  44.   
  45. begin  
  46. end. 
  47.  
  48. C#部分

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Text;  
  4. using System.Runtime.InteropServices;  
  5.   
  6. namespace DllTest  
  7. {  
  8.     class Program  
  9.     {  
  10.         public struct Method  
  11.         {  
  12.             public IntPtr code;  
  13.             public IntPtr data;  
  14.         }  
  15.         [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "DllTest1")]  
  16.         public static extern void DllTest1(IntPtr p, [MarshalAs(UnmanagedType.LPStr)] string s, int i);  
  17.         [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "DllTest2")]  
  18.         public static extern void DllTest2(IntPtr p, [MarshalAs(UnmanagedType.LPStr)] string s, int i);  
  19.         [DllImport("Test.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi, EntryPoint = "DllTest3")]  
  20.         public static extern void DllTest3(Method m, [MarshalAs(UnmanagedType.LPStr)] string s, int i);  
  21.   
  22.         public delegate void Callback([MarshalAs(UnmanagedType.LPStr)] string s);  
  23.         public delegate void MethodCallback(IntPtr self, [MarshalAs(UnmanagedType.LPStr)] string s);  
  24.         public static void ShowInfo(string s)  
  25.         {  
  26.             Console.WriteLine("Info: " + s);  
  27.         }  
  28.         public static void ShowMethodInfo(IntPtr self, string s)  
  29.         {  
  30.             Console.WriteLine("Info: " + s);  
  31.         }  
  32.   
  33.   
  34.         static void Main(string[] args)  
  35.         {  
  36.             Method m;  
  37.             Callback info = ShowInfo;  
  38.             MethodCallback methodInfo = ShowMethodInfo;  
  39.             IntPtr p = Marshal.GetFunctionPointerForDelegate(info);  
  40.             IntPtr pm = Marshal.GetFunctionPointerForDelegate(methodInfo);  
  41.   
  42.             // function callback example  
  43.             DllTest1(p, "test", 42);  
  44.             // method callback example 1  
  45.             DllTest2(pm, "test", 42);  
  46.             // method callback example 2  
  47.             m.code = pm;  
  48.             m.data = IntPtr.Zero;  
  49.             DllTest3(m, "test", 42);  
  50.         }  
  51.     }  
  52. }  
Delphi中制作DLL •一 Dll的制作一般分为以下几步: 1 在一个DLL工程里一个过程或函数 2 一个Exports关键字,在其下过程的名称。不用参数和调用后缀。 二参数传递 1 参数类型最好与window C++的参数类型一致。不要用DELPHI的数据类型。 2 最好有返回值[即使是一个过程],来报出调用成功或失败,或状态。成功或失败的返回值最好为1[成功]或0[失败].一句话,与windows c++兼容。 3 用stdcall声明后缀。 4 最好大小敏感。 5 无须用far调用后缀,那只是为了与windows 16位程序兼容。 三 DLL的初始化和退出清理[如果需要初始化和退出清理] 1 DLLProc[SysUtils单元的一个Pointer]是DLL的入口。在此你可用你的函数替换了它的入口。但你的函数必须符合以下要求[其实就是一个回调函数]。如下: procedure DllEnterPoint(dwReason: DWORD);far;stdcall; dwReason参数有四种类型: DLL_PROCESS_ATTACH:进程进入时 DLL_PROCESS_DETACH进程退出时 DLL_THREAD_ATTACH 线程进入时 DLL_THREAD_DETACH 线程退出时 在初始化部分: DLLProc := @DLLEnterPoint; DllEnterPoint(DLL_PROCESS_ATTACH); 2 如Form上有TdcomConnection组件,就Uses Activex,在初始化时一句CoInitialize (nil); 3 在退出时一定保证DcomConnection.Connected := False,并且数据集已关闭。否则报地址错。 四全局变量的使用 在widnows 32位程序中,两个应用程序的地址空间是相互没有联系的。虽然DLL在内存中是一份, 但变量是在各进程的地址空间中,因此你不能借助dll的全局变量来达到两个应用程序间的数据 传递,除非你用内存映像文件。 五、其他:调用方式按照标准的Windows调用方式. 六、关于参数传递 •Delphi程序之间调用DLL,如果要用String类型的话,要在引用的单元加上ShareMem 单元。 •如果DelphiDLL供其他开发工具使用的话,不要使用String类型,用PAnsiChar类型。 尽量使用标准DLL接口。指的是传递的参数类型及函数返回类型不能是Delphi特有的, 比如string(AnsiString),以及动态数组和含有这些类型成员的复合类型(如记录),也不 能是包含有这些类型成员数据成员的对象类型,以避免可能的错误。如果使用了string类型或 动态数组类型,且调用方不是Delphi程序,则基本上会报错。如果调用方是Delphi但调用方或 被调用方没有在工程文件的第一包含单元不是ShareMem,也可能会出错。 七、关于回调Funciton 你可以把Callback函数看作是一种特殊的消息响应函数,一般来说我们不会自己调用这种函数, 而是有某些系统函数调用,而且不需要向后传递消息。 只要象C/C++这样支持函数指针的语言都 有回调函数的概念,它实际上是向被调用函数传一个你的函数地址,然后被调用函数向通过你传 入的函数地址来调用你的函数 。 以上是结构化回调,到高级语言Object Pascal、C++中回调函数并没有退出,反而得到延伸与 扩展,在面向对像中的回调,其实是指面向对像中类对像的事件,事件就是原始的回调函数。面 向对像, 将回调函数定义成事件过程,在程序引用对像时,若指定了对像的过程事件后,那么在 要进行事件触发的地方检查事件过程是否分配,如果分中的就执行事,也就是执行了回调函数
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值