我是 C++/CLI 新手,但多年来一直在编写托管代码......显然已经太多年了。 :)
尝试为第三方提供的非托管类编写包装器,我看到了一些奇怪的东西。我希望你们都能帮我找出我的愚昧和奇怪的地方。
CLI 包装器:
public ref class Wrapper
{
public:
Wrapper(const float* flts, unsigned int fltlen, int offset)
{
_unmanagedClass = new UnmanagedClass(flts, fltlen, offset);
}
~Wrapper()
{
delete _unmanagedClass;
}
String^ getSomeString()
{
string x = _unmanagedClass->getSomeString(); //1
String^ ret = gcnew String(x.c_str()); //2
return ret; //3
}
private:
UnmanagedClass* _unmanagedClass;
};
我还应该注意,我在标头中有这些指令;
#pragma managed(push, off)
#include "Unmanaged.h"
#pragma comment(lib, "lib\\Unmanaged_dll.lib")
#pragma managed(pop)
这是 Unmanagement.h;
class UNMANGED_API UnmanagedClass
{
public:
UnmanagedClass(const float* flts, uint fltlen, int offset);
string getSomeString() { return _someString; }
private:
string _someString;
};
这一切都可以编译。然后,奇怪/缺乏经验就开始出现。
在 DEBUG 配置中调试此问题时,UnmanagedClass::getSomeString()
似乎返回一个合理/预期的字符串值。我可以通过在 //2
上设置断点并查看 x
的值来看到这一点。如果我进入 //3
,我可以看到 ret
的值为 x
。但是,如果我尝试跳出/越过 //3
,我会收到几个失败的断言(BLOCK_TYPE_IS_VALID
和 _CrtIsValidHeapPointer
)和调试器停滞不前,永远不会返回到托管实现。
在 RELEASE 配置中调试此问题时,我没有得到失败的断言,并且返回到我的托管实现,但是 getSomeString()
返回的字符串值在我查看的任何地方都是垃圾; //2
、//3
以及托管实现中。
我用几种不同的方式修改了代码,但没有效果。
我认为需要围绕 //2
完成一些编组工作,但就如何编组 basic_string< 而言,我还没有找到任何真正切中要害的东西/code> 到 System::String^
,或者如果需要的话。如果是这样,那么我们将非常感谢有关显式语法的一些帮助。
我还通过返回 return ""; 将产生失败断言的调用范围缩小到 //1
。 //3
。这些断言指出尝试修改/删除当前运行时可以访问的堆上不存在的内存。这是否与需要编组 UnmangedClass::getSomeString() 的返回值有关?
希望我只是在这里遗漏了一些简单的概念,并且第三方代码没有问题。如果我可以提供更多细节,请告诉我,并对我对所有语言的祖父几乎完全无知表示歉意。
预先感谢您提供任何信息或“指示”;
编辑:添加 C# 托管客户端实现;
public unsafe string GetString(List<float> flts )
{
float[] fltArr = flts.ToArray();
Wrapper wrap;
fixed (float* ptrFlts = fltArr)
{
wrap = new Wrapper(ptrFlts , fltArr.Length, 0);
}
var x = wrap.getSomeString();
return x.ToString();
}
编辑:添加 Unmanged.dll 的 Dumpbin.exe 签名!UnmangedClass::getSomeString()
(public: class std::basic_string,class std::allocator< ;char> >
__thiscall Codegen::getSomeString(void))
I'm new to C++/CLI but have been coding managed code for many years... apparently too many years. :)
Attempting to write a wrapper for an unmanaged class provided by a 3rd party, and I'm seeing some strange stuff. I'm hoping you all can help me out weed what what is my noobishness and what is actually strange.
CLI Wrapper:
public ref class Wrapper
{
public:
Wrapper(const float* flts, unsigned int fltlen, int offset)
{
_unmanagedClass = new UnmanagedClass(flts, fltlen, offset);
}
~Wrapper()
{
delete _unmanagedClass;
}
String^ getSomeString()
{
string x = _unmanagedClass->getSomeString(); //1
String^ ret = gcnew String(x.c_str()); //2
return ret; //3
}
private:
UnmanagedClass* _unmanagedClass;
};
I should also note that I have these directives in the header;
#pragma managed(push, off)
#include "Unmanaged.h"
#pragma comment(lib, "lib\\Unmanaged_dll.lib")
#pragma managed(pop)
Here's Unmanaged.h;
class UNMANGED_API UnmanagedClass
{
public:
UnmanagedClass(const float* flts, uint fltlen, int offset);
string getSomeString() { return _someString; }
private:
string _someString;
};
This all compiles. Then the strangness/lack of experience kicks in.
When debugging this in DEBUG configuration, UnmanagedClass::getSomeString()
appears to be returning a resonable/expected string value. I can see this by setting a breakpoint on //2
and peeking the value of x
. If I step to //3
, I can see that ret
has the value of x
. However, if I attempt to step out/over //3
, I get a couple of failed assertions (BLOCK_TYPE_IS_VALID
and _CrtIsValidHeapPointer
) and the debugger stalls, never returning to the managed implementation.
When debugging this in RELEASE configuration, I don't get the failed assertions and I return to my managed implementation, but the string value returned by getSomeString()
is garbage where ever I peek it; //2
, //3
as well as in the managed implementation.
I've massaged the code in a few different ways to no avail.
I think there is some Mashalling that needs to be done around //2
, but I haven't been able to find any thing that really hits home as far as how to marshall that basic_string
to a System::String^
, or if it's even required. If so, then some help with explicit syntax would be greatly appreciated.
I've also narrowed the call that produces the failed assertions down to //1
by returning return ""; //3
. These assertions point to trying to modify/delete memory that dosn't exist on the heap the current runtime has access to. Does that have to do with needing to marshall the return value of UnmangedClass::getSomeString()
?
Hoping I'm just missing some simple concept here and there isn't a problem with the 3rd party code. Please let me know if I can provide any more detail, and apologies for my almost complete ignorance of the grand-daddy of all languages.
Thanks in advance for any information or "pointers";
EDIT: Adding C# Managed client implementation;
public unsafe string GetString(List<float> flts )
{
float[] fltArr = flts.ToArray();
Wrapper wrap;
fixed (float* ptrFlts = fltArr)
{
wrap = new Wrapper(ptrFlts , fltArr.Length, 0);
}
var x = wrap.getSomeString();
return x.ToString();
}
EDIT: Adding Dumpbin.exe signature of Unmanged.dll!UnmangedClass::getSomeString()
(public: class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >
__thiscall Codegen::getSomeString(void))
发布评论
评论(2)
这个问题与.NET或C++/CLI无关,问题纯粹是在本机代码中。
您违反了
std::string
的单一定义规则,如果您的定义与Unmanagement_dll.dll
使用的内容不完全匹配,那么一切都会崩溃。听起来好像该 DLL 用于调试定义/类布局。This problem has nothing to do with .NET or C++/CLI, the problem is purely in the native code.
You've violated the One Definition Rule for
std::string
, if your definition doesn't exactly match whatUnmanaged_dll.dll
uses, all hell breaks loose. And it sounds as if that DLL is used the debug definition/class layout.您可以将本机字符串转换为托管字符串。这篇 MSDN 上的文章提供了有关如何操作的示例在 Microsoft 平台上提供的所有不同字符串类型之间进行转换:
话虽如此,我获取了您的代码并对其进行了编译,并且我不会遇到任何失败。当然,我必须想出自己的初始化 UnmanagedClass::_someString 的方法,我只是这样做:
当我这样做时,并逐步执行此代码:
它工作得很好。
这是我所做的其余部分:
以及它的实现:
我希望托管类
能有所帮助。
You converted your native string to a Managed string just fine. This article on MSDN, has samples on how to convert between all the different string types that ships on Microsoft platforms:
Having said that, I took your code, and compiled it, and I couldn't get anything to fail. Of course I had to come up with my own way of initializing UnmanagedClass::_someString, which I did by just doing this:
When I did that, and stepped through this code:
It worked just fine.
Here is the rest of what I did:
And it's implementation:
And the managed class
I hope that helps a little.