代码之家  ›  专栏  ›  技术社区  ›  JimEvans

如何在C++平台上使用C++类而不考虑平台?

  •  4
  • JimEvans  · 技术社区  · 14 年前

    我有一个原生的/非托管的C++库,有许多类,我想用C语言来使用。我读过的大多数解决方案(比如 this one this one 建议我应该创建一个C++/CLI包装器,并使用我的C语言项目中的包装器。然而,这些建议大多忽略了平台。据我所知,如果非托管DLL是32位的,我的包装器DLL必须是32位的,这将迫使我的C#项目使用x86平台,即使我有32位和64位版本的非托管DLL可用。

    我以前用C api通过使用P/Invoke解决过这个问题 LoadLibrary() Marshal.GetDelegateForFunctionPointer() 但是我认为包装C++对象的每一个方法调用都容易出错,而且难以维护。我也不认为我应该尝试依赖于发现在C++ DLL中导出的被损坏的名称。

    顺便说一下,我尝试使用的C++库是谷歌V8JavaScript VM( http://code.google.com/p/v8/ http://v8sharp.codeplex.com/ )和Javascript.NET( http://javascriptdotnet.codeplex.com/ ). 然而,据我所知,它们都使用了特定于平台的C++/CLI包装器。为了与其他托管代码库进行互操作,我需要托管代码组件使用AnyCPU。

    有没有一个好的方法来实现这一点?

    2 回复  |  直到 7 年前
        1
  •  4
  •   tyranid    14 年前

    有一种巧妙的方法可以做到这一点,但它确实增加了额外的代码负担(尽管你可以在应用程序开始时就这么做)。

    它依赖于创建一个新的应用程序域,其中包含从何处加载程序集的特定于平台的专用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);
            }
        };
    }
    

    现在您需要更改代码,以便它创建一个新的应用程序域,并在该域中运行所有代码(您也可以在默认值中创建类型,但这会使它稍微复杂一些,而且可能会变慢)。

    因此,请定义一个引导类来启动应用程序:

    using NativeLib;
    
    namespace BitnessTest
    {
        class StartClass
        {
            public static void Start()
            {
                NativeClass.DoSomething();
            }
        }
    }
    

    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);
            }
        }
    }
    

    现在确保删除NativeLib.dll从当前的应用程序目录中,创建一个x86和x64目录,并在每个目录中放置本机lib的相应版本。运行它,它现在应该在32位和64位上工作。

    if (IntPtr.Size > 4)
    {
        AppDomain.CurrentDomain.AppendPrivatePath("x64");
    }
    else
    {
        AppDomain.CurrentDomain.AppendPrivatePath("x86");                
    }
    
    StartClass.Start();
    

    当然有一些警告,它依赖于程序集通常是后期绑定的事实,因此如果在创建应用程序域之前使用本机类型,它可能会中断。还有一些方法可以使它更通用,例如,您可以编写一个包装器exe来引导包含真实代码的延迟加载程序集,这意味着它将更通用。

    当然,当您希望它成为一个库时,您可能必须使用一个引导程序集,在静态构造函数中处理appdomain的私有路径,这可能不是一个非常礼貌的做法;)

        2
  •  0
  •   Jimmy Hoffa    14 年前

    编译x64和x86版本,分别为它们创建PInvoke sig,只需为要使用的每个sig创建一个方法即可内部尺寸并为当前位调用正确的pinvoke。

    除非我错了,但我相信这是可以做到的,我不记得你是否需要一个额外的间接层,因为你使32位和64位的互操作与各自的pinvoke sig和反射加载正确的一个取决于内部尺寸而不是将pinvoke sig放在同一个二进制文件中。