0
votes

I have a simple native class that creates small files in a directory every 9 seconds.

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;
}

This code works exactly as expected when the native class is compiled in a c++/cli project, and invoked via a .NET wrapper.

However, I get an error at strncpy(currentSegmentPath, directoryPath, sizeof(currentSegmentPath)) if the native class is compiled in a native dll (not c++ cli) and executed. It seems that when I invoke the SegmentCreatedDelegate, the currentSegmentPath gets deleted in memory. Remember, this only happens when this SampleClass is in a native dll and called from c++/cli with __declspec( dllexport ).

NOTE: The SegmentCreatedDelegate is a managed-to-native delegate, created using the following code (.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);
}

Why does invoking the SegmentCreatedDelegate delete currentSegmentPath, ONLY if the native method is compiled outside of the c++/cli project?

This is the exact error message I get.

Attempted to read or write protected memory. This is often an indication that other memory is corrupt.

UPDATE

I created a sample project to reproduce the issue.

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

UPDATE 2

I added a [UnmanagedFunctionPointer(CallingConvention::Cdecl)] attribute to my managed delegate and my issue seemed to be resolve. Both projects are using the cdecl calling convention, so I don't know why this was needed.

1
Don't do (SegmentCreatedDelegate)(void*)callbackPointer on an IntPtr. Instead, (SegmentCreatedDelegate)callbackPointer.ToPointer() - Ben Voigt
Please also show the typedef of SegmentCreatedDelegate - Ben Voigt
You got the delegate declaration wrong in your previous question, doesn't sound like you made progress. That your previous code didn't crash was an unlucky accident. The 'currentSegmentPath' variable can't be "deleted", it is a local variable. You've been stuck on this long enough, to get the help you need you must post a small repro solution that demonstrates this issue to a file sharing service so we can actually repro the problem. - Hans Passant
I created a sample project that you can reproduce the issue with. Run the program in release mode. bitbucket.org/theonlylawislove/… - Paul Knopf

1 Answers

0
votes

It could be a conflict with calling conventions. As this MSDN page says, unmanaged and mixed assemblies uses _cdecl calling convertions, but for pure managed assemblies the builder uses _clrcall by default.

Try to compile both DLLs with the same calling convention. For VS 2010 this can be done into Project properties -> C/C++ -> Advanced -> Calling Convention.