• Welcome to Theos PowerBasic Museum 2017.

News:

Attachments are only available to registered users.
Please register using your full, real name.

Main Menu

Problem with FreeLibrary

Started by Paul Breen, February 04, 2011, 11:15:57 PM

Previous topic - Next topic

0 Members and 1 Guest are viewing this topic.

Paul Breen

I thought I would improve my DLL.PBTPL by adding FreeLibrary instead of holding on to the dll untill the program exited. I figured "What is the use of a DLL if your main program is bound to it all the time, I want to load large dll's and free the memory after use. I want to put a lot of constant data into dll's. The problem is that either Libmain or FreeLibrary seems to be lieing to me. I get success (1) as the result of FreeLibrary but the process detach function in Libmain says it aint so. I do not know which function to believe. msgboxes tell you the story - process detach comes at the end of main instead of at the FreeLibrary call.
Here is the code, it is a marriage of pb example code and Gazette powerlib code. The COM code works fine but my regular dll call using a function pointer does not work.

Client
=================================
#COMPILER PBWIN 9
#COMPILE EXE "Client"
#DIM ALL
#INCLUDE "stringmath.inc" 'com browser generateed
#INCLUDE "Win32API.inc"

' The direct function call does not involve COM, so...
' we declare it manually here.
' We do not want to load the dll at compile time so comment
'DECLARE FUNCTION AddStrings LIB "Server.dll" ALIAS "AddStrings" _
'    (s1 AS STRING, s2 AS STRING) AS STRING
'
GLOBAL ghInstance AS DWORD

FUNCTION PBMAIN () AS LONG

   DIM MyMath AS StringMath

   LET MyMath = NEWCOM CLSID $CLSID_StringMath_STRINGMATHCLASS LIB "Server.dll"

   ghInstance = LoadLibrary("Server.dll")
   DIM pAddr AS DWORD
   pAddr = GetProcAddress(ghInstance, "AddStrings")

   ' here, we're calling the DLL version of AddStrings
   IF pAddr <> 0 THEN
       MSGBOX "Here is ProcAdress: " + STR$(pAddr)
'    Call DWord pAddr USING AddStrings(11,12) 'TO result_var  'arguments
   END IF
'    MSGBOX "DLL version of AddStrings (11,12)" + AddStrings("11","12")

   ' here, we're calling the COM version of AddStrings
   MSGBOX "MyMath =  " + MyMath.AddStrings("11","12")
   DIM my_result_str AS STRING
   CALL MyMath.AddStrings("44","2")TO my_result_str
   MSGBOX "com ver: " + my_result_str

'It looks like FreeLibrary is not actually working?
   DIM free_lib AS LONG
   free_lib = FreeLibrary (ghInstance)
   MSGBOX "result from FreeLibrary " + STR$(free_lib)
   MSGBOX "now the Client is doing something else"

END FUNCTION

=================================
#COMPILER PBWIN 9
#COMPILE DLL "Server"
#DIM ALL

#COM NAME "StringMath", 1.0
#COM GUID GUID$("{952988DB-403E-41C2-B9E6-0342F4632B3C}")
%USEMACROS = 1
#COM TLIB ON

#INCLUDE "Win32API.inc"

GLOBAL ghInstance AS DWORD

' YOU MUST PICK NEW GUIDS FOR EVERY NEW COM OBJECT, AS UNIQUE IDENTIFIERS.
' *** do not re-use the GUIDs from this example code in other programs ***

'#COM NAME "StringMath", 1.0
'#COM GUID GUID$("{952988DB-403E-41C2-B9E6-0342F4632B3C}")
'#COM TLIB ON


' This is a standard DLL function that can be called directly. No COM needed.
FUNCTION AddStrings _
   ALIAS "AddStrings" _
   (s1 AS STRING, s2 AS STRING) _
   EXPORT AS STRING

'    MSGBOX "AddStrings function"
   FUNCTION = "I am inside dll" + FORMAT$(VAL(s1) + VAL(s2))

END FUNCTION


' This is a COM object that contains a single method. It can be called by any
' language that provides COM support.
CLASS StringMathClass _
   GUID$("{6D46AC9F-7D4A-4361-BE62-766BAA797161}") _
   AS COM ' The AS COM attribute to the CLASS statement makes the class
   'available externally, to virtually any process which is COM-aware.

   INTERFACE StringMath _
       GUID$("{34363450-6B5D-4D14-B0BF-7371EDE1791D}")
'        INHERIT IDISPATCH
       INHERIT DUAL
       METHOD AddStrings ALIAS "AddStrings" (s1 AS STRING, s2 AS STRING) AS STRING
'            MSGBOX "AddStrings COM method"
           ' We'll rely on the AddStrings function to get the work done.
           METHOD = AddStrings(s1, s2)
       END METHOD

   END INTERFACE

