【问题标题】:Is it possible to import unmanaged class or struct from DLL using C# DllImport?是否可以使用 C# DllImport 从 DLL 导入非托管类或结构?
【发布时间】:2012-06-03 15:51:03
【问题描述】:

正如 msdn http://msdn.microsoft.com/en-us/library/aa288468(v=vs.71).aspx 所说,“平台调用服务 (PInvoke) 允许托管代码调用在 DLL 中实现的非托管函数。”

我想从我用 c++ 制作的 DLL 中导入一些类
有可能吗?如何实现?

例如,我在 DLL 中有一些结构:

struct __declspec(dllexport) DLLVector3
{
    float x, y, z;
};

struct __declspec(dllexport) DLLQuaternion
{
    float x, y, z, w;
};

class __declspec(dllexport) DLLCharacter
{
public:
    DLLVector3 position;
    DLLQuaternion orientation;

    DLLCharacter()
    {

    }

    ~DLLCharacter()
    {

    }

    void setPosition(PxVec3 pos)
    {
        position.x = pos.x;
        position.y = pos.y;
        position.z = pos.z;
    }

    void setOrientation(PxQuat or)
    {
        orientation.x = or.x;
        orientation.y = or.y;
        orientation.z = or.z;
        orientation.w = or.w;
    }
};

struct __declspec(dllexport) PhysicalObject
{
    DLLCharacter *character;
    PxRigidActor *mActor;
    PxController *mController;
};

我可以通过什么方式导入这些?尤其是那些带有指针的结构

【问题讨论】:

  • 为什么需要这个?您使用 .Net Questionerion 和 Vector3 并在 C# 中定义您的类
  • 这只是类的一个例子...我有 DLL 与 PhysX 一起工作,加载网格,创建几何等,但我仍然需要从这个 DLL 导入一些类才能在 .用于追踪的网络。我不是制作 PhysX 包装器,而是更好地在 DLL 中实现所需的功能,并从我的 DLL 导入更少的东西,而不是整个 PhysX

标签: c# c++ class dll dllimport


【解决方案1】:

您无需编写用于访问的 C++ 托管代码 - 但是 - 您不能轻松地在非托管内存和托管内存之间来回切换。这里有几个关键点 1) 使用 [StructLayout(LayoutKind.Sequential)] 定义映射到 c++ 结构之上的 .net 类

[StructLayout(LayoutKind.Sequential)]
[Serializable]
public struct SFVec3F : IRawStreamIO
{
    public double _x;
    public double _y;
    public double _z;
}

2) 双精度、int32 等基本类型有效地在 P/Invoke 层中移动 - 更复杂的类型为您赢得了 .net 的 thunking 变体 - msft doco 涵盖了哪些数据类型可以有效地移动,尝试并使用它们

3) C++ 领域中的所有指针都是 .net 领域中的 IntPtr,如果您想安全起见,您应该将其视为句柄,即您可以让 C++ 端对底层结构进行任何操作/访问

4) 访问原生 C++ 非常简单(Handle props 是最初源自原生 C++ 端的 IntPtrs)

[DllImport("CS2V3.dll", CharSet = CharSet.Ansi)]
private static extern void AddChild(IntPtr csoPtr, IntPtr childPtr, short recomputeBounds, short adjustCoords, short blindchild);
public void AddChild(V3CSO theChild)
{
    AddChild(m_handle, theChild.Handle,0,0,0);
}

5) 字符串和其他一些类型需要编组才能获得 .net 可用的形式 - 请注意,当您将字符串传递给非托管代码时,字符串会自动发生这种情况,但在入站时您必须自己完成

 [DllImport("CS2V3.dll", CharSet = CharSet.Ansi)]
private static extern IntPtr GetName(IntPtr csoPtr);
[DllImport("CS2V3.dll", CharSet = CharSet.Ansi)]
private static extern void SetName(IntPtr csoPtr, string newName);
   public string Name
    {
        get
        {
            IntPtr np = GetName(m_handle);
            return Marshal.PtrToStringAnsi(np);
        }
        set
        {
            SetName(m_handle, value);
        }
    }

【讨论】:

    【解决方案2】:

    这是可能的....但是您需要在 C++/CLI 中编写包装器代码。这允许您编写可以从 C#/.net 代码中使用的类(在外部它是托管代码),并且在实现中您可以使用 C++ 代码。

    您不能直接导入 C++ 类。只有 C 风格的函数。 C 风格的函数只允许您传递 C 原始类型,这使得与它们的互操作变得容易。通常,从非 C++ 代码中,几乎不可能正确传递 C++ 类型。如果不能传入 C++ 对象,那么通常很少有可以调用的有趣函数。

    后期编辑: 或者,您可以为非托管功能编写 C 样式的包装函数。您必须一次性传递所有参数(您可以传递结构),但您可以用标准 C/C++ 编写包装器。

    【讨论】:

    • 如果我在 DLL 中创建一个将对象指针返回为 void* 的函数,然后从 c# 导入该函数并设置/读取类成员,我使用类似:(received_pointer + 4) = 2; //如果 dll 中的 struct 就像 struct monster_st {int a; int b;};?
    • 可以,但维护起来很痛苦。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-04
    • 2017-08-05
    • 2013-09-29
    • 2010-11-15
    • 2015-03-13
    • 1970-01-01
    相关资源
    最近更新 更多