You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何通过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=&lt;'+ wsCmdLineParms + '&gt;');

  ////////////////////////////////////////////
  iManV2SDKCall(pszCmdLineParms, pszReturned);
  ////////////////////////////////////////////

  wsReturned := WideString(pszReturned);

  self.WriteLog('wsReturned=&lt;' + wsReturned + '&gt;');
end;

end.

把生成的C# DLL复制到Delphi 6程序目录后,运行时出现应用程序错误。注意到该C#类库目标框架为.NET 6.0,而解决方案中另一个类库最高仅支持.NET 4.8,两者项目类型不同。

疑问

  1. 是否可以通过RGiesecke's UnmanagedExports实现Delphi 6调用封装iManage SDK的C# DLL?
  2. 若可行,是否需要将解决方案中所有项目统一为支持.NET 5.0+的项目类型?

解答

问题1:是否可用RGiesecke's UnmanagedExports实现Delphi 6调用C# DLL?

可以,但需注意几个关键细节:

  • 调用约定匹配:必须确保C#导出方法的调用约定和Delphi声明一致。修改C#的导出属性为[DllExport(CallingConvention = CallingConvention.StdCall)],避免因调用约定不匹配导致崩溃。
  • 字符串内存管理:当前代码中C#返回的托管字符串直接传递给Delphi的PWideChar会存在内存风险,建议二选一:
    1. 让Delphi预先分配足够大的缓冲区,C#将结果写入该缓冲区;
    2. 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。

另外,当前测试代码崩溃的大概率原因:

  1. 调用约定不匹配:C#默认导出可能为cdecl,但Delphi声明的是stdcall;
  2. 平台不兼容:C# DLL编译为Any CPU或x64,与Delphi 6的32位架构冲突;
  3. 缺少.NET运行时:目标机器未安装对应版本的.NET 6.0 x86运行时。

内容的提问来源于stack exchange,提问作者TheGDog

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.01 11:57:43