Apart from Connection points ,,Is there any way a Method inside my COM server can call method inside MFC Client ?
Related
I am trying to figure out how to implement custom marshalling for an out-of-proc COM server when it fires an event. The server implements the IConnectionPoint interface. One of the methods on the interface that it calls to signal an event takes a pointer to an interface (Call it IMyEventData.). The class that implements IMyEventData in the server also implements IMarshal. When my server fires the event, I get the calls to IMarshal that I expect, including GetMarshalSizeMax, GetUnmarshalClass, and MarshalInterface. So far, so good.
I have implemented the unmarshaller in a separate DLL that is registered on the system. Right after the server handles the MarshalInterface call, my unmarshaller DLL gets loaded into the client, but the calls that I get on its IMarshal interface are not what I expect. The calls are to GetUnmarshalClass, GetMarshalSizeMax, and MarshalInterface. In all of these calls, the context is in-proc, apparently attempting to marshal across apartments rather than process boundaries. I never get the expected call to UnmarshalInterface. When I run both client and server under the debugger, each displays an exception in the output window right after the calls to my unmarshaller's IMarshal interface are made, indicating that an incorrect parameter error (0x80070057) occurred.
Can anyone tell me what I am doing wrong? I had expected my unmarshaller to get a call to IMarshal::UnmarshalInterface so that it would get access to the data that the server supplied in the call to IMarshal::MarshalInterface. I must be missing something basic here.
Thanks.
Wayne
The question is a bit not specific, so the answer will outline the main steps taking place.
Given is an interface pointer on the server side and the pointer needs to get marshaled into foreign apartment. The server COM object implements custom marshaler supposed to be picked up. The client expects the interface pointer to a proxy obtained through custom marshaling.
Server Side
COM queries the COM object in question for IMarshal interface - this succeeds, our object indeed implements it
COM calls IMarshal::GetUnmarshalClass to get CLSID of the class responsible for unmarshaling on client side; this might be the same CLSID of the server object , this could be proxy class CLSID, or proxy factory class CLSID - the idea is that COM requests this via method call, so object is free to return whatever seems appropriate
COM might call IMarshal::GetMarshalSizeMax to prepare a buffer for marshaling data
COM calls IMarshal::MarshalInterface to marhsal the data
Client Side
COM starts here getting eventually external request suggesting that some magic is necessary to spawn a proxy object and obtain COM interface pointer to be given to the controlling/caller application
COM has on hands CLSID of the unmarshaler class and data from the marshaler
COM instantiates a class using the CLSID
COM calls IMarhsal::UnmarshalInterface on the created class and provides IID it eventually wants to obtain
Note that on the last step above COM has the following:
CLSID of the class to instantiate to request interface pointer from
IID of the interface pointer requested
Data produced on server as a result of marshaling process
With all these inputs, the unmarshaler class is expected to return a valid pointer, which might be or might be not the interface of the unmarshaler itself. So you are free to choose whether you want a specialized "factory" unmarshaler class to do create client side object, or you want simply create a new instance and initialize it from marshaling data.
From what I understand, when a COM component marked as using STA is used from an MTA thread, the calls are supposed to be marshalled to an STA thread and executed from that dedicated thread. In the case of a Windows client application, this would mean that it would execute on the UI thread (if marked as STA), and that callbacks from the COM component to me would be handled by Windows messages sent to a hidden window and processed on the Windows message loop.
What happens though if I use a STA COM component in a WCF service hosted in IIS? Will the worker process have a Windows message loop on a STA thread? Can I fire up my own STA thread with its own message loop?
The COM runtime looks after the dispatching of calls to methods on a COM object inside an STA: you are right that this is based on the same OS mechanism used for dispatching Windows messages, but you don't need to worry about making this happen - COM does this for you under the hood.
What you do need to worry about is which STA your COM objects are going to live in. If you instantiate apartment-threaded COM objects using COM Interop from a WCF service, you need to be careful.
If the thread on which you do this is not an STA thread, then all in-process COM objects will live in the default Host STA for the IIS worker process. You do not want this to happen: all your COM objects for all service operations will end up in this same STA. The clue is in the name - there is just one thread for all the objects - and all calls to their methods will be serialized waiting for the one and only thread in the apartment to execute them. Your service will not scale to handle multiple concurrent clients.
You need to make sure that COM objects you instantiate to service a particular WCF request are in their own STA separate from objects created for other requests. There are broadly two ways to do this:
Spin up your own Thread, specifying ApartmentState.STA in SetApartmentState() before you start it, on which to instantiate the COM objects for a particular request. This is the approach detailed by Scott Seely in the link in Kev's answer: he ensures that each service operation call is invoked on a new STA-initialised Thread. A harder but more scalable solution along these lines would be to implement a pool of reusable STA-initialised Threads.
Host your COM objects in a COM+ Application, so that they live in a separate DllHost process, where COM+ (through its abstraction called the Activity) can take care of putting the objects for different requests into different STAs.
I'm not sure exactly what you mean when you refer to callbacks. Perhaps you mean COM method calls on some COM interface implemented in your managed code, via a reference passed in to the COM objects as an argument to one of the COM objects' methods: if so, this should just work. But perhaps you mean something else, in which case perhaps you could amend the question to clarify.
I've found that you need to pump messages on your STA thread in a WCF service or you miss callbacks from the COM object.
The following code works, but it requires you call the COM object via a Dispatcher.
ComWrapper comWrapper;
Thread localThread;
Dispatcher localThreadDispatcher;
public Constructor()
{
localThread = new Thread(ThreadProc)
{
Name = "test"
};
localThread.SetApartmentState(ApartmentState.STA);
AutoResetEvent init = new AutoResetEvent(false);
localThread.Start(init);
init.WaitOne();
}
private void ThreadProc(object o)
{
localThreadDispatcher = Dispatcher.CurrentDispatcher;
((AutoResetEvent)o).Set();
comWrapper = new ComWrapper()
Dispatcher.Run();
localThreadFinished.Set();
}
And then make calls as follows.
public void UsefulComOperation()
{
localThreadDispatcher.Invoke(new Action( () => comWrapper.UsefulOperation);
}
Who is responsible for calling CoInitialize and CoUninitialize if
1- I host a COM dll within a surrogate process (either by using dcomcnfg utility or COM+ wizard)?
2- I build a ISAPI DLL that contains COM objects?
No idea on case 2, but in case 1 the surrogate process certainly calls CoInitialize()/CoUninitialize(). Here's how I know this.
We have a COM object that we host in COM+ and that itself uses MS XML 4. When you need to create an XML document object in MS XML 4 you call CoCreateInstance(). We never call CoInitialize() in our COM object, yet document creation is always successful - certainly the surrogate calls CoInitialize(). If we do the same in a stand-alone app document creation fails unless we ourself call CoInitialize().
So the bottom line is: COM+ surrogate (and I'm sure that applies to DCOM surrogate as well) calls CoInitialize() itself.
Btw you can use this to investigate the case 2 - call CoCreateInstance() inside the ISAPI dll - if it is successful CoInitialize() has already been called.
Is it possible to pass a delegate to a WCF remote object from the client and have the remote object execute the delegate?
I would guess not since a delgate is a function pointer for the client process.
My goal is to have an interface structure that I can "subscribe" to events from a client to the interface. I would pass a delgate from the client to the interface and I want the interface to be able to execute the event.
The idea is to have the ability for the interface to be loaded either the assembly or remotly and have the code work the same.
If I can't pass the delegate how can I implement an event structure?
not a "delegate" in C# per se, but in WCF you have duplex bindings to enable call back from the service side to the client side
http://msdn.microsoft.com/en-us/library/system.servicemodel.wsdualhttpbinding.aspx
I doubt you could come up with a cohesive way to pass a delegate from a C# client to a Java service implementation and have it properly executed......
WCF is not a strict .NET implementation and thus has to deal with interop issues. It cannot rely on the assumption that both ends of the conversation are in .NET - that's why you shouldn't throw around custom exceptions between server and client either (exceptions are a .NET construct) - you need to use SOAP faults instead (which are interoperable).
So I don't think what you want to do can be done with a delegate - you'll have to come up with another way to achieve your goal.
Marc
We have a third party dll that is used in our web service hosted in IIS6. The problem is that once this dll is loaded into memory, the exception AccessViolationException gets thrown if a thread different then the one that created it tries to execute any code within the dll. The worker process is multi threaded and each call to the web service will get a random thread from the pool. We tried to unload it from memory and reload it each time we needed it, but I guess only the front end is .Net and the rest is unmanaged so it never actually gets completely unloaded from memory. We are using VB and .Net 2.0. Any suggestions?
(Response to Rob Walker)
We thought about creating a new thread and using it to call the dll, but how do we make the thread sit and wait for calls? How do you delegate the call to the thread without having the Dispatcher class supplied by .Net 3.0? Creating a hidden form and putting it in a message loop might work. And then we could call the Invoke() method of the form. But I can see many problems occurring if we create a form inside an IIS hosted web service.
I have read about a class in .net 3.0 called Dispatcher that allows you to put a thread in a loop and then call the method Invoke() using a delegate to execute a method using the thread. But this solution will not work if you cannot update to .Net 3.0. Another solution would be to host the third party dll in another application on the server and use some form of Remoting to access it. But you may still have a problem with the Remoting because it behaves similar to IIS and will also pick a random thread to execute the code . To get around this, you could put a wrapper around the dll and use it to delegate the calls to the UI thread by using the Invoke() method of the form.
I think you need to look at using a wrapper thread that handles all calls to the DLL, and deals with the serialization.
This thread is outside of the managed thread pool, so you control its lifetime. But even this would not be foolproof unless you can prevent IIS from restarting the app domain your web service is in.
You also need to worry about what happens when two web service requests come in at the same time. Is each call into the DLL standalone, or do you have to group together all the calls associated with a single web service request before allowing any other request to be serviced?
You could create a service that hosts the extra DLL. Via remoting you access the service, this will dispatch the calls the the thread that manages the DLL.
This way you have control over the thread that calls the DLL, and over the lifetime of the thread.
I'm a bit rusty, but you might try wrapping calls to the DLL in a single threaded apartment COM object. This would ensure that all calls go through the COM object's windows messaging thread. I think you would have to register the component in a server application within Component Services to do this.
Can you run the dll inside different threads as different instances? Like thread1 creates an instance of this third party dll, and thread2 also does, but as long as thread1 doesn't try to use thread2's instance it won't throw that exception? If thats the case, .Net never unloads any code once its loaded, if you load an assembly and then remove it, it still sits in that application pool. If you can create more than one instance at a time, you could load it up in a separate app pool you control per a request, then unload the app pool. Performance might drop though.