Link to home
Start Free TrialLog in
Avatar of JohannRamon
JohannRamonFlag for Colombia

asked on

How do I unload a DLL that is "stuck" in memory?

Hi:

We have a program that runs as a service (S.EXE) and loads a dll (A.DLL), this dll calls another dll (B.DLL) which in turn calls yet another dll (C.DLL) in order to perform some device functions. We have the source code from S.EXE, A.DLL and B.DLL but C.DLL is an old driver library for a magnetic card reader (MCR) from a third party and i don´t have the source code, i have the LIB files and the header files.

The problem is that under some circunstances C.DLL "hangs" while using the MCR and the service can not longer perform the functions related with it. In development and testing environments we can just restart the service and continue working, however in production environment this is not possible because of user permissions and they have to restart the machine seriously affecting the operation.

How can i unload the dlll in my VC++ program? I need to continue the execution of the service and that the next time C.DLL is needed it loads just like the very first time. I looked at the code of B.DLL and there is not LoadLibrary or CreateInstace or GetProcAddress. In that project there are only the includes of the header files of C.DLL, the LIB files of C.DLL and the function call.

Thanks,

Johann

Pd: This is not MFC or ATL, just a DLL with standard windows libraries.
Avatar of itsmeandnobodyelse
itsmeandnobodyelse
Flag of Germany image

>>>> I need to continue the execution of the service and that the next time C.DLL is needed it loads just like the very first time.

You could try to use a individual instance of the C.dll rather than use the global instance. You can achieve that by putting a local copy of the dll (and all dlls it is dependent of) into the same folder where the service resides. That way you don't have to bother with other process which may have access to the C.dll. If that doesn't solve the problem, you should try to load the C.dll dynamically by means of LoadLibrary. Then you have to access the main functions by means of a fucntion pointer you retrieve by calling GetProcAddress. You can write some kind of wrappers (even C++ class wrappers) for that, so you can get any comfort you want.  To unload the dll you would use FreeLibrary but if it *hangs* you hardly will achieve the unloading. So, you may try to kill any thread that still was using the library or kill the calling thread.

Regards, Alex
Avatar of JohannRamon

ASKER

Thanks Alex,

About the options i have:
- Indeed i'm using an individual instance of C.dll, there are no other processes acessing the dll. When it hangs is in a function called from B.dll when some physical conditions of the device are met.
- Using LoadLibray/GetProcAddress/FreeLibrary: I supose the same, FreeLibrary won't work when it "hangs".
- Kill the thread: Sounds like the best option (too bad is the one i know less about) i'll try to implement it and will post again as soon as i have results.

Thanks again,

Johann
If you can get the virtual address of C.DLL in memory with a debugger (like OllyDbg) or something, you can call GetModuleHandleEx with GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS to get the handle of C.DLL, and then you can call FreeLibrary.
SOLUTION
Avatar of itsmeandnobodyelse
itsmeandnobodyelse
Flag of Germany image

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Hi,

I made many changes to the code but still with no positive results, the changes i tried were:

- In the program S.exe i found the instantiation of the DLL, and discovered it was done with "CreateInstance", and i moved it from the main thread to a "child" thread that is created and finished in every call. That was what i wanted, however even when the child thread finishes A.DLL (along with B.DLL and C.DLL) is still in memory (i checked using MSINFO32.exe) and when it "hangs" because of a device problem the program cannot recover properly and in the next call i have the problem. When i stop S.exe the dll's are unloaded from memory.

- Then I tried changing "CreateInstance" to LoadLibray/GetProcAddress/FreeLibrary only to find out that as A.DLL is a COM the only methods in its def file are DllCanUnloadNow, DllGetClassObject, DllRegisterServer and DllUnregisterServer, all of the functions that are called from S.exe are in the idl file.
Adding those methods to the EXPORTS section of the def file won't work.

Johann
ASKER CERTIFIED SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Thanks John but i'm afraid i don't quite understand how can i use the code in my case. I need to unload the dll.

Regards,

Johann
Well, you can use that code to create your interface instead of using the CoCreateInstance call.  In this case, since you loaded the DLL, you would be able to unload it later.  When windows loads the dll in response to a CoCreateInstance call, it will only unload it later if the DllCanUnloadNow function returns S_OK.  If your program hangs inside the DLL, then this will never happen and it cannot be unloaded.  If you load it yourself, however, you can unload it, regardless of whether the dll tells you that you can.

Note, that I am not sure that creating the interface in this manner will actually work.  It depends on how your com control is build and what it does.  Also unloading the dll when it is hung, might cause other problems, depending on what it was doing.  You would, at the very least, need to kill the thread that was executing the dll code.
Mh, I am doing something wrong, i added the function CreateInstance to my code and i am using it this way:

