473,406 Members | 2,467 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,406 software developers and data experts.

C++/CLI, Unmanaged to Managed callback without using a bridge, or pinning pointers.

Hi,

I have a rather interesting problem. I have a unmanged c++ class
which needs to communicate information to managed c++ via callbacks,
with a layer of c# on top of the managed c++ ultimatley retreiving the
data. Presently all of the c++ code is still in .NET 1.1, so we're
using a _nogc bridge class wrapped in a _gc c++ class in order to
facilitate this interop.

But we've converted everything not c++ to .NET 2.0 and would love to
convert the c++ as well, but I'm not sure how to overcome this
problem. My issues...

1.) C++/CLI does not support this _nogc bridge class idea.

2.)I would need to have this pointer around for the life of the
application, which means I would need to pin it, which is a
ridiculously bad idea considering it will be locked on the heap for
the duration.

So I'm not sure how to overcome this. Any help would be greatly
appreciated.

Feb 9 '07 #1
12 12485
I have a rather interesting problem. I have a unmanged c++ class
which needs to communicate information to managed c++ via callbacks,
with a layer of c# on top of the managed c++ ultimatley retreiving the
data. Presently all of the c++ code is still in .NET 1.1, so we're
using a _nogc bridge class wrapped in a _gc c++ class in order to
facilitate this interop.

But we've converted everything not c++ to .NET 2.0 and would love to
convert the c++ as well, but I'm not sure how to overcome this
problem. My issues...

1.) C++/CLI does not support this _nogc bridge class idea.

2.)I would need to have this pointer around for the life of the
application, which means I would need to pin it, which is a
ridiculously bad idea considering it will be locked on the heap for
the duration.
Hi,

First of all, have you tried building your C++ code with /clr:oldSyntax?
That should enable you to port to .NET2.0 and VC2005 without doing anything,
save for a tweak here or there maybe.

I am not sure I understand (1) correctly. In C++/CLI you can still wrap a
managed class around a native class. Do you mean somehing else?

--

Kind regards,
Bruno van Dooren
br**********************@hotmail.com
Remove only "_nos_pam"
Feb 9 '07 #2
On Feb 9, 1:04 am, "Bruno van Dooren [MVP VC++]"
<bruno_nos_pam_van_doo...@hotmail.comwrote:
I have a rather interesting problem. I have a unmanged c++ class
which needs to communicate information to managed c++ via callbacks,
with a layer of c# on top of the managed c++ ultimatley retreiving the
data. Presently all of the c++ code is still in .NET 1.1, so we're
using a _nogc bridge class wrapped in a _gc c++ class in order to
facilitate this interop.
But we've converted everything not c++ to .NET 2.0 and would love to
convert the c++ as well, but I'm not sure how to overcome this
problem. My issues...
1.) C++/CLI does not support this _nogc bridge class idea.
2.)I would need to have this pointer around for the life of the
application, which means I would need to pin it, which is a
ridiculously bad idea considering it will be locked on the heap for
the duration.

Hi,

First of all, have you tried building your C++ code with /clr:oldSyntax?
That should enable you to port to .NET2.0 and VC2005 without doing anything,
save for a tweak here or there maybe.

I am not sure I understand (1) correctly. In C++/CLI you can still wrap a
managed class around a native class. Do you mean somehing else?

--

Kind regards,
Bruno van Dooren
bruno_nos_pam_van_doo...@hotmail.com
Remove only "_nos_pam"- Hide quoted text -

- Show quoted text -
Really? I was under the impression it wasn't supported. Would you
mind showing me an example with CLI syntax?

Feb 9 '07 #3
DaTurk wrote:
Really? I was under the impression it wasn't supported. Would you
mind showing me an example with CLI syntax?
http://tweakbits.com/UnmanagedToManagedCallback.cpp
http://tweakbits.com/ManagedToUnmanagedCallback.cpp

This is how you bridge managed and unmanaged code.

You should only pin an object for a very brief period of time, while
you're accessing or marshalling data. Under no circumstances should you
keep an object pinned for the entire lifetime of the application.

