views:

86

answers:

3

I've been converting a bunch of old C++ code into C++/CLI code, and I think I've coded myself into a corner.

My goal was to take an unmanaged c++ library and a bunch of header files and expose their functionality to a C# solution. From reading on the internet, the standard way to do this is to:

  • Create two c++ classes: one managed, the other unmanaged.
  • The unmanaged class will wrangle the objects in the c++ library to provide the desired functionality.
  • The managed class will wrap all of the public methods in the unmanaged class. Each wrapper method would handle the necessary conversions from String^ to string, etc..

But, my scenario isn't very complex, so I decided to just try and implement everything in one class. I am now wrestling with a peculiar problem that generates AccessViolationExceptions.

My header file looks like this:

public ref class ManagedClass
{
public:
  ManagedClass();
  void CreateUnmanagedObject(String^ param1);
  void UseUnmanagedObject();

  UnmanagedObject *myUnmanagedObject;
}

And my cpp file looks like this:

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! */
}

The intriguing thing is that if I call LibraryMethod2 within CreateUnmanagedObject immediately after LibraryMethod1, it works fine. But after CreateUnmanagedObject exits, it seems that the memory pointed to by myUnmanagedObject is lost.

Can anyone see a reason that this is happening?

Edit: Library declarations look like this:

UnmanagedObject LibraryMethod1();
void LibraryMethod2(UnmanagedObject &param);
+1  A: 

Are you not taking the address of a temporary variable? If

LibraryObject.LibraryMethod1()

returns a copy of some value, then you are taking the address of a local variable, which goes out of scope at the end of the method. Using that address afterwards is undefined behavior, in this case causing access violations!

André Caron
Slider345
Did it solve your problem?
André Caron
+1  A: 

Not sure what your real problem might be but it looks all wrong. The managed wrapper should be a pretty close facsimile to the unmanaged one. Let's work from an unmanaged declaration like this:

class Unmanagedclass {
public:
    Unmanagedclass(const char* arg) {}
    void mumble() {}
};

Then your wrapper ought to resemble this:

#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();
    }
};

The recipe here is that the instance of the unmanaged class is a pointer in the wrapper. And just delegate the managed method calls to the unmanaged one. Yes, some hokeypokey with strings, as shown. And making sure that this native instance gets deleted when either the user or the garbage collector gets around to it.

Hans Passant
Hmm, a downvote is *not* exactly what I expected from this post. Care to explain what the problem is?
Hans Passant
@nobugz: +1. Congrats on your 100k achievement. Total domination :O
Joan Venge
Thanks for the code example. This is definitely what I should have done from the outset.
Slider345
A: 

You are taking a pointer to the on-stack-temp variable. By pure luck, it points to something if you call the methods one after another.

I am coding something in c++ after many years of c# comfort, and must tell you that now and then I have same issues.

In short - don't take an address of something that is created temporary and store it in a pointer for later use. Period.

Daniel Mošmondor