C++/CLI pointer issue = fun!
我一直在将一堆旧的C代码转换为C / CLI代码,我想我已经将自己编码到了一个角落。
我的目标是采用一个不受管理的c库和一堆头文件,并将其功能公开给C#解决方案。通过在互联网上阅读,实现此目的的标准方法是:
- 创建两个c类:一个托管类,另一个非托管类。
- 非托管类将对c库中的对象进行纠缠以提供所需的功能。
- 托管类会将所有公共方法package在非托管类中。每个package器方法都将处理从String ^到string等的必要转换。
但是,我的情况不是很复杂,所以我决定尝试在一个类中实现所有内容。我现在正在努力解决一个特殊问题,该问题会生成AccessViolationExceptions。
我的头文件如下:
1 2 3 4 5 6 7 8 9 | public ref class ManagedClass { public: ManagedClass(); void CreateUnmanagedObject(String^ param1); void UseUnmanagedObject(); UnmanagedObject *myUnmanagedObject; } |
我的cpp文件看起来像这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | void ManagedClass::CreateUnmanagedObject(String^ param1) { /* Convert params, use them in some way. */ /* capture the output of this library call to the pointer defined in ManagedClass.*/ myUnmanagedObject= &(LibrayObject.LibraryMethod1()); } void ManagedClass::UseUnManagedObject() { /* This function will pass the Unmanaged object into * a library function which will do some work on it. */ LibraryObject.LibraryMethod2(*myUnmanagedObject); /* Whoops! System.AccessViolationException is thrown! */ } |
有趣的是,如果我在LibraryMethod1之后立即在CreateUnmanagedObject中调用LibraryMethod2,它会正常工作。但是在CreateUnmanagedObject退出后,似乎myUnmanagedObject指向的内存丢失了。
任何人都可以看到发生这种情况的原因吗?
编辑:库声明如下:
1 2 | UnmanagedObject LibraryMethod1(); void LibraryMethod2(UnmanagedObject ¶m); |
不确定您的真正问题是什么,但看起来似乎是错误的。托管package器应该与非托管package器非常接近。让我们从这样的非托管声明开始工作:
1 2 3 4 5 | class Unmanagedclass { public: Unmanagedclass(const char* arg) {} void mumble() {} }; |
然后您的package器应类似于以下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | #pragma managed(push, off) #include"unmanagedclass.h" #pragma managed(pop) using namespace System; using namespace System::Runtime::InteropServices; public ref class ManagedWrapper { Unmanagedclass* instance; public: ManagedWrapper(String^ arg) { IntPtr mem = Marshal::StringToCoTaskMemAnsi(arg); instance = new Unmanagedclass((char*)(void*)mem); Marshal::FreeCoTaskMem(mem); } ~ManagedWrapper() { delete instance; instance = 0; } !ManagedWrapper() { delete instance; } void mumble() { instance->mumble(); } }; |
这里的秘诀是非托管类的实例是package器中的指针。只需将托管方法调用委托给非托管方法即可。是的,一些带有字符串的hokeypokey,如图所示。并确保当用户或垃圾收集器处理该本地实例时删除该本地实例。
您是否不使用临时变量的地址?如果
1 | LibraryObject.LibraryMethod1() |
返回某个值的副本,然后使用本地变量的地址,该地址在方法末尾超出范围。之后使用该地址是未定义的行为,在这种情况下会导致访问冲突!
您正在使用指向on-stack-temp变量的指针。纯粹靠运气,如果您一个接一个地调用方法,则它指向某种东西。
经过多年的C#舒适度,我正在用c编写代码,并且必须时不时地告诉您我遇到同样的问题。
简而言之-不要使用临时创建的地址,并将其存储在指针中以备后用。周期。