You can no longer put a nested unmanaged class inside a managed one, but
that's just syntax and style. From the technical standpoint, there's
practically nothing that you can't achieve without nested classes,
thanks to the public protected and internal access modifiers.

There is nothing in MC++ that C++/CLI can not do anymore (nesting is not
really a major feature, just a convenience). They just use a different
syntax. How did you solve pinning in your MC++ code? The rules are the
same in C++/CLI. How did you bridge unmanaged and managed classes? You
do exactly the same in C++/CLI, with a different syntax.

Tom
Feb 9 '07 #4
On Feb 9, 11:48 am, Tamas Demjen <tdem...@yahoo.comwrote:
DaTurk wrote:
Really? I was under the impression it wasn't supported. Would you
mind showing me an example with CLI syntax?

http://tweakbits.com/UnmanagedToMana...edCallback.cpp

This is how you bridge managed and unmanaged code.

You should only pin an object for a very brief period of time, while
you're accessing or marshalling data. Under no circumstances should you
keep an object pinned for the entire lifetime of the application.

You can no longer put a nested unmanaged class inside a managed one, but
that's just syntax and style. From the technical standpoint, there's
practically nothing that you can't achieve without nested classes,
thanks to the public protected and internal access modifiers.

There is nothing in MC++ that C++/CLI can not do anymore (nesting is not
really a major feature, just a convenience). They just use a different
syntax. How did you solve pinning in your MC++ code? The rules are the
same in C++/CLI. How did you bridge unmanaged and managed classes? You
do exactly the same in C++/CLI, with a different syntax.

Tom
OK, I see. But here's my question.

static void __stdcall CallbackForwarder(void* param, int arg)
{
static_cast<Thunk*>(param)->receiver->HandleEvent(arg);
}
if this is a ,managed call. How is it the pointer not moving around.
How does it stay pinned? If I'm registering a managed delegate to an
unmanged class, how do I keep that pointer around for the life of the
application for all of the information flowinf back from unmanaged to
managed.

Thankd for that help, I really do appreciate it.

Feb 9 '07 #5
DaTurk wrote:
static void __stdcall CallbackForwarder(void* param, int arg)
{
static_cast<Thunk*>(param)->receiver->HandleEvent(arg);
}
This is a snippet from my
http://tweakbits.com/UnmanagedToManagedCallback.cpp sample.
if this is a ,managed call. How is it the pointer not moving around.
How does it stay pinned?
In the above example, Thunk is not a ref class, it's a native class. It
doesn't require pinning. Remember, * is a native pointer, ^ is a managed
handle.
If I'm registering a managed delegate to an
unmanged class
Then you need to look at my other sample,
http://tweakbits.com/ManagedToUnmanagedCallback.cpp
how do I keep that pointer around for the life of the
application
Once again:

UnmanagedToManagedCallback.cpp:
When your receiver is managed, it is gcroot-ed in the unmanaged sender
(the Thunk class, in my example). gcroot-ed classes are fully managed,
they don't require pinning. This is Microsoft's solution for embedding a
managed object inside an unmanaged one. The same gcroot template existed
back in the old MC++. There's no difference between MC++ and C++/CLI
here. The way it works is that the native class stores just a 32-bit (or
64-bit) handle to the managed object, but it's not an actual memory
address. When the object gets moved around, its handle value still stays
the same.

ManagedToUnmanagedCallback.cpp:
When your receiver is unmanaged, you don't have to pin that, since
unmanaged classes don't get moved.

Maybe you don't fully understand when pinning is required. You only pin
a managed object when you directly deal with its memory address. For
example:

array<int>^ arr = gcnew array<int>(10); // managed array
pin_ptr<intp = &arr[0]; // pin it
int* native_ptr_to_managed = p; // unmanaged pointer to the array
// Source: http://msdn2.microsoft.com/en-us/library/1dz8byfh.aspx

Note how in this example you are accessing a managed object's memory
address directly via an unmanaged pointer. This requires pinning, or
else the memory address of the managed object might change.

