是否可以使用C# DllImport导入来自DLL的未托管类或结构体?

4
根据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 Questerion 和 Vector3,并在 C# 中定义您的类。 - Lukasz Madon
这只是一个类的示例...我有一个与PhysX一起工作的DLL,它加载网格,创建几何体等,但我仍然需要从此DLL导入一些类以在.Net中使用它们进行跟踪。与其制作PhysX包装器,不如在DLL中实现所需的功能,并从我的DLL中导入更少的内容,而不是整个PhysX。 - Kosmo零
2个回答

6

您不需要编写C++托管代码来进行访问,但是您不能轻松地在非托管内存和托管内存之间跳转。以下是一些关键要点: 1)使用 [StructLayout(LayoutKind.Sequential)] 定义 .net 类,将其映射到您的 c++ 结构体上。

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

2) 像双精度浮点数、int32等基本类型在P/Invoke层之间传递效率很高 - 更复杂的类型会导致.net版本的thunking - msft文档涵盖了哪些数据类型可以高效移动,请尝试使用它们。

3) 在C++中,一切指针都是在.net中的IntPtr,如果您想要安全,请将其视为句柄,即您让c++端处理任何操作/访问底层结构。

4) 访问本地C++非常简单(Handle属性是最初从本地C++端获取的IntPtr)。

[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 weird_st {int a; int b;};这样的结构体。 - Kosmo零
你可以这样做,但是听起来会很麻烦去维护。 - user180326

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接