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:
What do you think about this approach ?
Do you see a better solution to the problem ?
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.
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!
Related
I don't usually program in the .NET framework however I've needed to use it to create a simple exe application that logs data from some measurement equipment. I want this application to have an automation interface with a couple of methods. I've done this before using Delphi/Pascal and it was very straightforward, and I can create a Class Library in Visual Studio that registers itself and the methods and can easily be interfaced with but adding a ComClass to the windows form application doesn't work, can anyone point me in the right direction?
This started as a comment but just kept growing, hopefully i am not off track ...
You can only register a dll. So the short answer would be to add a class library to your project and put the com class in there. This would allow you to share the functionality of the class, but not the same instance of that class.
I think your problem is that you are trying to establish Interprocess Communications. My immediate suggestion is not to do that, at least at first.
For a start you could have your com app interface with a .net class library which would log data. You could then have another project with a .net executable that displays the log, and updates periodically. Now you have .net code on both sides of the fence and can start investigating ways to get them to talk to each other.
The hard part is invoking methods and returning values. If you really need a responsive system you can investigate named pipes inter-process communication, or using a WCF service with callbacks between the two systems. The problem is that in either case you are hamstrung by the class library not being hosted by .net so you have limited functionality (no config file etc). If you can put up with a time lag I would suggest simply starting with writing messages to a local datastore and then polling from each client, every 30 seconds or so. For example the app would create a message saying "give me measurement A" and then start polling every second. The DLL would have a timer and within 30 seconds would read that message and write its own message with the measurement, which the EXE would read and then display. Once you get that system up and running you can decide whether you need to tackle inter-process communication, which is really just a different interface.
I am trying to write a running object table like WCF service (.NET 4.0) for providing access to some COM controls across processes. This service is accessed by both COM and .NET clients.
I chose WCF since it is recommended for inter-process communication and I also thought it would be good if I don't have to depend on ROT where I don't have much control over.
After solving several hiccups, I reached a road block. I don't know how to pass the COM control through the service and give it back to a client. The object never reaches the service. Though WCF is recommended for IPC, it does not provide out of the box support to pass COM objects. I also haven't found any solutions so far. May be WCF service is not the right approach to replace running object table. But I don't see a better way to do IPC.
Any suggestions on this?
A COM objref can't be passed around in a WCF message (well I guess you could create a MEOW interpreter on the receiver size and use CoMarshalInterThreadInterfaceInStream to pass the objref)
However, you could put the objects in the GIT and pass the GIT cookies around
But we can directly place COM object into the ROT by implementing IUNKNOWn interface
When I register my .NET Assembly with regasm.exe the registry key
HKEY_CLASSES_ROOT\CLSID{111E32AD-4BF8-495F-AB4D-6C61BD463EA4}\InprocServer32
is set to "mscoree.dll".
However, I am trying to mimic an existing COM-Server that was written in C. When registering this old COM-server the InprocServer32 is set to the full path to this component.
Unfortunately the existing system (a plugin host that I can not change) reads and use this value - an is confused by the "mscoree.dll" value.
My solution might be to patch this registry entry manually - but I would like to understand why regasm writes "mscoree.dll" into InprocServer32 .
The explanation is quite easy. When you use a native (unmanaged) COM server in-proc, it is loaded into the consumer process and the consumer process directly calls its functions.
This can't work that easily with a managed code COM-exposed assembly. In case of managed code an intermediate layer is needed that performs the managed/unmanaged interaction. mscoree.dll acts as this intermediate layer. So when the consumer calls CoCreateInstance() mscoree.dll is loaded and emulates the COM server by loading the COM-exposed assembly managed code and forwardind all calls to the latter.
i have two web services. One with user functionality, one with admin functionality.
Both services effectively work with the same object types, for instance:
AdminService provides functionality for deleting/modifying Customer objects
UserService provides functionality for listing/reading Customer objects
Now in the client i have two service references, Webservices.Admin and Webservices.User.
If i use the UserService to retrieve Customer objects, i cannot manipulate those via the AdminService, since the UserService retrieves objects of type Webservices.User.Customer, however the AdminService works with objects of type Webservices.Admin.Customer.
On the server side both types are identical, just belong to different namespaces in the client.
Now the question: How can i share types across different service references?
Check out https://github.com/geersch/WcfSvcMap
By tweaking the Reference.svcmap file you can make sure only one class is generated for each DataContract used by the different service references.
Note: Remember to delete the content of the node before pressing 'Update Service Reference'
If you're controlling both ends of the communication, and both ends are .NET only, you could do this:
put all your contracts, including your data contracts, into a separate "Contracts" assembly
reference that assembly in both the server side implementation code, as well as the client side code
If you do this, when adding the service references, WCF will find and use that shared assembly, and not create new types for the entitites. In your case, you'd only ever have one type Contracts.Customer or whatever you're dealing with.
This works only if you control both ends of the wire and have .NET on both ends! But in that case, it's a great way to share contracts - especially data contracts - across both the server and any number of clients.
Use the slsvcutil to create the WCF proxy on the clientside (assuming the clientside is a .net application), reference the DLL which contains your objects and it will be used for all endpoints that pass the same object in the DLL
Open Visual Studio Command prompt from the Start -> Visual Studio 2008 -> Tools -> Visual Command Prompt
goto directory similar to
C:\Program Files\Microsoft SDKs\Silverlight\v3.0\Tools
type slsvcutil and follow the syntax
slsvcutil http://somewcfservice:8080 /r:CommonLibrary.dll
where CommonLibrary.dll is the dll that contains the business objects
[edit] fixed the fact that the project is a silverlight project
There is an easy way to share types between client and service, just by adding reference to shared type assembly to your client BEFORE adding the service reference.
You can find the detailed scenario and sample project there:
http://blog.walteralmeida.com/2010/08/wcf-tips-and-tricks-share-types-between-server-and-client.html
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.