How to marshal code to the original thread

F

Frank Rizzo

Thank you Willy. Hopefully this does the trick.
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.



| Willy Denoyette [MVP] wrote:
| > | > | 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.
| >
| >
 

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