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

如何用C++/CLI封装C# DLL供非托管Pascal(Delphi)调用?

C++/CLI桥接C#与Delphi的实现验证及完整封装指南

一、当前实现方式的正确性验证

你的思路是完全可行的:C++/CLI作为托管(C#)与非托管(Delphi)代码的中间桥接层,确实比COM方案更灵活,能避开COM复杂的类型适配坑。只要你的C++/CLI项目满足以下几点,基础实现就是正确的:

  • 项目类型为C++/CLI类库(而非纯C++ DLL)
  • 已正确引用C# Selenium核心DLL,且能在C++/CLI代码中正常实例化C#类、调用方法
  • 暴露给Delphi的导出函数用extern "C"修饰,参数与返回值均为非托管类型(如const char*、int、void*等)

二、完整封装步骤

1. 项目结构与基础配置

  • 整体分为三个部分:已完成的C# Selenium核心库、C++/CLI桥接DLL、Delphi测试项目
  • C++/CLI项目关键设置:
    • 右键项目→属性→常规→公共语言运行时支持:选择公共语言运行时支持(/clr)
    • 链接器→高级→目标文件格式:设置为DLL (/DLL)

2. 类型适配与内存管理

字符串处理

Delphi使用PAnsiChar/PWideChar,C++/CLI需完成托管字符串与非托管字符串的双向转换:

// 托管字符串转非托管宽字符
const wchar_t* ManagedToUnmanaged(String^ managedStr)
{
    return Marshal::StringToHGlobalUni(managedStr).ToPointer();
}

// 非托管宽字符转托管字符串
String^ UnmanagedToManaged(const wchar_t* unmanagedStr)
{
    return gcnew String(unmanagedStr);
}

注意:Delphi调用后需负责释放C++/CLI返回的字符串内存,或在桥接层提供专门的释放函数:

extern "C" __declspec(dllexport) void FreeString(wchar_t* str)
{
    Marshal::FreeHGlobal(IntPtr(str));
}

对象句柄传递

C#对象无法直接传递给Delphi,需将托管对象引用包装为void*作为句柄传递:

// 全局字典保存C#驱动实例,防止被GC回收
static gcroot<Dictionary<IntPtr, IWebDriver^>^> s_DriverMap = gcnew Dictionary<IntPtr, IWebDriver^>();

// 创建ChromeDriver并返回句柄
extern "C" __declspec(dllexport) void* CreateChromeDriver()
{
    ChromeDriver^ driver = gcnew ChromeDriver();
    IntPtr handle = IntPtr(driver);
    s_DriverMap->Add(handle, driver);
    return handle.ToPointer();
}

// 通过句柄获取C#驱动实例
IWebDriver^ GetDriver(void* handle)
{
    IntPtr ptr(handle);
    return s_DriverMap[ptr];
}

// 释放驱动实例并移除字典引用
extern "C" __declspec(dllexport) void DestroyChromeDriver(void* handle)
{
    IntPtr ptr(handle);
    if (s_DriverMap->ContainsKey(ptr))
    {
        IWebDriver^ driver = s_DriverMap[ptr];
        driver->Quit();
        s_DriverMap->Remove(ptr);
    }
}

3. 导出核心Selenium功能

以打开网页、获取页面标题为例:

extern "C" __declspec(dllexport) const wchar_t* DriverGetTitle(void* driverHandle)
{
    IWebDriver^ driver = GetDriver(driverHandle);
    String^ title = driver->Title;
    return ManagedToUnmanaged(title);
}

extern "C" __declspec(dllexport) void DriverNavigateTo(void* driverHandle, const wchar_t* url)
{
    IWebDriver^ driver = GetDriver(driverHandle);
    driver->Navigate()->GoToUrl(UnmanagedToManaged(url));
}

4. Delphi端调用示例

type
  TDriverHandle = Pointer;

function CreateChromeDriver: TDriverHandle; cdecl; external 'SeleniumBridge.dll';
procedure DestroyChromeDriver(Handle: TDriverHandle); cdecl; external 'SeleniumBridge.dll';
function DriverGetTitle(Handle: TDriverHandle): PWideChar; cdecl; external 'SeleniumBridge.dll';
procedure DriverNavigateTo(Handle: TDriverHandle; Url: PWideChar); cdecl; external 'SeleniumBridge.dll';
procedure FreeString(Str: PWideChar); cdecl; external 'SeleniumBridge.dll';

procedure TForm1.btnTestClick(Sender: TObject);
var
  Driver: TDriverHandle;
  PageTitle: PWideChar;
begin
  Driver := CreateChromeDriver;
  try
    DriverNavigateTo(Driver, 'https://www.google.com');
    PageTitle := DriverGetTitle(Driver);
    try
      ShowMessage(WideString(PageTitle));
    finally
      FreeString(PageTitle);
    end;
  finally
    DestroyChromeDriver(Driver);
  end;
end;

三、关键注意事项

  • GC回收防护:必须用全局容器保存托管对象引用,否则GC可能在Delphi仍在使用时回收C#对象
  • 异常处理:C++/CLI中要捕获C#的异常,转换为非托管错误码返回给Delphi,避免程序崩溃
  • 字符集统一:全程使用Unicode(宽字符),避免ANSI/Unicode转换混乱
  • 平台一致性:C#、C++/CLI、Delphi需编译为相同平台(x86或x64),否则会出现调用错误

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 12:15:30