views:

260

answers:

2

We are adapting our client side relatively complicated application (ActiveX / .net / Delphi / C++ / COM ) to use SxS to achieve non admin deployment and isolation from older versions of our product.

We were able to achieve this goal for almost all our in proc components such as our .net ui, Delphi ui, and the COM servers we use in proc by composing a manifest file which described all the libraries used by our process, with no registration on the client of any of the components (almost).

And here comes the almost part: At the moment, our application invokes (from it's c++ portion) an out of proc ActiveX server (Delphi ActiveX EXE), which in turn itself invokes another set of out of proc ActiveX servers (third party plugins, any thing goes here, Delphi, C++, any thing as long as it's out of proc ActiveX EXE and implements our interfaces).

As we know SxS does not support out of proc ActiveX servers. And we can't use these objects as in proc com servers in our main process because that would require a major rewrite of our application and even worst, a break of our public facing API which is used by third party tools and vendors, an api break which we can't allow.

We have stumbled on this article which describes how IHTMLDocument2 can be extracted from an Internet Explorer window running in a separate process. Which made us think of this approach:

We would create a secondary satellite application / process which will run the ActiveX as in process server. Then we will use LresultFromObject and ObjectFromLresult to transfer a reference of the ActiveX object from the satellite application to the main application process. The satellite application will have it's own manifest file which will allow it to run in SxS mode.

Same approach will be taken to communicate between this Delphi ActiveX EXE and the third party AciveX EXE Plugins

There is an alternative solution, which for the moment we do not prefer over the proposed solution above which is to use .net remoting and .net com proxy classes to open the communication channel between the two processes, by translating the com request to .net remoting, and back to com on the second process.

So here comes the question:

  1. What do you think about this approach ?
  2. Do you see a better solution to the problem ?
+1  A: 

It is possible to do. What is needed:

  • An application needs to start a server itself rather than relying on COM to do it. You don't need the extra indirection provided by the registry, just use CreateProcess().
  • A server should register its class factories in its main() method with CoRegisterClassObject().
  • Important: the CLSID it uses for each factory should be altered to be unique for each service instance. This ensures that the client connects to the correct server. I simply XOR the process ID with a class factory CLSID. The client knows the process ID as well so can make the same alteration.
  • The application should call CoCreateInstance() in a loop with a Sleep() call to wait for the object factory to appear. Don't declare failure until at least 60 seconds have passed (that bit me).
  • Both the application and the server need a manifest that contains a <file> element for each proxy/stub DLL and <comInterfaceExternProxyStub> elements for each interface that is remoted.
Hans Passant
Correct me if I'm wrong, but your proposition does not use ObjectFromLresult as I have proposed right ?About CoRegisterClassObject, where exactly does it register it, how will my application be able to find it using CoCreateInstance if it's not registered in the registry, even if it is in the manifest ?
Alex Shnayder
It does not. CoCreateInstance() will find it in the running object table.
Hans Passant
Thanks a lot, it would have made me ages to make it work, if not for your help.
Alex Shnayder
I'm having a problem with casting to the implemented interface, I get this error on cast operation HRESULT: 0x80004002 (E_NOINTERFACE). Which does not make sense, cause my object does implement the relevant interface, and I can access methods of the interface by name from the client application via reflection, any ideas ?
Alex Shnayder
This might have some thing to do with both client and server implemented using .net, will provide more info once I'm 100% sure.
Alex Shnayder
A: 

Alex,

nobugz is right, you can access the Running Object Table to create an instance of a COM Object from a currently running process of your Delphi automation exe.

However I have found a big issue that I cant explain. I can only access the object via the variant dispatch method when working this way.

Basically if my Active X exe is not registered, I get an "Interface Not Supported" error if I try to instance the object through interfaces for example:

WebUpdate : IAutomation;

WebUpdate := CoAutomation.Create; <-- Wont Work Error


WebUpdate : Variant;

WebUpdate := CreateOleObject('WebUpdate.Automation'); <-- Works Fine

If I register the active x exe using regserver the problem goes away!!

Go Figure!

richardspud
Am not an expert on Delpi but from what I understand Delphi com framework has high coupling to what is actually registered in registry and present in type libraries (specially the one in which the code is running), as such it's pretty effective in rendering SxS ineffective. But this is true only regarding interfaces and not objects.We were able to go around this limitation by combining ROT and relative registration of interfaces and type libraries (not the CoClasses).
Alex Shnayder
By relative I mean that in registry we place not the whole path to the library, but only .\libName.dll, such that each process can expect to find libName.dll in it's own folder. libName.dll can even be the process itself, so you can reference your own interfaces from with in your code with no worries.
Alex Shnayder