【发布时间】:2014-03-13 14:13:03
【问题描述】:
我正在尝试创建一个 C++/CLI 包装器,用于将类对象从非托管 C++ DLL 传递到托管 C# 代码(随后在网页上显示对象的内容)。我在非托管 C++ 代码中有这个函数:
ProbeState _cdecl ManagerAPI::getProbeState()
{
ProbeState ps = psdao.getLastProbeStateByProbeId(1);
return ps;
}
我在 C++/CLI 包装器中调用函数:
using namespace System;
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include "../ManagerApp/ProbeState.h"
typedef ProbeState(*PSFunc)(void);
public ref class ManagerAPIWrapper
{
private:
HINSTANCE managerApp;
public:
ManagerAPIWrapper()
{
managerApp = LoadLibrary(L"ManagerApp.dll");
}
System::String^ testFunc()
{
PSFunc psFunc = (PSFunc)GetProcAddress(managerApp, "?getProbeState@ManagerAPI@@QAA?AVProbeState@@XZ");
ProbeState *ps = new ProbeState(psFunc());
System::String ^s = gcnew System::String(ps->getName().c_str());
delete ps;
return s;
}
};
最后我从我的 C# 控制器调用包装器:
ManagerAPIWrapper.ManagerAPIWrapper wrapper = new ManagerAPIWrapper.ManagerAPIWrapper();
ViewBag.DllMessage = wrapper.testFunc();
它总是在 ProbeState *ps = new ProbeState(psFunc());
行抛出异常不过,奇怪的是,当我将 C++/CLI 包装器编译为控制台应用程序并添加了主函数时:
int _tmain(int argc, _TCHAR* argv[])
{
ManagerAPIWrapper::ManagerAPIWrapper wrapper;
System::Console::WriteLine(wrapper.testFunc());
getchar();
return 0;
}
这段代码运行良好,并打印出 C++ DLL 从数据库中检索到的状态名称。 C++/CLI 如何在控制台应用程序中工作并在从 C# 调用时抛出异常?
P.S.:包装器是使用 /clr 选项编译的。当我使用 /clr:pure 编译包装器时,异常与 C# 调用相同。这是否意味着当包装器在 C# 应用程序中编译并从 C# 应用程序调用时,它采用纯选项?
包装器旨在在 C++ 和 C# 之间转换数据,因此根据我的观点,它不应该在 C# 应用程序中使用更严格的选项进行编译。有没有办法告诉 C# 编译器这个程序集包含混合代码?
【问题讨论】:
-
可能是
_cdecl——如果是这种情况,在 C# 中添加一个编组属性应该可以解决它:stackoverflow.com/questions/5155180/… -
跨模块边界返回 C++ 对象是非常风险的。使用完全相同的编译器版本和完全相同的设置构建此 DLL 并且 CRT 是共享的(/MD 选项)是绝对关键的。 GetProcAddress 的恶作剧和崩溃强烈表明情况并非如此。您必须重建 DLL。
-
那个 _cdecl 属性是我徒劳地努力让代码工作的结果......所以你认为我应该没有它吗?
-
好的,我用显式的
_cdecl尝试了控制台输出,但它也因 AccesViolationEx 失败,所以这绝对是个坏主意。但是,进入 C# 代码的接口仍然不起作用。 -
我做了一些调试,发现 LoadLibrary 调用失败并出现错误 126 - 未找到模块。但是问题来了:为什么调用在 DLL(由 C# 代码加载)中失败时可以与控制台应用程序一起使用?