What is proper way to pass a managed object into unmanaged code?

E

EdwardS

I would greatly appreciate some help on passing managed object into unmanaged
code.
I need to pass a reference (address of) of a managed class into unmanaged
code (written by a thrid party). The 3rd party unmanaged DLL will pass this
reference into standard Win32 unmanaged static callback function in my code.
Inside this unmanaged callback function I need to cast this unmnaged pointer
that I have received from 3rd party back into the reference to a managed
class, so I could use member variables and functions of that managed class.

I do not have source code for the third party DLL, but our applicatin was
working perfectly fine with that thrid party DLL when it was built with all
unmanaged C++ code.

Here is my managed C++ class declaration inside Wrapper.h:

namespace MyNamespace
{
public ref class Wrapper
{
unsigned long m_hSource;
public:
Wrapper();
bool SetupCallback();
};
}

Here is code inside Wrapper.cpp

unsigned long __stdcall CallBackFunc(const void * pData, unsigned Context )
{
gcroot<eWCamera_Canon::CanonWrapper^> cwThis;

// Here is the place in question for casting back into managed class

// use m_hSource to delegate to C# User Interface
}

bool MyNameSpace::Wrapper::SetupCallback()
{
unsigned int err;

// Registering callback with 3rd party DLL
err = RegisterCallback(CallBackFunc, this);
}
 
M

Mark Salsbery [MVP]

EdwardS said:
I would greatly appreciate some help on passing managed object into
unmanaged
code.
I need to pass a reference (address of) of a managed class into unmanaged
code (written by a thrid party). The 3rd party unmanaged DLL will pass
this
reference into standard Win32 unmanaged static callback function in my
code.
Inside this unmanaged callback function I need to cast this unmnaged
pointer
that I have received from 3rd party back into the reference to a managed
class, so I could use member variables and functions of that managed
class.


It seems to me it would be easier to use gcroot to store a managed reference
in an unmanaged object which is passed through the callback mechanism. Then
in the callback, you can use the gcroot-held managed reference as usual.

Here's an example (namespace removed for clarity)...

class Wrapper
{
unsigned long m_hSource;

public:
gcroot<eWCamera_Canon::CanonWrapper^> ManagedReference;

Wrapper();
bool SetupCallback();
};

unsigned long __stdcall CallBackFunc(const void * pData, unsigned Context )
{
// Get the unmanaged wrapper pointer
Wrapper *pWrapper = (Wrapper *)pData;

// Use the wrapped managed object
pWrapper->ManagedReference->...
}

bool Wrapper::SetupCallback()
{
unsigned int err;

// Registering callback with 3rd party DLL
err = RegisterCallback(CallBackFunc, this);
}




Mark
 
E

EdwardS

Mark,
Thanks for reply, I just realized that while cleaning code for you, I did
not clean everything (<eWCamera_Canon::CanonWrapper^> is actually
<MyNamespace::Wrapper^>, I think that created a lot of confusion. Could you
take a look at this code again, I greatly appreciate your help. Does it
change your response in anyway? In the following code Wrapper must be a
managed class, that is the whole point of my question - how to pass managed
object (pass itself) into unmanaged area and later be casted into the managed
type of itself, so that member methods of that managed class could be used
inside that unmanaged static callback function?

namespace MyNamespace
{
public ref class Wrapper
{
unsigned long m_hSource;
public:
Wrapper();
bool SetupCallback();
};
}

Here is code inside Wrapper.cpp

unsigned long __stdcall CallBackFunc(const void * pData, unsigned Context )
{
gcroot<MyNamespace::Wrapper^> cwThis;

// Here is the place in question for casting back into managed class

// use m_hSource to delegate to C# User Interface
}

bool MyNamespace::Wrapper::SetupCallback()
{
unsigned int err;

// Registering callback with 3rd party DLL
err = RegisterCallback(CallBackFunc, this);
}
 
M

Mark Salsbery [MVP]

EdwardS said:
Mark,
Thanks for reply, I just realized that while cleaning code for you, I did
not clean everything (<eWCamera_Canon::CanonWrapper^> is actually
<MyNamespace::Wrapper^>, I think that created a lot of confusion.

Yes - I wasn't quite sure what needed to be managed so I used what was there
to demonstrate the basic framework of what I was referring to :)
Could you
take a look at this code again, I greatly appreciate your help. Does it
change your response in anyway? In the following code Wrapper must be a
managed class, that is the whole point of my question - how to pass
managed
object (pass itself) into unmanaged area and later be casted into the
managed
type of itself, so that member methods of that managed class could be used
inside that unmanaged static callback function?

You really can't do that. There's no way for the system to track the
managed reference once you pass it to unmanaged code. That's why I kind of
flipped the way you were attempting it so an unmanaged wrapper could go
through the unmanaged callback mechanism, carrying with it the managed
reference stored with gcroot.

Mark
 
E

EdwardS

One more clarification to my code - inside Callback function, I would like to
cast Context which now holds the pointer to the managed object into
<MyNamespace::Wrapper^> type. Origianly pointer was passed inside the
RegisterCallback function in MyNamespace::Wrapper::SetupCallback() method of
the <MyNamespace::Wrapper^> class.

unsigned long __stdcall CallBackFunc(const void * pData, unsigned Context )
{
gcroot<MyNamespace::Wrapper^> cwThis;

// Here is the place in question for casting Context back
// into managed class cwThis of type <MyNamespace::Wrapper^>

// use m_hSource to delegate to C# User Interface
// where m_hSource will do something with pData,
// or pData converted into m_hSource (that part does not matter)
}
 
M

Mark Salsbery [MVP]

EdwardS said:
One more clarification to my code - inside Callback function, I would like
to
cast Context which now holds the pointer to the managed object into
<MyNamespace::Wrapper^> type.


That won't work either. The only way you can pass a pointer to a managed
object to unmanaged code is with a pin_ptr. The problem is, a pin_ptr only
exists until it goes out of scope, and I don't think that's going to work
for this unmanaged callback scenario, since a pin_ptr would go out of scope
at the end of SetupCallback().

Mark
 
E

EdwardS

Thanks, Mark... I tried pin_ptr... I know... I thought that my scenario would
not work. I just wanted to make sure. I will try to figure out a work
around...
Thank you very much for your help.
 
T

Tamas Demjen

Mark said:
That won't work either. The only way you can pass a pointer to a
managed object to unmanaged code is with a pin_ptr. The problem is, a
pin_ptr only exists until it goes out of scope, and I don't think that's
going to work for this unmanaged callback scenario, since a pin_ptr
would go out of scope at the end of SetupCallback().

If a managed object needs to be pinned for a longer amount of time, the
GCHandle class can be used:

GCHandle gch = GCHandle::Alloc(my_object, GCHandleType::pinned);

In the end it has to be freed:
gch.Free();

This can give a longer pinning lifetime than pin_ptr. On the other hand,
I would advice against pinning a lot of objects for an extensive amount
of time.

References:
http://msdn2.microsoft.com/en-us/library/83y4ak54.aspx
http://msdn2.microsoft.com/en-us/library/83y4ak54.aspx

Tom
 
M

Mark Salsbery [MVP]

Tamas Demjen said:
If a managed object needs to be pinned for a longer amount of time, the
GCHandle class can be used:


Right. That's why I originally suggested to the OP to store it using
gcroot<> (which uses GCHandle but is cleaner than the messy GCHandle stuff
IMO) :)

