暴露 C++ API 到 C#
因此,我拥有的是 *.dll 中包含的 C++ API,并且我想使用 C# 应用程序来调用 API 中的方法。
到目前为止,我已经创建了一个 C++/CLR 项目,其中包含本机 C++ API,并设法创建了一个类似于以下内容的“桥”类:
// ManagedBridge.h
#include <CoreAPI.h>
using namespace __CORE_API;
namespace ManagedAPIWrapper
{
public ref class Bridge
{
public:
int bridge_test(void);
int bridge_test2(api_struct* temp);
}
}
。
// ManagedBridge.cpp
#include <ManagedBridge.h>
int Bridge::bridge_test(void)
{
return test();
}
int Bridge::bridge_test2(api_struct* temp)
{
return test2(temp);
}
我还有一个 C# 应用程序,它引用了 C++/CLR“Bridge.dll”,然后使用其中包含的方法。我对此有很多问题:
- 我无法弄清楚如何在 C# 程序中调用 bridge_test2 ,因为它不知道 api_struct 实际上是什么。我知道我需要在某处封送对象,但我是在 C# 程序还是 C++/CLR 桥中执行此操作?
- 这似乎是一种公开 API 中所有方法的非常冗长的方式,难道没有一种我错过的更简单的方式吗? (这不使用 P/Invoke!)
编辑: 好的,由于下面的回复,我现在已经有了基础知识,但是我的结构(在本例中称之为“api_struct2”)有C++ 本机代码中的本机枚举和联合,如下所示:
typedef struct
{
enum_type1 eEnumExample;
union
{
long lData;
int iData;
unsigned char ucArray[128];
char *cString;
void *pvoid;
} uData;
} api_struct2;
我想我已经弄清楚如何让枚举工作;我已在托管代码中重新声明它,并执行“native_enum test = static_cast(eEnumExample)”以将托管版本切换为本机版本。
然而工会让我难住了,我真的不知道如何攻击它..有人有想法吗?
So what I have is a C++ API contained within a *.dll and I want to use a C# application to call methods within the API.
So far I have created a C++ / CLR project that includes the native C++ API and managed to create a "bridge" class that looks a bit like the following:
// ManagedBridge.h
#include <CoreAPI.h>
using namespace __CORE_API;
namespace ManagedAPIWrapper
{
public ref class Bridge
{
public:
int bridge_test(void);
int bridge_test2(api_struct* temp);
}
}
.
// ManagedBridge.cpp
#include <ManagedBridge.h>
int Bridge::bridge_test(void)
{
return test();
}
int Bridge::bridge_test2(api_struct* temp)
{
return test2(temp);
}
I also have a C# application that has a reference to the C++/CLR "Bridge.dll" and then uses the methods contained within. I have a number of problems with this:
- I can't figure out how to call bridge_test2 within the C# program, as it has no knowledge of what a api_struct actually is. I know that I need to marshal the object somewhere, but do I do it in the C# program or the C++/CLR bridge?
- This seems like a very long-winded way of exposing all of the methods in the API, is there not an easier way that I'm missing out? (That doesn't use P/Invoke!)
EDIT: Ok, so I've got the basics working now thanks to responses below, however my struct (call it "api_struct2" for this example) has both a native enum and union in the C++ native code, like the following:
typedef struct
{
enum_type1 eEnumExample;
union
{
long lData;
int iData;
unsigned char ucArray[128];
char *cString;
void *pvoid;
} uData;
} api_struct2;
I think I have figured out how to get the enum working; I've re-declared it in managed code and am performing a "native_enum test = static_cast(eEnumExample)" to switch the managed version to native.
However the union has got me stumped, I'm not really sure how to attack it.. Ideas anyone?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
是的,您正在通过引用传递非托管结构。这对于 C# 程序来说是一个问题,指针与垃圾收集非常不兼容。不考虑它可能也没有结构声明的事实。
您可以通过声明结构的托管版本来解决此问题:
可以将方法声明为
或
现在,如果结构具有超过 4 个字段(约 16 字节),则 选择后者。该方法需要将结构体成员一一复制到非托管 api_struct 中并调用非托管类方法。不幸的是,这是必要的,因为托管结构的内存布局是不可预测的。
这一切都非常机械,您可能会从 SWIG 获得帮助。我自己没有使用过它,不确定它是否足够智能来处理传递的结构。
一种完全不同的方法是通过为包装类提供一个构造函数和/或属性来让您构建 api_struct 的内容,从而使包装类更清晰。或者您可以为该结构声明一个包装器引用类,就像在托管代码中一样。
Yes, you are passing an unmanaged structure by reference. That's a problem for a C# program, pointers are quite incompatible with garbage collection. Not counting the fact that it probably doesn't have the declaration for the structure either.
You can solve it by declaring a managed version of the structure:
Now you can declare the method as
or
Pick the latter if the structure has more than 4 fields (~16 bytes). The method needs to copy the structure members, one-by-one, into an unmanaged api_struct and call the unmanaged class method. This is unfortunately necessary because the memory layout of a managed structure is not predictable.
This is all pretty mechanical, you might get help from SWIG. Haven't used it myself, not sure if it is smart enough to deal with a passed structure.
A completely different approach is to make the wrapper class cleaner by giving it a constructor and/or properties that lets you build the content of an api_struct. Or you could declare a wrapper ref class for the structure, much like you would in managed code.
您需要在 .NET 程序集中定义一个使用属性的托管版本(例如
StructLayoutAttribute
) 以确保其正确封送。另一种方法是围绕您的API 创建一个COM 包装器(例如使用ATL)。这可能会花费更多精力,但至少您可以避免 P/Invoke 所需的结构和函数定义的双重编码。
更正:您已经创建了一个 C++/CLI 项目:因此只需添加正确的“#pragma”来告诉编译器这是 .NET 代码,然后输出是一个程序集C#项目可以直接参考。
You need to define a managed version in a .NET assembly, that uses attributes (like
StructLayoutAttribute
) to ensure it marshals correctly.The other approach is to create a COM wrapper (e.g. using ATL) around your API. This might be more effort, but at least you avoid the double coding of struct and function definitions needed for P/Invoke.
Correction: You have created a C++/CLI project: so just add correct '#pragma' to tell the compiler this is .NET code, and then the output is an assembly the C# project can just reference.
Yuo 正在尝试将这种方式做得比实际情况更复杂。您想要的是两个不同的结构。一种是受管理的,另一种是不受管理的。您向外部公开托管版本(向您的 C# 应用程序)。它将全部是“.Net-ish”,没有联合等概念。
在桥中,您收到结构的托管版本,手动创建非托管结构并编写代码以将数据逐个字段移动到非托管结构。然后调用非托管代码,最后将数据移回托管结构。
C++/CLI 的美妙之处在于托管代码还可以与非托管代码和数据一起使用(并包括非托管 .h 文件)。
Yuo are trying to do this way more complicated that it really is. What you want is two different structs. One managed and one unmanaged. You expose the managed version externally (to your C# app). It will be all ".Net-ish" with no concepts of unions or so.
In your bridge you receive the managed version of the struct, manually create the unmanaged struct and write code to move your data, field by field over to the unmanaged struct. Then call your unmanaged code and finally move the data back to the managed struct.
The beautiful thing about C++/CLI is that the managed code also can work with unmanaged code and data (and include the unmanaged .h files).