END CLASS
'----------------------------------------------------------------------------
'--------
'----------------------------------------------------------------------------
FUNCTION LIBMAIN (BYVAL hInstance   AS LONG, _
                 BYVAL fwdReason   AS LONG, _
                 BYVAL lpvReserved AS LONG) AS LONG

   SELECT CASE fwdReason

   CASE %DLL_PROCESS_ATTACH
       'Indicates that the DLL is being loaded by another process (a DLL
       'or EXE is loading the DLL).  DLLs can use this opportunity to
       'initialize any instance or global data, such as arrays.
       MSGBOX "%DLL_PROCESS_ATTACH"

       ghInstance = hInstance

       FUNCTION = 1   'success!

       'FUNCTION = 0   'failure!  This will prevent the EXE from running.

   CASE %DLL_PROCESS_DETACH
       'Indicates that the DLL is being unloaded or detached from the
       'calling application.  DLLs can take this opportunity to clean
       'up all resources for all threads attached and known to the DLL.
       MSGBOX "%DLL_PROCESS_DETACH"

       FUNCTION = 1   'success!

       'FUNCTION = 0   'failure!

   CASE %DLL_THREAD_ATTACH
       'Indicates that the DLL is being loaded by a new thread in the
       'calling application.  DLLs can use this opportunity to
       'initialize any thread local storage (TLS).
       MSGBOX "%DLL_THREAD_ATTACH"

       FUNCTION = 1   'success!

       'FUNCTION = 0   'failure!

   CASE %DLL_THREAD_DETACH
       'Indicates that the thread is exiting cleanly.  If the DLL has
       'allocated any thread local storage, it should be released.
       MSGBOX "%DLL_THREAD_DETACH"

       FUNCTION = 1   'success!

       'FUNCTION = 0   'failure!

   END SELECT

END FUNCTION 'LIBMAIN
'=========================
'Here is the include file stringmath.inc
' The COM definitions we want are supplied by the PowerBASIC COM Browser.
' Typically, you would place these definitions in an #include file.

' Generated by: PowerBASIC COM Browser v.2.00.0076
' Date & Time : 12/31/2009 at 12:09 PM
' ------------------------------------------------
' Library Name: StringMath
' Library File: C:\proj\gaz\lib\DLL2.tlb
' Description : COM Library
' GUID : {952988DB-403E-41C2-B9E6-0342F4632B3C}
' LCID : 0
' Version : 1.0

' Class Identifiers
$CLSID_StringMath_STRINGMATHCLASS = GUID$("{6D46AC9F-7D4A-4361-BE62-766BAA797161}")

' Interface Identifiers
$IID_StringMath_StringMath = GUID$("{34363450-6B5D-4D14-B0BF-7371EDE1791D}")

' Interface Name  : StringMath
' Description     : STRINGMATH is a dual interface with VTable/Dispatch access.
' Class Name      : STRINGMATHCLASS
' ClassID         : $CLSID_StringMath_STRINGMATHCLASS
INTERFACE StringMath $IID_StringMath_StringMath
   INHERIT IDISPATCH

   METHOD AddStrings <257> (BYREF INOUT S1 AS STRING, BYREF INOUT S2 AS STRING) AS STRING
END INTERFACE

'-------------------------- end of COM definitions


Frederick J. Harris

I didn't look at your code Paul, but you are aware that LoadLibrary() / FreeLibrary() work on a reference counting mechanism?  FreeLibrary() doesn't actually unload the Dll; it simply decrements a reference count on it.  Even if the reference count hits zero, depending on the memory load on the system, its my understanding it might not be released right away.  Maybe that is what is going on in your code?

Paul Breen

Fred, how do force the issue? Since this is a practice program and only run on my computer how do I find out what is holding on the dll? Can I query windows with some function to find out?
thanks,
Paul

Florent Heyworth

#3
Paul

from your code it seems that you have produced either 2 versions (1 COM/1 Standard) of the same DLL (both called Server.dll) or you have 1 DLL which contains both the COM class and the standard DLL code. Which is it?

I notice that you do not set MyMath to nothing. Structure your code as follows:
1. Instantiate COM MyMath, call AddStrings and then set MyMath = NOTHING
2. Next call load library, getprocaddress, call AddStrings and thenn call FreeLibrary()

Does the DLL get unloaded?

Florent


Paul Breen

Hello Florent:
This is a 'hybrid' dll from powerbasic which they call a 'powerlib'.

I added the line "LET MyMath = NOTHING " to the client.bas but it seems to act the same. This code is just slightly changed from a powerbasic gazette about "powerlib", which is the making of a dll with both regular and com functions that duplicate functionality but allows calling both com style and 'regular' function style code on the same dll. You can search 'powerlib' at powerbasic to see the gazette.

I have to use the internet at the library and sometimes the time is restricted so I can't respond as I would like.

However, I tried this with the 'powerlib' (com) feature removed and freelibrary works as advertised. I get the process detach msg before the client shuts down. I suppose there is something about com that prevents this?  So, the 'pure' regular dll is detached, but the com hybrid version does not.

thanks,
PB

José Roca


Paul Breen

#6
Jose, this does not appear to work. I single stepped the debugger on this code (the tail of the program).

'It looks like FreeLibrary is not actually working?
   DIM free_lib AS LONG ' holds result of FreeLibrary
   
   free_lib = FreeLibrary (ghInstance) ' this returns 1, which means success
   CALL CoFreeUnusedLibraries() ' this has no return so I do not know what happened.
   rem I think I should get %DLL_PROCESS_DETACH   here, but I do not..
   MSGBOX "result from FreeLibrary " + STR$(free_lib)
   MSGBOX "now the Client is doing something else"

END FUNCTION 'PBMAIN ' I get %DLL_PROCESS_DETACH   on this line

(later) Do I have to implement DllCanUnloadNow() in my server for CoFreeUnusedLibraries to work?