473,327 Members | 2,090 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,327 software developers and data experts.

BeginInvoke on a delegate

I'm trying to call a function on a background thread in a C++/CLI (VS 2005)
application compiled with /clr. I store a delegate reference in a module-level
gcroot. When I call BeginInvoke on the delegate I get a RemotingException. The
inner exception says "Pointer types cannot be passed in a remote call". A code
example that demonstrates the problem appears below.

Am I even wiring up the delegate/BeginInvoke/EndInvoke stuff correctly? The
documentation is pretty sketchy.

TIA - Bob

----- Code example -----

#include <gcroot.h>

using namespace System;

// Class with no members
class MyClass {
};

// Forward references
void BackgroundWorker(MyClass& myClass);
void _Callback(IAsyncResult ^ar);

// Module-level data
delegate void BackgroundWorkerDelegType(MyClass& myClass);

static gcroot<BackgroundWorkerDelegType^>
m_BackgroundWorkerDeleg
= gcnew BackgroundWorkerDelegType(&BackgroundWorker);

static gcroot<AsyncCallback^>
m_callbackDeleg
= gcnew AsyncCallback(&_Callback);

// Main program
int main(array<System::String ^^args)
{
MyClass myClass;
// RemotingException: Pointer types cannot be passed in a remote call
m_BackgroundWorkerDeleg->BeginInvoke(myClass, m_callbackDeleg, nullptr);
return 0;
}

void BackgroundWorker(MyClass& myClass)
{
Console::WriteLine("In BackgroundWorker");
}

// Routine called when the asynchronous call completes
void _Callback(IAsyncResult ^ar)
{
m_BackgroundWorkerDeleg->EndInvoke(ar);
}
Jun 27 '08 #1
9 3938
I simplified the sample app by removing all of the gcroot stuff. The resultant
app still gets the same error. I'm obviously doing something fundamentally
wrong, but the docs make no mention of how to use BeginInvoke with a C++
delegate.

----- New sample app ------

#include <gcroot.h>

using namespace System;

// Class with no members
class MyClass {
};

// Forward references
void BackgroundWorker(MyClass& myClass);
void Callback(IAsyncResult ^ar);

// Module-level data
delegate void BackgroundWorkerDelegType(MyClass& myClass);

// Main program
int main(array<System::String ^^args)
{
MyClass myClass;
BackgroundWorkerDelegType^ d = gcnew
BackgroundWorkerDelegType(&BackgroundWorker);
AsyncCallback^ cb = gcnew AsyncCallback(&Callback);
// RemotingException: Pointer types cannot be passed in a remote call
d->BeginInvoke(myClass, cb, d);
return 0;
}

void BackgroundWorker(MyClass& myClass)
{
Console::WriteLine("In BackgroundWorker");
}

