如何制作一个 PInvoke 友好的原生 API?
Posted
技术标签:
【中文标题】如何制作一个 PInvoke 友好的原生 API?【英文标题】:How to make a PInvoke friendly native-API? 【发布时间】:2010-01-19 07:23:19 【问题描述】:如何让原生 API 对 PInvoke 友好?
有一些关于如何修改本机程序以与 P/Invoke here 一起使用的提示。但在我编写本机程序之前,我应该注意哪些事项才能使我的程序/库 PInvoke 友好?
使用 C 或 C++ 都可以。
更新: 如果我编写一个 C API,我必须做些什么才能使用 C# 语法进行 P/Invoke-able,如下所示:
[DLLimport("MyDLL.dll")]
是否可以对本机 C++ 代码/库执行相同操作?
制作 P/Invoke 友好的原生 API 的一些技巧的总结/改写: + 参数应该是原生类型(int、char*、float、...) + 参数越少越好 + 如果动态内存被分配并传递给托管代码,请确保创建一个“更清洁”的函数,它也是 p/invoked + 提供示例和/或单元测试来说明如何从 .NET 调用 API + 提供 C++/CLI 包装器
【问题讨论】:
【参考方案1】:不使用 P/Invoke,如果您自己控制本机库,您可以编写一组 C++/CLI 类来包装本机调用。在许多情况下,这将比使用平台调用执行得更好,并且您可以获得类型正确性的额外好处。例如,如果你有类似下面的某种 C API(它没有做任何有用的事情,我只是添加了指针和结构来强化它是本机代码的事实):
struct SomeStruct
int a, b;
int* somePtr;
;
int foo(struct SomeStruct* a, int b)
*a->somePtr = a->a + a->b;
return a->b * *a->somePtr + b;
您可以创建一个 C++/CLI 类来包装它:
public ref class MyNativeAPI
private:
SomeStruct* x;
public:
MyNativeAPI()
x = new SomeStruct;
~MyNativeAPI()
delete x;
int Foo(int a)
pin_ptr<SomeStruct*> ptr = this->x;
return foo(ptr, a);
然后,您可以在 C# 中调用它:
MyNativeAPI a = new MyNativeAPI();
if(a.Foo(5) > 5) ... ;
您必须阅读更多有关 C++/CLI 的内容,以了解您对托管堆和本机堆的新控件,以及混合使用两者的注意事项(如我上面使用的 pin_ptr
),但是总体而言,这是在 .NET 中完成本机互操作的更优雅的解决方案。
【讨论】:
【参考方案2】:根据定义,每个本机函数都可以从托管代码中调用/调用。但是为了对 p/invoke 友好,函数应该具有尽可能少的参数,这些参数应该是本机类型(int、char*、float、...)。此外,如果函数在返回给托管代码的某个指针上分配内存,请确保编写其计数器部分以释放指针,因为托管代码无法释放从非托管代码分配的内存。
【讨论】:
"一个函数应该有尽可能少的参数,这些参数应该是本机类型(int,char*,float,...)"我可以把它总结为:->参数应该是本机类型 (int, char*, float, ...) -> 参数越少越好 -> 如果动态内存被分配并传递给托管代码,请确保创建一个“更清洁”的函数,该函数也是 p/invoked跨度> 【参考方案3】:提供一个从 C# 或 .NET 正确调用它的示例,最好提供一个包含所有方法的 .NET 类
使用 nunit 编写简单的单元测试 这证明您的代码可以正常工作 从.Net调用时会很棒 这样做的方法。
还请记住,可能调用您的代码的 .NET 开发人员不太可能了解 C++,或者不知道不同数据类型的大小等,也不知道这些类型如何映射到 PInvoke 属性。
首先要考虑您希望客户代码的外观,然后设计一个允许它的 API。
【讨论】:
以上是关于如何制作一个 PInvoke 友好的原生 API?的主要内容,如果未能解决你的问题,请参考以下文章