关于c cli:C / CLI指针问题=好玩!

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 &param);


不确定您的真正问题是什么,但看起来似乎是错误的。托管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编写代码,并且必须时不时地告诉您我遇到同样的问题。

简而言之-不要使用临时创建的地址,并将其存储在指针中以备后用。周期。