如何通过RGiesecke UnmanagedExports实现Delphi 6调用C# DLL?
问题背景
我有一个遗留Delphi 6应用,原本使用iManage的COM接口DLL(导入为_TLB单元文件),但iManage已终止其COM API服务,现在iManage Work改用REST API。iManage推出了面向.NET应用的iManage Work SDK,包含iwto.dll、iwhost.dll、IWInterfaces.dll及iwSessionCache.exe工具(用于维持已认证会话)。
我想编写一个C#类库封装iManage Work SDK的调用,通过RGiesecke's UnmanagedExports 1.2.7 NuGet包暴露方法给Delphi 6应用调用并返回结果。目前我的Delphi 6应用是通过ShellExecute调用C#中间程序,调用iManage功能后把结果写入SQL表,再用Windows消息通知Delphi 6读取结果,这种方式效率很低。
因为我不熟悉在Delphi 6中编写OAuth认证的REST请求,所以尝试实现C# DLL调用,已编写如下代码:
C#类库代码
using System; using System.Collections.Generic; using System.Linq; using System.Runtime.InteropServices; using System.Text; using System.Threading.Tasks; using RGiesecke.DllExport; namespace iManExecutorLib { public partial class IManExecutorLib { public IManExecutorLib() { } [DllExport] public static void iManV2SDKCall( [MarshalAs(UnmanagedType.LPWStr)] string cmdLineParms, [MarshalAs(UnmanagedType.LPWStr)] out string sReturned) { var sRslt = string.Empty; sRslt = "cmdLineParms passed-in = <" + cmdLineParms + ">"; sReturned = sRslt; } } }
Delphi 6测试程序代码
unit MainForm; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TfrmMain = class(TForm) mmoLog: TMemo; btnGo: TButton; procedure btnGoClick(Sender: TObject); private { Private declarations } public { Public declarations } procedure WriteLog(sMsg : String); end; var frmMain: TfrmMain; const kDLLname = 'iManExecutorLib.dll'; procedure iManV2SDKCall(cmdLineParms: PWideChar; var sReturn: PWideChar); stdcall; external kDLLname; implementation {$R *.dfm} procedure TfrmMain.WriteLog(sMsg : String); begin mmoLog.Lines.Add(sMsg); mmoLog.Refresh; end; procedure TfrmMain.btnGoClick(Sender: TObject); var wsCmdLineParms : WideString; wsReturned : WideString; pszCmdLineParms : PWideChar; pszReturned : PWideChar; begin wsCmdLineParms := 'Here are some cmdLineParms.'; pszCmdLineParms := PWideChar(wsCmdLineParms); self.WriteLog('wsCmdLineParms=<'+ wsCmdLineParms + '>'); //////////////////////////////////////////// iManV2SDKCall(pszCmdLineParms, pszReturned); //////////////////////////////////////////// wsReturned := WideString(pszReturned); self.WriteLog('wsReturned=<' + wsReturned + '>'); end; end.
把生成的C# DLL复制到Delphi 6程序目录后,运行时出现应用程序错误。注意到该C#类库目标框架为.NET 6.0,而解决方案中另一个类库最高仅支持.NET 4.8,两者项目类型不同。
疑问
- 是否可以通过RGiesecke's UnmanagedExports实现Delphi 6调用封装iManage SDK的C# DLL?
- 若可行,是否需要将解决方案中所有项目统一为支持.NET 5.0+的项目类型?
解答
问题1:是否可用RGiesecke's UnmanagedExports实现Delphi 6调用C# DLL?
可以,但需注意几个关键细节:
- 调用约定匹配:必须确保C#导出方法的调用约定和Delphi声明一致。修改C#的导出属性为
[DllExport(CallingConvention = CallingConvention.StdCall)],避免因调用约定不匹配导致崩溃。 - 字符串内存管理:当前代码中C#返回的托管字符串直接传递给Delphi的
PWideChar会存在内存风险,建议二选一:- 让Delphi预先分配足够大的缓冲区,C#将结果写入该缓冲区;
- C#使用
CoTaskMemAlloc分配字符串内存,Delphi调用后通过CoTaskMemFree释放(需引入ComObj单元)。
- 平台与运行时匹配:Delphi 6是32位应用,C# DLL必须编译为x86目标平台,同时确保目标机器安装了对应版本的.NET运行时(如.NET 6.0 x86运行时)。
问题2:是否需要统一解决方案项目的.NET版本?
不需要强制统一,但要遵循兼容性规则:
- 如果你的C#导出DLL依赖仅支持.NET 4.8的类库,那么导出DLL只能目标.NET Framework 4.8(.NET 5+属于.NET Core系列,无法直接引用.NET Framework类库)。且RGiesecke's UnmanagedExports在.NET Framework下的兼容性更稳定,原包对.NET Core/.NET 5+的支持有限,可能需要使用社区维护的分支。
- 若要使用.NET 6.0,必须确保所有依赖类库能升级到.NET Standard 2.0或更高(.NET 6支持.NET Standard 2.0),或者直接迁移依赖类库到.NET 6。如果依赖类库无法升级,只能将导出DLL的目标框架改为.NET Framework 4.8。
另外,当前测试代码崩溃的大概率原因:
- 调用约定不匹配:C#默认导出可能为
cdecl,但Delphi声明的是stdcall; - 平台不兼容:C# DLL编译为Any CPU或x64,与Delphi 6的32位架构冲突;
- 缺少.NET运行时:目标机器未安装对应版本的.NET 6.0 x86运行时。
内容的提问来源于stack exchange,提问作者TheGDog
相关产品推荐
相关产品推荐

