C#应用程序调用C方法,错误:PInvoke:无法返回变体
内容导读
互联网集市收集整理的这篇技术教程文章主要介绍了C#应用程序调用C方法,错误:PInvoke:无法返回变体,小编现在分享给大家,供广大互联网技能从业者学习和参考。文章包含3175字,纯文字阅读大概需要5分钟。
内容图文
我试图找出如何将复杂对象从C dll返回到调用C#应用程序.我有一个简单的方法,返回一个工作正常的int.谁能告诉我我做错了什么?
C#应用:
class Program
{
static void Main(string[] args)
{
// Error on this line: "PInvoke: Cannot return variants"
var token = LexerInterop.next_token();
}
}
C#LexerInterop代码:
public class LexerInterop
{
[DllImport("Lexer.dll")]
public static extern object next_token();
[DllImport("Lexer.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int get_int(int i);
}
C Lexer.cpp
extern "C" __declspec(dllexport) Kaleidoscope::Token get_token()
{
int lastChar = ' ';
Kaleidoscope::Token token = Kaleidoscope::Token();
while(isspace(lastChar))
{
lastChar = getchar();
}
... Remainder of method body removed ...
return token;
}
extern "C" __declspec(dllexport) int get_int(int i)
{
return i * 2;
}
C Lexer.h
#include "Token.h"
namespace Kaleidoscope
{
class Lexer
{
public:
int get_int();
Token get_token();
};
}
C Token.h
#include <string>
namespace Kaleidoscope
{
enum TokenType
{
tok_eof = -1,
tok_def = -2,
tok_extern = -3,
tok_identifier = -4,
tok_number = -5
};
class Token
{
public:
TokenType Type;
std::string IdentifierString;
double NumberValue;
};
}
我确定这与令牌返回类型有关,但我找不到任何不错的信息.
任何帮助或方向表示赞赏!
解决方法:
您不能将C对象返回给非C调用者. (当您尝试将C对象导出到调用者时,如果使用不同的C编译器编译它,它甚至不起作用.)原因是C对象的实际内存中布局没有标准化,不同的编译器可能会这样做它不同.
.NET运行时不知道如何处理对象,也不知道构成令牌对象的各种类型.类似地,std :: string对.NET没有意义,因为它是一个C类型,依赖于非托管内存分配和不同的内部字符串格式以及与C#不同的语义.
您可以尝试将C对象转换为COM对象,然后将COM接口返回给C#调用者.这将需要一些工作,因为COM类型同样与C类型不兼容(出于上述类似的原因).
您还可以尝试将C对象序列化为字节数组,然后将缓冲区/长度返回给C#,它将首先将对象反序列化为.NET表示形式.你必须复制你试图以这种方式处理的类.
另一种可能性是从函数调用中将所有数据作为独立输出参数返回 – 也就是说,您不会尝试返回令牌,而是将其每个属性作为C中的单独输出参数返回.您仍然需要将一些数据类型转换为.NET可识别的内容. (无法返回std :: string,您必须将其复制到字节数组或将其分配为BSTR等)
最后,一个相当复杂的选项:您可以从C函数返回令牌的地址作为void *,然后创建一些导出的纯C函数,以根据输入指针读取各种属性.完成后,您还需要一个释放C对象的函数.就像是:
__declspec(dllexport) TokenType WINAPI GetTokenType ( Kaleidoscope::Token* pToken )
{
return ( pToken->Type );
}
然后,您将在C#中声明这些函数,如下所示:
[DllImport ( "MyDll.dll" )]
public static extern int GetTokenType ( UIntPtr pObj );
UIntPtr实例将从您的next_token函数返回的void *初始化.然后,您将调用每个导出的属性读取器函数以获取令牌的各个属性.
我可能会使用自定义序列化程序,因为这是最少量的工作(在我看来),我认为这是最干净的解决方案(在可读性和可维护性方面).
注意:使用COM对象可能会减少工作量,但您必须远离任何C类型(至少在公共接口中).
编辑:
我忘了早些时候提到一个有点明显的选择 – 使用混合代码和C/C++LI.这样,您可以在同一DLL中同时拥有非托管代码和托管代码,并且可以在C代码中执行所有转换.如果您在C/C++LI中定义了大多数类,则只需将相关(托管)实例传递给C#应用程序而无需额外转换.
(当然,如果使用前者,你仍然需要在std:string和System.String之间进行转换,但至少你可以在同一个项目中拥有所有的转换逻辑.)
此解决方案很简单,但生成的DLL现在需要.NET运行时.
内容总结
以上是互联网集市为您收集整理的C#应用程序调用C方法,错误:PInvoke:无法返回变体全部内容,希望文章能够帮你解决C#应用程序调用C方法,错误:PInvoke:无法返回变体所遇到的程序开发问题。 如果觉得互联网集市技术教程内容还不错,欢迎将互联网集市网站推荐给程序员好友。
内容备注
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 gblab@vip.qq.com 举报,一经查实,本站将立刻删除。
内容手机端
扫描二维码推送至手机访问。