I'm implementing a DLL containing a shared ADO Connection by using the ConnectionObject property of TADOConnection and passing it across DLL boundaries into other instances of TADOConnection. I need to make sure COM is initialized, so I need to call CoInitialize / CoUninitialize. Currently it's in the context of a VCL main thread, but could be elsewhere in another thread, which of course requires its own instance of COM. I'm not currently implementing multi-threading.
Where is the appropriate place to call these; inside the DLL (during loading/unloading), outside the DLL (calling process), or both? Considering it's only one thread, shouldn't it be only one time outside the DLL in the original process?
I'm assuming the original calling thread should be solely responsible for this, since COM runs in the context of a thread. Of course calling them on both sides shouldn't hurt any, however it would also create multiple COM instances.
Long story short... Is it "safe to be safe" in this case? Or is it important to only keep one COM instance?
You should not be doing this in the DLL. Make it part of the contract between the DLL and the host that the host is responsible for initializing COM.
The DLL cannot be expected to initialize COM because the host may already have done so. And use a different threading model. Once COM has been initialized then future attempts to initialize will fail if they attempt to change the threading mode.
So, don't initialize COM in your DLL. Require the host to do so.
Related
Is there ever a reason to use CoInitialize inside my DLL function when using IXMLDocument (msxml wrapper)? (or Other com object for that matter)
Is the calling application/thread responsible for calling CoInitialize/CoUninitialize?
What if I use ComObj inside my DLL which automatically calls CoInitialize in its initialization section and CoUninitialize in its finalization section?
Is the calling application/thread responsible for calling CoInitialize/CoUninitialize?
Yes. As a general rule, the creator of a thread must be responsible for initializing COM. Which means that the functions that you expose from your DLL should not initialize COM for the thread on which they were called.
The reason being that if you take responsibility for initializing COM in the thread that calls the DLL, then that places an unreasonable constraint on the creator of that thread. What if the creator of the thread needs to perform another action that requires COM to be initialized? The standard practise is that the DLL specifies COM initialization as one of its requirements. In the documentation for your DLL, state that the caller must initialize COM.
More details here: Things you shouldn't do, part 2: Dlls can't ever call CoInitialize* on the application’s thread.
That's why you should not initialize COM in your DLL on the caller's thread. There's even more reason not to initialize COM in an initialization section. Initialization sections are executed inside the DLL's DllMain. There are very few things that you are allowed to call inside DllMain, COM functions not being on the list of allowed actions. For more details: Some reasons not to do anything scary in your DllMain, part 3.
What if I use ComObj inside my DLL which automatically calls CoInitialize in its initialization section and CoUninitialize in its finalization section?
The ComObj unit does not do that. For a DLL, the COM initializing code that you refer to is suppressed. Including ComObj will force COM to be initialized in an executable project, but not in a library.
I made a custom control that needs to use API DoDragDrop function and they say I must call OleInitialize before calling this function.
I do that in MyControl.Create method but I get S_FALSE result, so it means that the COM library is already initialized. But who initialize it ?
I do not initialize this before in my application. I am thinking, maybe there is some delphi unit who initialize it in his initialization section... And a secondary question is what should I do, should I continue initializing the COM Library in MyControl.Create method or should I not initialize at all (because it's already initialized) ?
As stated in my answer to a similar question:
In the RTL/VCL source, COM is initialized in the following ways:
By a call to OleInitialize made from Forms.TApplication.Create. So this call will be made for all VCL forms applications, but not, for
example, for service applications.
By a call to CoInitialize or CoInitializeEx in ComObj.InitComObj. This is registered as an InitProc in the
initialization section of the ComObj unit. In turn, the call to
Application.Initialize in your project .dpr file's code will invoke
ComObj.InitComObj.
In many and various other locations around the RTL/VCL. Including, but not limited to, Datasnap, ComServ, Soap, System.Win.Sensors,
Winapi.DirectShow9. Some of these areas of code are more recent than
Delphi 7.
Now, of these various COM initializations, the ones that count are 1
and 2. In any standard VCL forms application, both of these will run
at startup in the main thread. Item 1 runs first and so gets to
initialize COM first. That's the initialization that counts. Item 2
runs after and returns S_FALSE meaning that COM was already
initialized.
All VCL applications include the Forms unit and so you can be sure thaat COM is initialized in the main VCL thread. For other threads you need to initialize COM if necessary.
You must never initialize COM in control or more generally a thread that you are not in control of. If you do so then your initialization may conflict with the apartment type specified by the owner of the thread.
I have written a dll in d-7. It functions correctly. It gets loaded when an application starts and unloaded when it exists.
Now, that dll must also work on the server side of that application, being loaded from the COM+ surrogate (dllhost.exe) as the server is based on com+ technology.
The problem is that one process may have only one handle for loaded library. I cannot have separate handles on each LoadLibrary call.
In com+, one dllhost.exe can serve many clients, which means that if I load an external library it gives the same instance for each clients calls.
So:
1) Is it possible to somehow workaround this problem?
2) Or Is it possible to directly create instance of the class which resides in this dll for each client call?
3) Or How to make a dll thread-safe by instantiating an internal class for each call.
Thanks for suggestions in advance!
Since a DLL uses the local memory of the thread/process that calls it, I'm not sure you're really going to have a "problem". You do not want to have the DLL maintain some global information because that will only lead to threading nightmares.
If you have a function within the DLL return an object reference (effectively a handle) you can also guarantee that each instance gets its own information to play with and not stomp on other processes or threads. Define the function to return your TWhatever object, have the DLL create it ( TWhatever.Create() ) and return it to the calling process. It would then be up to the calling process to free the object. If you need COM+ compatibility then it should be possible to use an interface reference (IWhatever) and return that from your function.
If each thread deals with its own object instance (or interface reference) then you could have the DLL serve as many threads and processes as you wish.
Your question is confusing to me so I'm not sure if I answered it correctly. :p
Delphi.
How from DLL to learn Handle the appendix which has caused this DLL?
That is necessary: Knowing Handle appendices, I wish in Dll to use this Handle at creation of dialogues, because dialogue created as TOpendialog.Create(nil) sometimes it appears under the main window of the basic form. And so, in DLL I would make:
application.handle:=GetExeHandle; // GetExeHandle - How to learn?
Opendialog1:=TOpendialog.Create(application);
...
So it is correct?
The only time your DLL shows a dialog box is when the host application calls a function from your DLL. Include the parent window handle as one of the function's input parameters so that the EXE can tell you which handle to use. Do not attempt to discover the handle yourself. As a library developer, you cannot guess what the host application is doing.
If you don't want to include the handle on every function call, then add an initialization function that users of the DLL need to call before any other functions. Pass the handle in the initialization and then store it in a variable in your DLL so that other functions can use the value when they need it.
Unless you use runtime packages (and you don't, or not the right ones), you are in for a world of pain.
Your library will have its' own copy not only of (T)Application but also of thread sycnhronization queue and event (and everything else).
What you are trying to do can seem to work, but it may (and it will) break anytime cause any complex dialog, regardless if VCL or WinAPI, does its' own message pumping, which will bypass the applications' idle and synchronization handling, resulting in reentrancy issues and random stalls or deadlocks.
You may try to handle a lot of the cases by copying the applications' handles, events etc. to the DLL's globals upon its' initialization (is I tried to do), but (not only) if you use anything like TApplication or TThread in the DLL, it will break sometimes.
You can avoid these problems if you use the right BPL runtime packages in your app and the library, as they will share the same namespace and globals as the application using them.
Just want to confirm with the COM/DCOM experts out there...
I have a Delphi COM EXE that is running on a server (threading is Single Apartment) and another copy of that same EXE starts up on the same server
(a server that has say 2 processors with dual core so the task manager shows 4 separate graphs) will they take turns waiting for each other or will they run on separate cores?
I found a post somewhere that said
"If two clients need to use the same object, they have to take turns. With this threading model the instance data is safe, global data must be protected using critical sections or some other form of serialization. Of course, the thread's local variables are reliable across multiple calls."
You've missed the "different objects from the same server" part from the same paragraph. Since you have "Single instancing" you get a separate server for each COM object instance, so calls to those instances can be processed in parallel and COM will not have to provide any mutual exclusion.
Think of it this way. Threading models are there to provide thread safety - synchronized access to data shared between threads of the same process. You have one object per process, so there's no two threads will try to access the same object unless of course you try to pass a pointer to one object into another object method call.