The value of a managed handle never changes, so it can be stored inside
a native object without pinning:

ref class Managed { };

class Native
{
[...]
gcroot<Managed^managed;
};

If "managed" moves around, its handle value remains the same.

Tom
Feb 10 '07 #6
On Feb 9, 6:21 pm, Tamas Demjen <tdem...@yahoo.comwrote:
DaTurk wrote:
static void __stdcall CallbackForwarder(void* param, int arg)
{
static_cast<Thunk*>(param)->receiver->HandleEvent(arg);
}

This is a snippet from myhttp://tweakbits.com/UnmanagedToManagedCallback.cppsample.
if this is a ,managed call. How is it the pointer not moving around.
How does it stay pinned?

In the above example, Thunk is not a ref class, it's a native class. It
doesn't require pinning. Remember, * is a native pointer, ^ is a managed
handle.
If I'm registering a managed delegate to an
unmanged class

Then you need to look at my other sample,http://tweakbits.com/ManagedToUnmanagedCallback.cpp
how do I keep that pointer around for the life of the
application

Once again:

UnmanagedToManagedCallback.cpp:
When your receiver is managed, it is gcroot-ed in the unmanaged sender
(the Thunk class, in my example). gcroot-ed classes are fully managed,
they don't require pinning. This is Microsoft's solution for embedding a
managed object inside an unmanaged one. The same gcroot template existed
back in the old MC++. There's no difference between MC++ and C++/CLI
here. The way it works is that the native class stores just a 32-bit (or
64-bit) handle to the managed object, but it's not an actual memory
address. When the object gets moved around, its handle value still stays
the same.

ManagedToUnmanagedCallback.cpp:
When your receiver is unmanaged, you don't have to pin that, since
unmanaged classes don't get moved.

Maybe you don't fully understand when pinning is required. You only pin
a managed object when you directly deal with its memory address. For
example:

array<int>^ arr = gcnew array<int>(10); // managed array
pin_ptr<intp = &arr[0]; // pin it
int* native_ptr_to_managed = p; // unmanaged pointer to the array
// Source:http://msdn2.microsoft.com/en-us/library/1dz8byfh.aspx

Note how in this example you are accessing a managed object's memory
address directly via an unmanaged pointer. This requires pinning, or
else the memory address of the managed object might change.

The value of a managed handle never changes, so it can be stored inside
a native object without pinning:

ref class Managed { };

class Native
{
[...]
gcroot<Managed^managed;

};

If "managed" moves around, its handle value remains the same.

Tom
OK, I put a post in the main forum, but I was curious if you knew. I
see what your saying. So I moved my native bridge class outside of
the managed class. Now to make it work, I have the bridge class
contain a gcrooted reference to my managed class. But I want it to be
static, but I get a couple linker errors. Apparently you can't have a
static gcrooted managed variable in a native class. Do you know of
any work around?

Feb 13 '07 #7
DaTurk wrote:
But I want it to be
static, but I get a couple linker errors. Apparently you can't have a
static gcrooted managed variable in a native class. Do you know of
any work around?
What's the exact error? Have you initialized the gcroot? You have to
initialize it in your .cpp file, like this:

// .h
ref class Managed { };

class Native
{
static gcroot<Managed^managed;
};

// .cpp
gcroot<Managed^Native::managed(gcnew Managed);

Tom
Feb 13 '07 #8
I'll fiddle with it, I was getting a linking error. I was using
something similar to this

msclr::auto_gcroot<ManagedReceiver^receiver;

and as soon as I threw static in front it was not happy.

On another note, I've found a different solution to the problem and I
was hoping you could help explain exactly what is going on.

So, my problem is that I have a native class that needs to
communicate, via callbacks, asynchronously to a managed C++/CLI class,
which then communicates to C#.

