C ++ CLI自动删除非托管对象

时间:2014-01-13 19:54:09

标签: memory-management c++-cli marshalling

我有一个简单的本机类,每9秒在目录中创建一个小文件。

void SampleClass::DoWork(char *directoryPath, SegmentCreatedDelegate callback, bool *cancel)
{
    if(*cancel) return;

    char* segmentFileName = "test%d.ts";

    // open the file to write
    FILE *pFilempegTs = NULL;

    int segmentIndex = 0;
    clock_t beginingTime = clock();
    char currentSegmentPath[256];

    while(!*cancel)
    {
        // create the segment file to write if we haven't already.
        if(pFilempegTs == NULL)
        {
            segmentIndex++;
            strncpy(currentSegmentPath, directoryPath, sizeof(currentSegmentPath));
            strncat(currentSegmentPath, segmentFileName, sizeof(currentSegmentPath));
            sprintf(currentSegmentPath, currentSegmentPath, segmentIndex);
            if( (pFilempegTs  = fopen(currentSegmentPath, "wb" )) == NULL ) 
            {
                std::cout << "The file can not be opened for writing\n";
                return;
            }
        }

        if((double(clock() - beginingTime) / CLOCKS_PER_SEC) >= 9)
        {
            fclose(pFilempegTs);
            pFilempegTs = NULL;
            callback(currentSegmentPath); // the moment I invoke the delegate, the currentSegmentPath gets deleted.
            beginingTime = clock();
        }
    }  

    if(pFilempegTs != NULL)
    {
        fclose(pFilempegTs);
        callback(currentSegmentPath); // the moment I invoke the delegate, the currentSegmentPath gets deleted.
    }

    return;
}

当在c ++ / cli项目中编译本机类并通过.NET包装器调用时,此代码完全按预期工作。

但是,如果本机类是在本机dll(不是c ++ cli)中编译并执行的,那么我在strncpy(currentSegmentPath, directoryPath, sizeof(currentSegmentPath))会收到错误。似乎当我调用SegmentCreatedDelegate时,currentSegmentPath会在内存中被删除。请记住,只有当SampleClass位于本机dll并且使用__declspec( dllexport )从c ++ / cli调用时才会发生这种情况。

注意:SegmentCreatedDelegate是托管到本机的委托,使用以下代码(.NET / c ++ / cli)创建。

void SampleClassNet::DoWork(System::String^ directoryPath, SegmentCreatedDelegateNet^ segmentCreatedCallback, bool% cancel)
{
    SampleClass* nativeClass = new SampleClass();

    System::IntPtr directoryPathPointer = System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(directoryPath);
    char *directoryPathNative = static_cast<char*>(directoryPathPointer.ToPointer());

    System::IntPtr callbackPointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(segmentCreatedCallback);

    pin_ptr<bool> pinnedCancel = &cancel;
    bool* pinnedCancelRef = pinnedCancel;

    nativeClass->DoWork(directoryPathNative, (SegmentCreatedDelegate)(void*)callbackPointer, pinnedCancelRef);

    System::GC::KeepAlive(segmentCreatedCallback);
    System::Runtime::InteropServices::Marshal::FreeHGlobal(directoryPathPointer);
}

为什么只调用SegmentCreatedDelegate删除currentSegmentPath,如果本地方法是在c ++ / cli项目之外编译的?

这是我得到的确切错误消息。

  

尝试读取或写入受保护的内存。这通常是一个   表明其他内存已损坏。

更新

我创建了一个示例项目来重现该问题。

https://bitbucket.org/theonlylawislove/so-c-cli-deleting-unmanaged-object-automatically

更新2

我向我的托管代表添加了[UnmanagedFunctionPointer(CallingConvention::Cdecl)]属性,我的问题似乎已经解决了。这两个项目都使用了cdecl调用约定,所以我不知道为什么需要它。

1 个答案:

答案 0 :(得分:0)

这可能与调用约定发生冲突。正如this MSDN page所述,非托管和混合程序集使用_cdecl调用转换,但对于纯托管程序集,构建器默认使用_clrcall

尝试使用相同的调用约定编译这两个DLL。对于VS 2010,这可以在项目属性 - &gt;中完成。 C / C ++ - &gt;高级 - &gt;召集会议