if(CreateInstance(theDLL, __uuidof(NavFINLDeviceCOM), __uuidof(IDispatch), &pRet) != S_OK)
      {
            ::CoUninitialize();
            return DWORD(0);
      }
      short            iRetCode;
      iRetCode = g_pDevice->ComDeviceStartUp();

But got an E_POINTER exception in the last line when trying to use "ComDeviceStartUp", should i change the Interface ID to something else?
What is g_pDevice, and how is it related to the thing that is returned as &pRet in your CreateInstance call?
It is my mistake, it was on the old code where i had:
INavFINLDeviceCOMPtr    g_pDevice = NULL;

if(g_pDevice.CreateInstance(__uuidof(NavFINLDeviceCOM)) != S_OK)

and i didn't changed it. I have to use pRet for calling the methods of the dll (like "ComDeviceStartUp") but i don't know how, this is very different from the "old" way of calling CreateInstance (where i pass the CLSID)
Can you post the header file in which INavFINLDeviceComPtr is declared?
According to the documentation of IClassFactory::CreateInstance in:
http://msdn.microsoft.com/en-us/library/ms682215(VS.85).aspx

ppvObject
[out] Address of pointer variable that receives the interface pointer requested in riid. Upon successful return, *ppvObject contains the requested interface pointer. If the object does not support the interface specified in riid, the implementation must set *ppvObject to NULL.

In this case ppvObject is pRet but of course niether of those will even complie:
      iRetCode = *pRet->ComDeviceStartUp();
      iRetCode = *pRet.ComDeviceStartUp();
Those are the files:

Thank,

Johann

NavFINLDevice.h.zip
navfinldevice.tlh.zip
Ok, those files define the interface INavFINLDeviceCOM, but where is the declaration of the wrapper class INavFINLDeviceCOMPtr that you are actuall using?
I though that it was done in the TLH file with the following line:
_COM_SMARTPTR_TYPEDEF(INavFINLDeviceCOM, __uuidof(INavFINLDeviceCOM));
Actually when i choose "Go to definition" of "INavFINLDeviceCOMPtr" that is the line where VS sends me.

But in the new code there should not be INavFINLDeviceCOMPtr, shouldn't it?
SOLUTION
Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
The comment in my code example which reads "This will not generate..." should have been "This will NOW generate...".  My point was, that if I call an interface of the control after I have unloaded the dll, it will generate an exception, since that memory is no longer valid.
Working on it... i hope to post again soon.

I made the changes and run the code, taking care of the exception on the call to "Release" after the library is unloaded. However the Dll is still in memory rigth after the function call and after exiting the thread the Dll is still there. I doubled checked the code and test it debugging many times. This is strange...
You are calling "FreeLibrary" to unload the DLL, and it is not being unloaded?  You said that you had to fix the exception caused by the call to Release after the library was unloaded.  Yet, if the library was never unloaded, why was there a problem there?  The debugger will tell you if the DLL is unloaded.
You are rigth. It should be unloaded, i simplified the threads in order to reduce the noise and i am checking the code again.
Also, make sure that you are not calling LoadLibrary more than once.  If you do, you need to call FreeLibrary once for each time you call LoadLibrary.
I am much much closer now! After the last changes the dll is unloaded:
...
FreeLibrary(theDLL); // Dll unloaded, checked with debugger & MSINFO32
// Release is not called in order to avoid the exception after dll is unloaded
CoUninitialize();
return DWORD(0); // End of function of the thread. An exception is generated
}

I found that the exception is generated because when exiting the function of the thread the following code from compi.h is called:

    ~_com_ptr_t() throw()
    {
        _Release();
    }
which in turn calls:
    void _Release() throw()
    {
        if (m_pInterface != NULL) {
      m_pInterface->Release();
        }
    }

I am trying setting m_pInterface to NULL before the call but it still gives an exception. Again i am checking and debugging the code.


You should set m_pInterface to null before you unload the DLL.  You must ensure that no code calls any DLL functions after you unload the DLL.  Also do you make copies of your interface (e.g. via assignment, or by passing it as an argument to a function?  If so then those copies must be cleared as well before they go out of scope.
>>>> You should set m_pInterface to null before you unload the DLL.  

You may try to let the Release as bfore but add the Unload to the Release function:

   void _Release() throw()
    {
        if (m_pInterface != NULL) {
      m_pInterface->Release();
        }
        if (theDLL != NULL)
        {
           FreeLibrary(theDLL); // Dll unloaded
        }
    }
Hi:

Sorry for the long delay, i had to manage other code issues.

I couldn't made the last part work because the _Release function that is called is in the file compi.h where there is no reference to "TheDLL".

However i think the very last part of the problem is going to be solved if i move it and instead of using it where S.EXE is calling A.DLL i use it where B.DLL calls C.DLL. It will take some time doing this because of the project schedule and i don't think it is fair to keep the question open (even) longer until i have more definitive results.

THANKS A LOT for all your help, this will help me not only in this case but also in some other issues that i have to manage here ;)

Best regards,

Johann