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.
(SegmentCreatedDelegate)(void*)callbackPointer
on anIntPtr
. Instead,(SegmentCreatedDelegate)callbackPointer.ToPointer()
- Ben VoigtSegmentCreatedDelegate
- Ben Voigt