Originally with MC++ we were using the bridge class, and now that
we've moved, or are moving to CLI we need to find a different
approach. So while I was fiddling, I did this. I just included a
seperate function outside of the managed class in a seperate
namespace. And it uses the managed class static reference to itself
to invoke it's callbacks, and it works. But why has no one mentioned
this before? It's the best solution I;ve seen yet, what's wrong with
it? Here's some code.
namespace ManagedLib
{
[StructLayout(LayoutKind::Sequential, CharSet=CharSet::Ansi, Pack=8)]
public ref struct MStruct
{
public:
System::Int32 x;
System::Int32 y;

[MarshalAs(UnmanagedType::ByValTStr, SizeConst=50)]
System::String^ str;

virtual String^ ToString() override
{
return String::Format("X:{0} Y:{1} STR:
{2}",Convert::ToString(x),Convert::ToString(y),str );
}
};

public delegate void DataChangedEventHandler(MStruct^ item);

public ref class MClass
{
private:
UnmanagedLib::UClass* pUnmanagedClass_ ;

public:
MClass()
{
pUnmanagedClass_ = new UClass(); //unmanaged heap
//pUnmanagedClass_-
>RegisterUnmanagedCallBack(&(_UClassBridge::Unmana gedBridgeCallback));
pUnmanagedClass_-
>RegisterUnmanagedCallBack(&(UnmanagedCallbackMeth ods::UnmanagedBridgeCallback));
pMe_ = this;
}

~MClass(){
delete pUnmanagedClass_;
};

static MClass^ pMe_ = nullptr;
DataChangedEventHandler^ pManagedDelegate_;

void RunSimulation()
{
MStruct^ item = gcnew MStruct();
item->x = 1;
item->y = 2;
item->str = "Test";

for(int i = 0;i< 100000;i++)
{
item->x = i+1;

UStruct uItem;
Marshal::StructureToPtr(item, (System::IntPtr)&uItem, true);

pUnmanagedClass_->FireUnmanagedCallback(uItem);
Thread::Sleep(100);
}
}
};
}

namespace UnmanagedCallbackMethods
{
static void UnmanagedBridgeCallback(const UStruct item)
{
Console::WriteLine("UnmanagedBridgeCallback: Fired");

UStruct uItem = item;
ManagedLib::MStruct^ mItem = gcnew ManagedLib::MStruct();
Marshal::PtrToStructure((System::IntPtr)&uItem, mItem);

ManagedLib::MClass::pMe_->pManagedDelegate_->Invoke(mItem);
}
};

Thanks again for all the help.

Feb 13 '07 #9
DaTurk wrote:
But why has no one mentioned this before?
Because your solution only works with singleton classes. As soon as you
have two objects in the memory, it stops working. My original example
didn't have that restriction. It was generic enough to track an
unlimited number of objects.

Of course, use what fits your needs. Glad you've worked it out.

Tom
Feb 13 '07 #10
On Feb 13, 4:06 pm, Tamas Demjen <tdem...@yahoo.comwrote:
DaTurk wrote:
But why has no one mentioned this before?

Because your solution only works with singleton classes. As soon as you
have two objects in the memory, it stops working. My original example
didn't have that restriction. It was generic enough to track an
unlimited number of objects.

Of course, use what fits your needs. Glad you've worked it out.

Tom
Ah, well I can live with that. We're connecting to C libraries at the
bottom, where all the callbacks are static! But how does this
solution work?
We have the native function which we manage with the native class, and
then because it has access to the managed class we're all good yes?
At this point we're in managed land, and because the managed pointer
is being mantained by the managed class we don't have to worry about
the address changing it because CLI takes care of pointing us in the
right direction again. Is this correct?

Feb 13 '07 #11
DaTurk wrote:
At this point we're in managed land, and because the managed pointer
is being mantained by the managed class we don't have to worry about
the address changing it because CLI takes care of pointing us in the
right direction again. Is this correct?
That's correct. A managed handle never gets invalidated due to the GC
moving stuff around in the memory. Even when it's used from unmanaged
code, it will work. That's because a managed handle is not a plain
direct memory address.

The only case when you have to worry about objects moving around is when
you get a native pointer to a managed object. For example, when you get
a char* to a System::String^, or an int* to a
cli::array<System::Int32>^. At the moment you start working with raw
memory addresses, you are detached from the garbage collector. That's
why you have to pin a managed object prior to obtaning its memory
address, and keep it pinned as long as the raw pointer is used. The
expression " Managed ^ " does not represent such a raw pointer.

Tom
Feb 14 '07 #12
On Feb 13, 7:10 pm, Tamas Demjen <tdem...@yahoo.comwrote:
DaTurk wrote:
At this point we're in managed land, and because the managed pointer
is being mantained by the managed class we don't have to worry about
the address changing it because CLI takes care of pointing us in the
right direction again. Is this correct?

That's correct. A managed handle never gets invalidated due to the GC
moving stuff around in the memory. Even when it's used from unmanaged
code, it will work. That's because a managed handle is not a plain
direct memory address.

The only case when you have to worry about objects moving around is when
you get a native pointer to a managed object. For example, when you get
a char* to a System::String^, or an int* to a
cli::array<System::Int32>^. At the moment you start working with raw
memory addresses, you are detached from the garbage collector. That's
why you have to pin a managed object prior to obtaning its memory
address, and keep it pinned as long as the raw pointer is used. The
expression " Managed ^ " does not represent such a raw pointer.

Tom
Well, thank you for all your help. It was really appreciated.

Feb 14 '07 #13

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

0
by: Bruce Farmer | last post by:
I am having problems accessing a managed object from unmanaged code. Specifically, I have a .NET forms application with a web service proxy. The forms application links to an unmanaged DLL which...
3
by: zhphust | last post by:
I want to convert a object of a managed class to a unmanaged structure that has the same member with that managed class. Can anybody tell me how i can do it? Thanks in advance. -- zhphust...
10
by: Bonj | last post by:
I use the following code to convert a managed string to an unmanaged one: _TCHAR error; const char* umstring = (const char*)Marshal::StringToHGlobalAnsi (merror).ToPointer(); _tcscpy(error,...
4
by: William F. Kinsley | last post by:
My understanding is that when I re-compile a existing MFC application with the /clr switch, that the code generated is managed(with some exceptions) but that the data isn't, i.e. not garbage...
2
by: Sasha Nikolic | last post by:
I have one unmanaged and one managed class in the same vc++ project. Managed object creates one instance of unmanaged class and needs to pass a callback reference so that unmanaged class can...
1
by: quat | last post by:
I have two unmanaged pointer in a managed form class: IDirect3D9* d3dObject; IDirect3DDevice9* d3dDevice; In a member function of the form, I call: d3dObject->CreateDevice(...
7
by: DaTurk | last post by:
Hi, I'm coding up an application that has a native c++ layer, asynchronously calling callback in a CLI layer. We originally did this with static inline methods in the CLI layer, but this...
20
by: =?Utf-8?B?VGhlTWFkSGF0dGVy?= | last post by:
Sorry to bring up a topic that is just flogging a dead horse.... but... On the topic of memory management.... I am doing some file parcing that has to be done as quick as posible, but what I...
9
by: =?Utf-8?B?RWR3YXJkUw==?= | last post by:
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...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
by: Hystou | last post by:
Most computers default to English, but sometimes we require a different language, especially when relocating. Forgot to request a specific language before your computer shipped? No problem! You can...
0
jinu1996
by: jinu1996 | last post by:
In today's digital age, having a compelling online presence is paramount for businesses aiming to thrive in a competitive landscape. At the heart of this digital strategy lies an intricately woven...
0
tracyyun
by: tracyyun | last post by:
Dear forum friends, With the development of smart home technology, a variety of wireless communication protocols have appeared on the market, such as Zigbee, Z-Wave, Wi-Fi, Bluetooth, etc. Each...
0
isladogs
by: isladogs | last post by:
The next Access Europe User Group meeting will be on Wednesday 1 May 2024 starting at 18:00 UK time (6PM UTC+1) and finishing by 19:30 (7.30PM). In this session, we are pleased to welcome a new...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.