I'm implementing a C++ library that wraps a COM library (IMAPI) to more easily provide the functionality throughout our applications.
It contains wrapper classes for the various interfaces of IMAPI. These are instantiated in the GUI thread of a client application to get information about the drives and their current medium. However, these objects also have functions to e.g. write data to a medium, which is a blocking function call.
Usually I would simply put this blocking function call onto another thread and execute it asynchronous to avoid blocking the GUI. However, since the COM objects were created in the GUI thread, which is initialized with COINIT_APARTMENTTHREADED (STA), this is not possible.
How do I best handle this in my shared library so that its clients do not have to worry about these details? Is the best thing to do to create a thread that belongs to my library that is initialized with COINIT_MULTITHREADED and is responsible for the creation of the COM objects?
Related
Either I don't understand COM objects, either it's all that confusing.
I frequently 'create' , 'dispatch' COM objects - either in Python, either in VB ( Obj = win32com.client.Dispatch('Visum.Visum') or Obj = CreateObject("Excel.Application") respectively).
That's easy, obvious and everything's fine.
But how can I:
a) connect to COM object which is already running
b) get list of running processes which are COM objects
in VB what is the reference, and additional info on: CreateObject command. In Python it's part of well defined, comprehensive library of win32com whereas in VB it's just single method without reference: http://msdn.microsoft.com/en-us/library/7t9k08y5(v=vs.80).aspx
Highly confusing (COM object,server? connection, dispatching, registering??)
Thanks for claryfying
Rafal
i2
First of all, I would suggest reading a good introduction to COM. Personally, i think that 'Essential COM' by Don Box is the best book about COM. Spending a few hours with it will save you many hours later.
That being said, let's move to your questions.
a) In order to retrieve an existing COM object from VB, you need to call GetObject function. This will only work for COM objects which are registered with Running Object Table. Excel does this, so there shouldn't be any problems with that.
b) as Hans Passant correctly noted, this question doesn't make sense. Processes and COM objects are completely unrelated things, except for the fact that processes host the COM objects. It is similar to asking 'which processes are instances of class X'. However, a process can host a COM object (or many of them). These object can be alive only temporarily, so what would you expect there? Besides, many COM classes are implemented within dlls, but created instances obviously are hosted within a process - so what would you expect there?
COM technology is something I didn't not use for a while, so sorry if some info might be wrong (anyway, I've got my book "Inside COM+ base services" with me!)
Briefly speaking, let's give a simplified definition of a COM object: it nothing else than a standard DLL with classes exposing a standard interface IUnkown with the following methods:
AddRef: to register a client consuming the COM object
Release: to unregister a client (usually unused objects stop running, but other might remain resident)
QueryInterface: to get the address of a function
QueryInterface is used to dynamically retrieve the address of a function (late-binding) which is powerful (you don't have to know the DLL at compile time) but time consuming. On the other hand, you can directly reference the addresses of the functions at compile-time because those DLL are registered in the system (all the informations are stored in the registry)
Creating a new object or getting the address of a running object is done through RPC (remote procedure call) which will load and/or play the role of a proxy
So to create a new instance of a COM object, you will call CreateObject (in VB) (Co)CreateInstance in VC++ (or even c#?), while to get an executing instance, you might want to call GetObject().
Getting the list of COM objects is not that easy (I guess not possible at all) because, as told earlier, a COM object is nothing else but a DLL: this means that the library will mainly load in the address space of each process which access is private. Even for out-process COM objects (opposite to in-process) which are object shared between many processes (i.e.: Excel is an out-of-process COM object: you don't load the DLL in your application's address space), they are loaded by a host (rpc.exe or a proprietary host)
Hope this helps
Serge
are there any objects when running wxWidget's common dialogs on a separate thread? I'm developing a browser plugin and so I need to put lengthy operations outside the main browser thread.
I did a small test and it seems to work but a warning appears:
....\src\msw\dirdlg.cpp(333): 'CoCreateInstance(CLSID_FileOpenDialog)' failed with error 0x800401f0 (coInitialize has not been called.).
Does that mean I have to call wxApp::Initialize(...) or some other functions?
Thanks,
Christian
You just need to call CoInitialize() in each thread where you are using COM. So before creating common dialog objects just call CoInitialize() (one per thread) and in the end of thread call CoUninitialize().
For main thread wxWidgets does this internally. For other threads you'll need to call these functions manually. It is generally not related to GUI but related to COM objects which wxWidgets uses internally.
Yes, there are 'objections'
Important notes for multithreaded applications
When writing a multi-threaded application, it is strongly recommended
that no secondary threads call GUI functions.
http://docs.wxwidgets.org/trunk/overview_thread.html
I am porting a regular C++ app to metro in C++ using WRL. I have an existing thread pool and that some point I need to update the UI from one of these threads.
Touching directly the UI objects gives the expected RPC_E_WRONG_THREAD so I need somehow to execute in the right thread. Looking in MSDN I discovered that the metro dispatcher (CoreDispatcher) has a RunAsync method.
Larry Osterman sort-of answers the question of how to use it here:
Run code on UI thread in WinRT
But what is not clear is if I can do that from a non-winrt thread, that is from a thread which has not called RoInitialize.
I guess to be more precise I fear that the dispatcher might belong to an STA and I would need to somehow marshal the interface so it would be safe to call from my other thread.
Note that the main() function of my app following the msdn samples calls RoInitialize(RO_INIT_MULTITHREADED).
You should be ok calling CoreDispatcher::RunAsync from a non UI thread. But there are a couple of caveats:
1) You need to be in a metro style app (this should go without saying). The reason is that the application object creates an MTA that lives for the life of the application. There's this nifty feature of COM called the implicit MTA - if the MTA exists in your process any threads are considered to be a part of that MTA even if they've not called CoInitialize.
That means that when you access CoreDispatcher::RunAsync, even if you need to proxy objects, the MTA is active so the marshaling should succeed.
Note that there is a period of time during app startup where it's possible that the application object hasn't yet been created - you should refrain from doing anything until your application's code has been executed.
2) You need to capture the CoreDispatcher object on the UI thread you want to use. This is made easier by the fact that the Xaml infrastructure already captures the dispatcher in DependencyObject. So if you already have a Xaml UI element, you can just call .Dispatcher.RunAsync().
PS: The UI thread is on an ASTA (application STA, a new kind of apartment added in Win8) but the dispatcher is thread agile. Note that while the dispatcher is agile, the CoreWindow should not be considered agile.
I was wondering is there a way to load multiple instances of a DLL explicitly (i.e. with LoadLibrary call). Normally, a process loads a DLL just once when LoadLibrary is called (so that a single copy of the library resides in the process' memory) and all consequent calls of LoadLibrary done for the same library would be returning just the same handle of the library, returned by the very first call. To be more specific, I want to be able to load a separate instance of a DLL for each thread created inside of a process. Currently, if I do LoadLibrary in each thread, I just get the same pointer to a single instance of the library residing in the process' address space, and all threads in fact refer to the same functions from the library (here a big mess occurs in my app). Instead, I want each thread to create an individual instance of the library in a separate memory space so that the threads don't interfere with each other when calling functions from the library.
I don't think that is possible (until you create a new process and load the DLL in that process). DLLs are loaded at the process level (hence have entries in Process control block). Although your DLL can detect if new thread is created in the host application so that it can initialize TLS specific data in DLLMain method.
Is there possible to create a COM-instance in it's own, dedicated, host-process?
I guess some background is needed.
We have an end-user client which has it's central logical components inside an singleton-COM object. (Not propper singleton, but it uses global variables internally, so it would fail.) So that there should be only one instance per exe-file. Convenient while making the client.
However, I should now make a "client-simulator" to test the server-side. I therefore which to make 20 instances of the client-component.
If I could make each instance instanciate in its own exe-host, then the singleton-issue would be handled.
Regards
Leif
I have been struggling with this problem for a few days. I finally found a solution that works. My COM object is written using ATL, so my code snippet will be geared toward that, but the technical solution should be clear. It all hinges on how the class objects are registered. The REGCLS_SINGLEUSE flag is the key. I now have separate processes for each object instance.
In the ATL module, override the RegisterClassObjects() function as follows:
HRESULT RegisterClassObjects(DWORD dwClsContext, DWORD dwFlags) throw()
{
return base::RegisterClassObjects(CLSCTX_LOCAL_SERVER, REGCLS_SUSPENDED | REGCLS_SINGLEUSE);
}
From MSDN regarding REGCLS_SINGLEUSE:
REGCLS_SINGLEUSE
After an application is connected to a class object with
CoGetClassObject, the class object is removed from public view so that
no other applications can connect to it. This value is commonly used
for single document interface (SDI) applications. Specifying this
value does not affect the responsibility of the object application to
call CoRevokeClassObject; it must always call CoRevokeClassObject when
it is finished with an object class.
My theory is that because the registration was removed from public view, it causes a new process to be created for the subsequent instantiations.
This other question mentioned a description of how to use DLLHost as a surrogate process:
http://support.microsoft.com/kb/198891
I've never tried this myself, and I don't know off-hand if you can specify flags for the factories (which control if surrogates can be reused for multiple objects), but maybe you can tweak that via DCOMCNFG or OLEVIEW.
My COM days are long gone, but as far as I remember, there's no built-in way to do that.
It might be easier to rewrite your code so it supports multiple instances than to go the one-process-per-instance route with COM, but here's what you could do:
Use thread-local storage for your global variables and write another CoClass, where each instance owns its own thread through which accesses to the class with the global variables are marshaled. This would at least allow you to avoid the performance impact of DCOM.
Write your own out-of-process exe server (similar to windows' DllHost.exe) to host your COM instances. This requires IPC (Inter-Process Communication), so you either have to code something yourself that marshals calls to the external process or use DCOM (presuming your COM object implements IDispatch)