是否可以使用C#DllImport从DLL导入非托管类或结构?

时间:2012-06-03 15:51:04

标签: c# c++ class dll dllimport

正如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;
};

我可以导入哪些方式?特别是那些带指针的结构

2 个答案:

答案 0 :(得分:6)

您不需要编写用于访问的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)像double,int32等基本类型有效地跨越P / Invoke层 - 更复杂的类型可以获得thunking的.net变体 - 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);
        }
    }

答案 1 :(得分:2)

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

您无法直接导入C ++类。只有C风格的功能。 C风格的函数只允许您传递C基元类型,这使得它们易于互换。通常,从非C ++代码中,几乎不可能正确传递C ++类型。如果你不能传入C ++对象,通常很少有你可以调用的有趣函数。

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