// Routine called when the asynchronous call completes
void Callback(IAsyncResult ^ar)
{
BackgroundWorkerDelegType^ d =
static_cast<BackgroundWorkerDelegType^>(ar->AsyncState);
d->EndInvoke(ar);
}
Jun 27 '08 #2
It occurs to me that the problem probably has to do with the fact that the
routine I am trying to call with BeginInvoke takes an argument that is a
reference to a native object. Delegates, being part of the CLR, probably
don't know how do deal with native objects. So, how can I pass a native
object (in my actual application it's an STL map) to a routine that I want
to execute on a background (thread pool) thread?

And, if my guess (above) is correct, why does is happily compile? You'd
think that the compiler would complain when it sees me trying to create a
delegate with a native object reference as an argument.

Or maybe I'm still totally clueless and totally off base...

Jun 27 '08 #3
Hi Bob,
Your analysis is correct. The problem here is that the native type object
"myClass" is passed as a parameter to BeginInvoke. There are two ways to
work around this issue, one is changing your MyClass type from native to
managed, and the other one is using a wrapper managed class to wrap your
native type object. For example:
=====================================
class MyClass {
public:
int Age;
string Name;
};

template <typename T>
ref struct MyWrapperClass
{
MyWrapperClass() : m_ptr(0) {}
MyWrapperClass(T* ptr) : m_ptr(ptr) {}
MyWrapperClass(MyWrapperClass<T>% right) : m_ptr(right.Release())
{
}
T* operator->()
{
return m_ptr;
}

T* Get()
{
return m_ptr;
}

T* Release()
{
T* released = m_ptr;
m_ptr = 0;
return released;
}

private:
T* m_ptr;
};
// Forward references
void BackgroundWorker(MyWrapperClass<MyClassmyWrapperCl s);
void Callback(IAsyncResult ^ar);

// Module-level data
delegate void BackgroundWorkerDelegType(MyWrapperClass<MyClass>
myWrapperCls);

// Main program
int main(array<System::String ^^args)
{
MyClass myClass;
myClass.Age = 26;
myClass.Name="Bill";

MyWrapperClass<MyClassmyWrapperCls(&myClass);
BackgroundWorkerDelegType^ d = gcnew
BackgroundWorkerDelegType(BackgroundWorker);
AsyncCallback^ cb = gcnew AsyncCallback(Callback);
d->BeginInvoke(myWrapperCls, cb, d);

Console::Read();
return 0;
}

void BackgroundWorker(MyWrapperClass<MyClassmyWrapperCl s)
{
Console::WriteLine("In BackgroundWorker");
Console::WriteLine(myWrapperCls->Age);
Console::WriteLine(myWrapperCls->Name.c_str());
}

// Routine called when the asynchronous call completes
void Callback(IAsyncResult ^ar)
{
BackgroundWorkerDelegType^ d =
static_cast<BackgroundWorkerDelegType^>(ar->AsyncState);
d->EndInvoke(ar);
}
================================================

For mix using native and managed types, you may also refer to this article:
Best Practices for Writing Efficient and Reliable Code with C++/CLI
http://msdn.microsoft.com/en-us/libr...37(VS.80).aspx

Hope this helps. If you have any other questions or concerns, please feel
free to let me know.

Best regards,
Charles Wang
Microsoft Online Community Support
================================================== =========
Delighting our customers is our #1 priority. We welcome your
comments and suggestions about how we can improve the
support we provide to you. Please feel free to let my manager
know what you think of the level of service provided. You can
send feedback directly to my manager at: ms****@microsoft.com.
================================================== =========
Get notification to my posts through email? Please refer to
http://msdn.microsoft.com/subscripti...ult.aspx#notif
ications.

Note: The MSDN Managed Newsgroup support offering is for
non-urgent issues where an initial response from the community
or a Microsoft Support Engineer within 1 business day is acceptable.
Please note that each follow up response may take approximately
2 business days as the support professional working with you may
need further investigation to reach the most efficient resolution.
The offering is not appropriate for situations
that require urgent, real-time or phone-based interactions or complex
project analysis and dump analysis issues. Issues of this nature are best
handled working with a dedicated Microsoft Support Engineer by
contacting Microsoft Customer Support Services (CSS) at
http://msdn.microsoft.com/subscripti...t/default.aspx.
================================================== ==========
This posting is provided "AS IS" with no warranties, and confers no rights.
================================================== =======


Jun 27 '08 #4
Thanks Charles, that is exactly what I'm looking for!

I looked at the article you referenced in your reply
(http://msdn.microsoft.com/en-us/libr...37(VS.80).aspx) and that
author provides a more complete implementation of your managed wrapper class
(shown below). I have a couple of questions about his code:

1. Why does the destructor call "delete m_ptr"? This wrapper didn't
allocate that memory. In my application, I have a module-level STL map that
I pass as an argument to some function. That function needs to wrap the map
reference in this managed wrapper so that it can pass it as an argument to a
managed function (in my case [delegate].BeginInvoke). But I certainly don't
want the managed wrapper to try to delete my map when the wrapper's
destructor is called!

2. What's the "!AutoPtr()" thing all about?

TIA - Bob

----- Code -----

template <typename T>
ref struct AutoPtr
{
AutoPtr() : m_ptr(0) {}
AutoPtr(T* ptr) : m_ptr(ptr) {}
AutoPtr(AutoPtr<T>% right) : m_ptr(right.Release()) {}

~AutoPtr()
{
delete m_ptr;
m_ptr = 0;
}
!AutoPtr()
{
ASSERT(0 == m_ptr);
delete m_ptr;
}
T* operator->()
{
ASSERT(0 != m_ptr);
return m_ptr;
}

T* Get()
{
return m_ptr;
}
T* Release()
{
T* released = m_ptr;
m_ptr = 0;
return released;
}
void Reset()
{
Reset(0);
}
void Reset(T* ptr)
{
if (ptr != m_ptr)
{
delete m_ptr;
m_ptr = ptr;
}
}

private:
T* m_ptr;
};

Jun 27 '08 #5
Bob Altman wrote:
I looked at the article you referenced in your reply
(http://msdn.microsoft.com/en-us/libr...37(VS.80).aspx) and that
author provides a more complete implementation of your managed wrapper
class (shown below). I have a couple of questions about his code:

1. Why does the destructor call "delete m_ptr"? This wrapper didn't
allocate that memory.
Those are the semantics of the class -- passing it a pointer means it'll
take ownership of the allocated memory. The STL's auto_ptr works the same
way. If that's not what you want, you are of course free not to do it.
However, take care that in doing this, it's possible for the managed class
to hold a dangling pointer if you free the object while it's still
referenced from managed code. This, as you can imagine, is very bad.
2. What's the "!AutoPtr()" thing all about?
That's the managed finalizer, not to be confused with the destructor. When
the object is garbage collected, the finalizer will be run. You typically
don't need this because C++ encourages you to manage objects explicitly, so
the destructor would take care of releasing resources.

--
J.
Jun 27 '08 #6

"Jeroen Mostert" <jm******@xs4all.nlwrote in message
news:48***********************@news.xs4all.nl...
Bob Altman wrote:
>I looked at the article you referenced in your reply
(http://msdn.microsoft.com/en-us/libr...37(VS.80).aspx) and that
author provides a more complete implementation of your managed wrapper class
(shown below). I have a couple of questions about his code:

1. Why does the destructor call "delete m_ptr"? This wrapper didn't allocate
that memory.

Those are the semantics of the class -- passing it a pointer means it'll take
ownership of the allocated memory. The STL's auto_ptr works the same way.
Aha! That also explains why the copy constructor modifies the "right" object by
clearing its pointer to the native data. That's what the author meant by
"transfer of ownership semantics".

But that still leaves the question of whether or not it's even correct to call
delete on the pointer. What if the native object was allocated on the stack,
e.g.

void MySub() {
MyNativeClass c;
MyOtherSub(c);
}

void MyOtherSub(MyNativeClass& c) {
// At this point we have no idea whether
// c was allocated on the stack or the heap
AutoPtr<MyNativeClassp(&c);
}

Assuming I got the syntax at least kind of correct, what will happen when the
AutoPtr goes out of scope and its destructor tries to delete c?
Jun 27 '08 #7
Bob Altman wrote:
"Jeroen Mostert" <jm******@xs4all.nlwrote in message
news:48***********************@news.xs4all.nl...
>Bob Altman wrote:
>>I looked at the article you referenced in your reply
(http://msdn.microsoft.com/en-us/libr...37(VS.80).aspx) and that
author provides a more complete implementation of your managed wrapper class
(shown below). I have a couple of questions about his code:

1. Why does the destructor call "delete m_ptr"? This wrapper didn't allocate
that memory.
Those are the semantics of the class -- passing it a pointer means it'll take
ownership of the allocated memory. The STL's auto_ptr works the same way.

Aha! That also explains why the copy constructor modifies the "right" object by
clearing its pointer to the native data. That's what the author meant by
"transfer of ownership semantics".

But that still leaves the question of whether or not it's even correct to call
delete on the pointer. What if the native object was allocated on the stack,
e.g.

void MySub() {
MyNativeClass c;
MyOtherSub(c);
}

void MyOtherSub(MyNativeClass& c) {
// At this point we have no idea whether
// c was allocated on the stack or the heap
AutoPtr<MyNativeClassp(&c);
}
It hurts when you move your arm that way, doesn't it? :-)
Assuming I got the syntax at least kind of correct, what will happen when the
AutoPtr goes out of scope and its destructor tries to delete c?
I'm pretty sure demons will fly out of your nose, since that invokes
undefined behavior.

The reason the class is named AutoPtr is to evoke images of the STL's
auto_ptr, which *also* isn't used this way. Indeed, there's no point at all
to using auto_ptr on objects you didn't construct yourself (as MyOtherSub is
doing here).

You can still write MyOtherSub this way, with some care:

void MyOtherSub(MyNativeClass& c) {
AutoPtr<MyNativeClassp(new MyNativeClass(c));
}

But of course copying the object isn't always desirable or even possible.

If you want to pass a stack-allocated objects, don't use smart pointers. On
the other hand, smart pointers are a pretty good approach to avoiding memory
leaks for objects you *can't* stack-allocate.

--
J.
Jun 27 '08 #8
It hurts when you move your arm that way, doesn't it? :-)

Yeah. My doctor told me not to do that any more.
If you want to pass a stack-allocated objects, don't use smart pointers. On
the other hand, smart pointers are a pretty good approach to avoiding memory
leaks for objects you *can't* stack-allocate.
Oh... I was so focused on figuring out how to wrap a native reference (or, as it
turns out, a native pointer) in a managed wrapper that I missed the fact that
the solution was really intended to solve other problems as well, and, as such,
has implicit usage restrictions placed on it.

Thanks a million Charles and Jeroen!

Bob
Jun 27 '08 #9
"Charles Wang [MSFT]" wrote:
Hi Bob,
Your analysis is correct. The problem here is that the native type
object "myClass" is passed as a parameter to BeginInvoke. There are
two ways to work around this issue, one is changing your MyClass type
from native to managed, and the other one is using a wrapper managed
class to wrap your native type object. For example:
In case writing a new ref class is overkill, consider just using
System::IntPtr.
Jul 29 '08 #10

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

Similar topics

1
by: Grandma Wilkerson | last post by:
My question concerns the documentation for Control.BeginInvoke(). At one point is says: "Executes the specified delegate asynchronously with the specified arguments, on the thread that the...
2
by: Sandor Heese | last post by:
Question, When using BeginInvoke (on a From to marshal to the UI thread) with the EventHandler delegate I see some strange behaviour. The problem is that I call the UpdateTextBox method with a...
6
by: arkam | last post by:
Hi, I found a sample on internet : formMain.BeginInvoke(new MyClickHandler(this.OnMyClick)); I would like to do the same but in a class library where there is no forms ! Where can I find...
4
by: Jon | last post by:
Why are out parmeters included in an BeginInvoke? They seem to do nothing? TestProgam: using System; namespace TempConsole { class App { public delegate void MyDelegate( out byte b, out...
6
by: Valerie Hough | last post by:
I'm not entirely sure what the difference is between these two approaches. In order to avoid reentrant code, I was using Control.BeginInvoke in my UI to cause an asynchronous activity to be done...
9
by: john doe | last post by:
I have a question about BeginInvoke method found on the Control class. To quote the docs: "Executes the specified delegate asynchronously with the specified arguments, on the thread that the...
11
by: Dean Shimic | last post by:
void DisplayLines(object state) { for (int i = 0; i < 500; ++i) { int iCopy = i; rtb.BeginInvoke((MethodInvoker)delegate { rtb.AppendText(iCopy + "\n"); }); }
2
by: Flack | last post by:
Hello, If I understand BeginInvoke correctly, when it is called your delegate is run on a thread pool thread. Now, if you supplied a callback delegate, that too is called on the same thread...
10
by: kimiraikkonen | last post by:
Hi, I have a small app which sends using .NET's SmtpClient Class and sends well. But the problem is that i placed a marquee-type progress bar to have an animation and hide by default. Then i...
7
by: Ben Voigt [C++ MVP] | last post by:
As much as the CLR team assures us that it's ok to fire-and-forget Control.BeginInvoke, it seems it isn't. Maybe this is a bug. See for example: the comments in...
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...
0
by: ryjfgjl | last post by:
ExcelToDatabase: batch import excel into database automatically...
0
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 6 Mar 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). In this month's session, we are pleased to welcome back...
1
by: PapaRatzi | last post by:
Hello, I am teaching myself MS Access forms design and Visual Basic. I've created a table to capture a list of Top 30 singles and forms to capture new entries. The final step is a form (unbound)...
1
by: CloudSolutions | last post by:
Introduction: For many beginners and individual users, requiring a credit card and email registration may pose a barrier when starting to use cloud servers. However, some cloud server providers now...
1
by: Shællîpôpï 09 | last post by:
If u are using a keypad phone, how do u turn on JavaScript, to access features like WhatsApp, Facebook, Instagram....
0
by: af34tf | last post by:
Hi Guys, I have a domain whose name is BytesLimited.com, and I want to sell it. Does anyone know about platforms that allow me to list my domain in auction for free. Thank you
0
by: Faith0G | last post by:
I am starting a new it consulting business and it's been a while since I setup a new website. Is wordpress still the best web based software for hosting a 5 page website? The webpages will be...

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.