Jim*_*ans 5 c# c++ interop 32bit-64bit
我有一个本机/非托管 C++ 库,其中包含许多我想从 C# 使用的类。我读过的大多数解决方案(比如这个和这个)都建议我应该创建一个 C++/CLI 包装器,并在我的 C# 项目中使用这个包装器。然而,这些建议中的大多数都忽略了平台。据我所知,如果非托管 DLL 是 32 位,我的包装 DLL 必须是 32 位,这将强制我的 C# 项目使用 x86 平台,即使我同时拥有 32 位和 64 位可用的非托管 DLL 的位版本。
我之前通过使用带有LoadLibrary()和的P/Invoke 使用 C API 解决了这个问题Marshal.GetDelegateForFunctionPointer(),但我认为包装 C++ 对象的每个方法调用将容易出错且难以维护。我也不认为我应该尝试依赖于发现 C++ DLL 中导出的损坏名称。
顺便说一句,我尝试使用的 C++ 库是 Google V8 JavaScript VM ( http://code.google.com/p/v8/ ),它可以为 x86 或 x64 编译,因此将 C++ 源代码直接移植到C# 是不可能的。是的,我知道有几个现有项目包装了 V8 以与托管代码一起使用,例如 v8sharp ( http://v8sharp.codeplex.com/ ) 和 Javascript .NET ( http://javascriptdotnet.codeplex.com/)。但是,据我所知,它们都使用特定于平台的 C++/CLI 包装器。为了与其他托管代码库互操作,我需要我的托管代码组件才能使用 AnyCPU。
有没有好的方法来实现这一点?
好吧,有一种狡猾的方法可以做到这一点,但它确实增加了额外的代码负担(尽管您可以在应用程序启动时执行此操作)。
它依赖于创建一个新的应用程序域,其中包含加载程序集的平台特定的私有 bin 路径。然后,您将本机代码隐藏在 32 位或 64 位目录中,它将加载最合适的一个。
因此,为了便于论证,您有一个 C++ CLR 项目:
#pragma once
using namespace System;
namespace NativeLib {
public ref class NativeClass
{
public:
static void DoSomething()
{
Console::WriteLine("IntPtr.Size = {0}", IntPtr::Size);
}
};
}
Run Code Online (Sandbox Code Playgroud)
将其构建为 32 位和 64 位。引用您的 C# 应用程序来使用该库。
现在您需要更改代码,以便创建一个新的应用程序域,并在其中运行所有代码(您也可以在默认值中创建类型,但这会使它稍微复杂一点并且可能会很慢)。
因此,定义一个引导类来启动您的应用程序:
using NativeLib;
namespace BitnessTest
{
class StartClass
{
public static void Start()
{
NativeClass.DoSomething();
}
}
}
Run Code Online (Sandbox Code Playgroud)
最后将您的 Main 函数更改为:
using System;
using System.Reflection;
namespace BitnessTest
{
class Program
{
static void Main(string[] args)
{
AppDomainSetup setup = AppDomain.CurrentDomain.SetupInformation;
if (IntPtr.Size > 4)
{
setup.PrivateBinPath = "x64";
}
else
{
setup.PrivateBinPath = "x86";
}
AppDomain appDomain = AppDomain.CreateDomain("Real Domain", null, setup);
appDomain.DoCallBack(StartClass.Start);
}
}
}
Run Code Online (Sandbox Code Playgroud)
现在确保从当前应用程序目录中删除 NativeLib.dll,创建一个 x86 和一个 x64 目录,并将各自版本的本机 lib 放入每个目录中。运行它,它现在应该可以在 32 位和 64 位上运行。
如果您不想要另一个应用程序域并且愿意接受已弃用的代码(这些代码可能会消失,但仍在 .net 4 中),您可以执行以下操作:
if (IntPtr.Size > 4)
{
AppDomain.CurrentDomain.AppendPrivatePath("x64");
}
else
{
AppDomain.CurrentDomain.AppendPrivatePath("x86");
}
StartClass.Start();
Run Code Online (Sandbox Code Playgroud)
当然有一些警告,它依赖于程序集通常是后期绑定的事实,因此如果在创建应用程序域之前使用本机类型,它可能会崩溃。还有一些方法可以使其更通用,例如,您可以编写一个包装器 exe,它引导包含真实代码的延迟加载程序集,这意味着它可以更通用地工作。
当然,当您希望这是一个库时,您可能必须使用启动捆绑程序集,例如静态构造函数中的应用程序域的私有路径,这可能不是一个非常有礼貌的事情;)
| 归档时间: |
|
| 查看次数: |
689 次 |
| 最近记录: |