I have a dll, which is loaded by the couple of processes. In that DLL source code I have an instance of global variable. Will this global variable be the same in all loaded processes ? Or will it be as the unique variable in each process ?
It will be unique to each process, unless (i) you've written explicit code to share inter-process or (ii) are still using a 16 bit dll.
Related
I am working with an application GUI program and a console program, which call a COM object, which calls a worker DLL. Let’s call them ConsoleApp, GUIApp, COM DLL, and Worker DLL. The two Apps use CreateObject (VBasic) or GetProcID(C#) to activate the COM object and call it. In turn, the COM object calls the Worker DLL.
Problem: My goal is to register the COM DLL once, and then to continually develop the worker DLL without having to reregister the COM object all the time. Things work as expected when using the ConsoleApp. I can call it and see a debug message from the Worker DLL. When I change the message and recompile the worker, the next invocation of ConsoleApp shows the expected Mbox message.
But things do not work as expected with the GUIApp. To pick up any new worker DLL code, I must reregister the COM object (of course the GUIApp is not alive during re-registration). But that defeats the purpose of me trying to split the big COM+Worker DLL into two pieces to avoid re-registering the COM object all the time.
I have read many web pages (including here) on COM objects, but have not found answers that might work for my goal of having the registered COM object call a Worker DLL that can change during development.
Q. Is this the normal case that all DLLs referenced and called by a COM object are somehow bound to the COM object at registration time? (Answered above and below.)
Q. Is there any way for me to register the COM object once and have it call a DLL that I can work on in development without constant reregistering the COM (calling) object every time I change the Worker DLL code?
That's my ultimate goal if it is possible (and it seems to work as expected for the ConsoleApp).
UPDATE: RomanR suggested that I use ProcessExplorer to see which process is hanging on to the Worker DLL once GUIApp has been shut down. I could find the Worker DLL when GUIApp was alive but could not find it when GUIApp was shutdown. At the moment, the visible evidence is questioning my statement that the GUIApp never lets go of the Worker DLL. I will have to figure out a way of absolutely showing whether rebooting GUIApp picks up the new versions of Worker DLL.
The problem occurred because I registered the COM object direct from VStudio as part of the build (as Administrator). In the COM VStudio project, the reference properties for Worker.DLL specified Copy Local = True. Therefore, at the time of registration, the COM object was referencing the locally-copied version of Worker.DLL and not the future (later modified) copies of Worker.DLL that were stored elsewhere.
If I set Copy Local = False, I could register the COM object, but it would fail because it could not find the Worker.DLL at runtime.
The easiest solution was to 1) close the GUIApp to release the COM and Worker DLLs, 2) modify the Worker DLL with new code, 3) and copy the new Worker DLL into the COM project\bin\Debug folder where the Copy Local=True operation would put it. That way the registered COM object would pick up the most recent Worker DLL from the expected location.
Another solution (that I have not tried) is to modify the COM code to dynamically load and instantiate Worker.DLL from some dynamic location. That looks like a good approach too, although it does not give compile-time feedback on Worker DLL methods, etc.
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 it possible for a function that is inside a DLL to unload the DLL? I need to do this so I can make sure the DLL is not in use, then write to the DLL's file.
As I understand it, it CAN be done and is MEANT to be done sometimes (for example in case of dll injection by CreateRemoteThread and other methods). So,
FreeLibraryAndExitThread(hModule, 0)
will do precisely that.
On the other hand, calling
FreeLibrary(hModule)
will not do here - from MSDN: "If they were to call FreeLibrary and ExitThread separately, a race condition would exist. The library could be unloaded before ExitThread is called." As a remark, ExitThread does some bookkeeping besides just returning from the thread function.
All this assumes that Your Dll obtained the hModule itself by calling LoadLibrary from inside the loaded Dll, or rather, by calling from inside the loaded Dll the following function:
GetModuleHandleEx
(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCTSTR)DllMain,
&hModule
)
This increments the reference count of the Dll so You know that if You free the library later using that handle and if the library is really unloaded then You had the last reference to it.
If You instead skip incrementing the Dll's reference count and obtain the hModule just from the argument to DllMain during DLL_PROCESS_ATTACH then You should not call FreeLibraryAndExitThread since the code that loaded the Dll is still using it and this module handle really isn't Yours to manage.
Use this when the dll has done it job:
CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)FreeLibrary, &__ImageBase, 0, NULL);
// terminate if dll run in a separate thread ExitThread(0);
// or just return out the dll
And the __ImageBase is your dll's PE header structure:
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
If your asking if you can safely unload/unmap a DLL loaded in a process from code in the DLL itself, the answer is no - there isn't really a safe way to do this.
Think about it this way: Unloading a DLL is done by decrementing it's reference count using FreeLibrary(). The problem of course is that once the reference count of the DLL hits zero, the module is unmapped. Which means that the code in the DLL that called FreeLibrary() is gone.
Even if you could do this, you'd still need to ensure that there are no other threads executing any exported functions from the DLL.
I don't think it will work. Calling FreeLibrary with a handle from the outside (LoadLibrary would have been called from an area outside the DLL) as the code runs in a memory location that will not be valid anymore.
Even if this is possible, it smells like a bad design. Maybe you want to make some updater or alike. Explain a bit more what is the result you expect. Unloading a DLL from within itself is not the way to go.
I have a VB6 DLL embedded in some ASP pages. The DLL hits a Codebase database, an obsure and obsolete database engine (a dialect/variation on dBase) that virtually no-one has even heard of. It takes Codebase nearly a second to initialise a new connection, which is unacceptably slow and so I've created a connection pool, managed by a VB class. The single instance of this class is created at the start of a VB module, i.e.:
Private m_codebaseManager As New CodebaseManager
My problem is that periodically the class initialization method is called again completely wrecking my pooling class and I've no idea why. Terminate does not fire and there's no sign of any crash occurring, so why on earth is initialize called? My understanding is that data in non-class modules persists for the lifetime of the DLL. Is that correct and if not, under what circumstances does a module 'restart'?
I would recommend removing the "New" from the variable declaration. Declaring a variable "As New" causes it to be checked every time it is referenced, and if set to Nothing a new instance of your CodebaseManager will be created.
A better solution would be to declare your variable like this:
Private m_codebaseManager As CodebaseManager
and then explicitly set it to a new instance when your application starts:
Set m_codebaseManager = New CodebaseManager
This means you can be sure you won't be creating any unintentional new instances of CodebaseManager. You'll probably then still have a bug but at least it will be an "Object or with block variable not set" error which you should be able to easily fix.
My understanding is that data in non-class modules persists for the lifetime of the DLL. Is that correct and if not, under what circumstances does a module 'restart'?
Sort of. Global state (module public/private vars) are apartment specific and is stored in TLS slots. VB6 supports apartment threading only, so each thread gets a "fresh" copy of the global state. Because ASP environment is multi-threaded so each thread gets a separate DB connection "pool".
If you need a real global state you have to use Application object to store it. If you put apartment threaded objects there (like VB6 ones) these can serialize you multi-threaded ASP environment and degrade performance. Use ADO objects or Dictionary objects or anything you are certain is free threaded.
Btw, you can let COM+ do the object/connection pooling for you. If OLEDB provider is a better one it can do connection pooling internally too (SQLOLEDB for MSSQL is an example).
But it's a DLL and not an ActiveX exe, so there's no Main() function and nowhere for the 'Set m_codebaseManager = New CodebaseManager' line to go except in global scope.
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)