Re: How to marshal code to the original thread
- From: Frank Rizzo <none@xxxxxxxx>
- Date: Wed, 08 Feb 2006 10:34:09 -0800
Thank you Willy. Hopefully this does the trick.
Willy Denoyette [MVP] wrote:
Frank,.
I didn't spend time on the code you posted, I just included here a complete (non-sense) sample (CS and a VB6 code) to illustrate how I think you could implement your wrapper and to prove you can fire events from abitrary CLR threads to VB6 COM clients using connectionpoint interfaces.
Just compile the code and register with COM, do this from the command line using
csc /t:library cowboy.cs
regasm /tlb /codebase cowboy.dll
when done create a new VB6 project and add a reference to the typelib produced by regasm
Note that if you change the filename you will have to change the VB6 type accordingly! (Cowboy.Cowboy)
The VB6 client creates an instance of Cowboy and calls a method Shoot with as argument an int value (shots).
To simulate your Asterix callback arriving on a different thread, the Shoot method creates a new thread and initializes it to enter the MTA (or STA doesn't matter) and calls it's procedure (starts the thread).
The threads procedure checks the value of 'shots', if it's > 2 then an event is fired with an arg. value 1, if 'shots' is < 2 an event is fired with a value 0.
Note that I could have used some asynchronous socket code instead of this silly sample, but this is not really relevant.
// cowboy.cs
// pay attention to the class decoration, this is important for connectionpoint interface support.
using System;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System.Diagnostics;
using System.Threading;
namespace Willys
{
[Guid(Cowboy.interfaceId)]
[InterfaceType(ComInterfaceType.InterfaceIsDual)]
public interface ICowboy
{
int Shoot(int number);
}
[Guid(Cowboy.eventsId)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface ICowboyEvents
{
[DispId(1)] void Touched(int level);
}
// Delegate for the event
public delegate void TouchedEventHandler(int level);
[Guid(Cowboy.classId)]
[ComSourceInterfaces(typeof(ICowboyEvents))]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("Eventing.Cowboy")]
[ComVisible(true)]
public class Cowboy : ICowboy
{
// UUID's of class, interface and event
const string classId = "1a6cdc6e-9123-494c-92c1-d1fad8f48f27";
public const string interfaceId = "59f20cb8-d7fa-41c1-83a2-50fba18f1f7d";
public const string eventsId = "74a49bed-5475-415e-8d9e-b889271040cc";
public event TouchedEventHandler Touched;
public Cowboy()
{}
public int Shoot(int shots)
{
// do some stuff ....
Thread t1 = new Thread(new ParameterizedThreadStart(DoWork));
t1.SetApartmentState(ApartmentState.MTA);
t1.Start(shots);
t1.Join();
return shots;
}
void DoWork(object o)
{
Touched += new TouchedEventHandler(OnTouched);
int n = (int)o;
if (Touched != null)
{
if (n <= 2)
Touched(0);
else
Touched(1);
Touched -= new TouchedEventHandler(OnTouched);
}
}
// Implemented by the COM client
void OnTouched(int level)
{}
}
}
[VB6]
'create a form with a single button
Private WithEvents myCowboy As Cowboy.Cowboy
Private Sub Command1_Click()
Set myCowboy = New Cowboy.Cowboy
Dim n As Long
n = myCowboy.Shoot(31)
End Sub
Private Sub myCowboy_Touched(ByVal level As Long)
MsgBox (level)
End Sub
Hope it's of any help.
Willy.
"Frank Rizzo" <nospam@xxxxxxxxxx> wrote in message news:%23nKdxwGLGHA.3104@xxxxxxxxxxxxxxxxxxxxxxx
| Willy Denoyette [MVP] wrote:
| > "Frank Rizzo" <none@xxxxxxxx> wrote in message
| > news:%23ECVFaDLGHA.3496@xxxxxxxxxxxxxxxxxxxxxxx
| > | Nicholas Paldino [.NET/C# MVP] wrote:
| > | > Frank,
| > | >
| > | > I would create an object in VB6 which you can pass to the .NET app.
| > | > This object would have methods which will fire the events to your main
| > app
| > | > when called.
| > | >
| > | > Then, I would make this instance available to your .NET code, using
| > the
| > | > mechanism I stated before with the global interface table.
| > |
| > | Do you have some kind of a sample for implementing this mechanism? I
| > | don't fully understand it. When you speak of the global interface
| > | table, are you talking about the COM construct?
| >
| > No need to do this at the managed side,the interface marshaling is done for
| > you by the CLR and this is not the issue either.
| > Point is that you want to sink an event on a COM connection point interface
| > (that's what I meant in my other reply ), note that sinking an event is the
| > same as calling an interface method (a VB6 method), the problem here is that
| > you call this method from a thread running in the MTA, no problem the
| > runtime knows the interface has an incompatible apartment and will
| > automatically marshal the call by creating a proxy/stub pair. So this should
| > work.
| > Now in another reply you said it doesn't work because VB is getting confused
| > (I didn't know that this was possible ;-)), and you are getting time-outs at
| > the VB6 side, and that's something that should get cleared up first, what
| > exactly do you mean with this?
| > Is it the call from VB6 into your friendly wrapper that is timed-out???
| > Do you have your "friendly wrapper code" available (I mean can you post it
| > here)?
|
| The entire thing is too long to repro here (and I don't have the code
| right here), but I'll give you a quick sample:
|
| namespace wrapper
| {
| //wrapper code
| [ComVisible]
| public class AsteriskWrapper
| {
| public delegate void DialEvent(string Status);
| public event DialEvent Dial;
|
| Asterisk.Net.Manager mgr;
| public AsteriskWrapper {} //for VB6 benefit
| public bool Initialize()
| {
| //instantiate the object
| mgr = Asterisk.Net.Manager("MyServer", 5038);
| //hook up an event
| mgr.Dial += new DialEventHandler(mgr_Dial);
| return true;
| }
|
| public bool MakeCall(string FromExt, string ToExt)
| {
| //this call will cause the mgr_Dial to fire
| mgr.InitiateCall(FromExt, ToExt);
| return true;
| }
|
| private void mgr_Dial(object sender, Event.DialEvent e)
| {
| //fire an event
| if (Dial != null)
| Dial(e.Status);
| }
| }
| }
|
|
| ' here is the vb6 code
| private withevents mobjAsterisk as MyWrapper.AsteriskWrapper
| public sub btnDoSomething()
| set mobjAsterisk = MyWrapper.AsteriskWrapper
|
| o.Initialize
| o.MakeCall"201, "202"
| end sub
|
| private sub mobjAsterisk_Dial(Status as string)
| 'it never gets here
| end sub
|
|
|
|
| > Is the wrapper correctly registerd with it's typelib (regasm /tlb ...).
|
| Yes, per this article:
| http://www.vbrad.com/pf.asp?p=source/src_real_interop.htm
|
|
| >
| > Willy.
| >
| >
- References:
- How to marshal code to the original thread
- From: Frank Rizzo
- Re: How to marshal code to the original thread
- From: Nicholas Paldino [.NET/C# MVP]
- Re: How to marshal code to the original thread
- From: Frank Rizzo
- Re: How to marshal code to the original thread
- From: Nicholas Paldino [.NET/C# MVP]
- Re: How to marshal code to the original thread
- From: Frank Rizzo
- Re: How to marshal code to the original thread
- From: Nicholas Paldino [.NET/C# MVP]
- Re: How to marshal code to the original thread
- From: Willy Denoyette [MVP]
- Re: How to marshal code to the original thread
- From: Nicholas Paldino [.NET/C# MVP]
- Re: How to marshal code to the original thread
- From: Frank Rizzo
- Re: How to marshal code to the original thread
- From: Nicholas Paldino [.NET/C# MVP]
- Re: How to marshal code to the original thread
- From: Frank Rizzo
- Re: How to marshal code to the original thread
- From: Willy Denoyette [MVP]
- Re: How to marshal code to the original thread
- From: Frank Rizzo
- Re: How to marshal code to the original thread
- From: Willy Denoyette [MVP]
- How to marshal code to the original thread
- Prev by Date: Re: How do you create a Access Database for a setup project
- Next by Date: Re: Regular Expression question
- Previous by thread: Re: How to marshal code to the original thread
- Next by thread: Re: How to marshal code to the original thread
- Index(es):
Loading