He was still looking for a way to pass a managed pointer to unmanaged code,
however :)

Cheers,
Mark
 
E

EdwardS

I greatly appreciate your help.

Refering to you first response where you did everything inside unmanaged
code (the same way we used to do before with all unmanaged C++), what I tried
to do couple of days ago was to go that route, but in order to pass the
callback to the calling C# exe, I have created an outer managed class inside
my DLL that would deal with talking to C# exe, and that outer managed C++ had
a reference to the old code unmanaged class. But when I started running that
code, then at the point when I tried registering my callback static function,
the debugger returned following:

System.AccessViolationException was unhandled
Message: Attempted to read or write protected memory. This is often an
indication that other memory is corrupt.

For reference - here is the actual code (point of my frastrations), it is
used to initialize and connect to Canon G6 camera, and it is a call to Canon
SDK, written originally in C++ with no more support for these cameras (I
removed handling of errors):

bool CanonCamera::Connect()
{
unsigned long err;

// get camera information inside SSrcInfo and Camera reference m_hSource
err = CDOpenSource(&SSrcInfo, &m_hSource);

// lock camera UI.
err = CDLockUI(m_hSource);

// register this unmanaged C++ class and open camera lens
// this operation takes about 3 seconds to complete and at the
// end it breaks down in debugger with above AccessViolationException
err = CDEnterReleaseControl(m_hSource, RelCallBackFunc, (unsigned int)this);

// unlock camera UI.
err = CDUnlockUI(m_hSource);
}

When I tried not to send pointer to "this" into to Canon SDK, but instead
replaced it with 0:
err = CDEnterReleaseControl(m_hSource, RelCallBackFunc, 0);
then what I got was intermittently it did not work in both C++ and managed
C++, so it is not an option not send an address of itself (this).

Unfortunately Canon does not support this code anymore, and did not help us.

Mark,
would you have any suggestions in our frustrating situation? Unfortuantely
for us newer Cameras G7 and G9 (that have newer OS) but have half battery
life of G6... Go figure...
 
M

Mark Salsbery [MVP]

EdwardS said:
I greatly appreciate your help.


No problem :) Hopefully I'll be able to actually help.

After reading your original post again, and reading this post, I think Tamas
had it right. The gcroot isn't necessary but passing a GCHandle is
necessary.

What about something like this (from your original posted code) - Note I
added a dummy method (SomeMethod() and an example of calling it from the
callback)...

//** Start Code ************************************************

namespace MyNameSpace
{
public ref class Wrapper
{
unsigned long m_hSource;

GCHandle gchThis;
public:
Wrapper();
bool SetupCallback();

void SomeMethod() {}
};
}

unsigned long __stdcall CallBackFunc(const void * pData, unsigned Context )
{
GCHandle gchWrapper = GCHandle::FromIntPtr((IntPtr)(int)Context);

MyNameSpace::Wrapper ^wrapper = (MyNameSpace::Wrapper ^)gchWrapper.Target;

wrapper->SomeMethod();

return 0;
}

bool MyNameSpace::Wrapper::SetupCallback()
{
unsigned int err;

gchThis.Alloc(this); // Note: gchThis.Free() should be called as soon as the
GCHandle isn't needed!!!

// Registering callback with 3rd party DLL
err = RegisterCallback(&CallBackFunc,
(unsigned)(int)GCHandle::ToIntPtr(gchThis));

return true;
}

//** End Code ************************************************

Does that do what you need?

Mark
 
M

Mark Salsbery [MVP]

In my previous example, the Alloc() call in
MyNameSpace::Wrapper::SetupCallback() should be changed to

gchThis = GCHandle::Alloc(this); // Note: gchThis.Free() should be
called as soon as the GCHandle isn't needed!!!

sorry :)

Mark
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top