News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

LoadLibrary, GetProcAddress, FreeLibrary

Started by dedndave, November 06, 2015, 05:20:09 AM

Previous topic - Next topic

dedndave

my current solution is to use this code
i am considering getting rid of the FreeLibrary call, and letting ExitProcess perform the clean-up
i have to test that strategy on a variety of OS's, though

    INVOKE  GetModuleHandleA,offset __szKernel32
    .if eax
        push    0                                   ;do not free the library handle
    .else
        INVOKE  LoadLibraryA,offset __szKernel32
        push    eax                                 ;free the library handle if non-zero
    .endif

    .if eax
        ;get proc addresses, here
    .endif

    pop     eax
    .if eax
        INVOKE  FreeLibrary,eax
    .endif

jj2007

Quote from: dedndave on November 07, 2015, 04:05:40 AMi am considering getting rid of the FreeLibrary call, and letting ExitProcess perform the clean-up

Should work, but I still lack a good reference which types of resources (memory, gdi objects, bitmaps, DLLs, ...) are cleaned up by ExitProcess, and which aren't. Documentation is vague and sparse :(

dedndave

it would seem logical, because the process memory is released
but - i need to devise a test of some sort to see if it works on win95, 98, 2K.....

TouEnMasm

It seems that many anwer are here:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms684232(v=vs.85).aspx
Fa is a musical note to play with CL

jj2007

Quote from: ToutEnMasm on November 07, 2015, 07:20:00 PM
It seems that many anwer are here:
https://msdn.microsoft.com/en-us/library/windows/desktop/ms684232(v=vs.85).aspx

Explain where exactly these pages on "Module Information" elaborate which types of resources (memory, gdi objects, bitmaps, DLLs, ...) are cleaned up by ExitProcess, and which aren't.

Simple examples:
QuoteBecause Win32 is based on Win16 there are a lot of Win16 legacy APIs that deal with window messages and the clipboard that do in fact expect HBITMAP's to be usable from multiple processes

Quoteresources managed by the Windows window manager, USER and GDI objects, that represent window elements (like windows and menus) and graphics constructs (like pens, brushes and drawing surfaces). Just like for the other resources I've discussed in previous posts, exhausting the various USER and GDI resource limits can lead to unpredictable behavior, including application failures and an unusable system

Released by ExitProcess, yes or no?

hutch--

I think you can reasonably assume the ExitProcess() does what its name says, exits a process and that entails dumping all of what was loaded when it was running. The reason to use FreeLibrary() apart from the documentation recommendation is to make the exit tidy and fast. I am yet to see what the gain is in omitting FreeLibrary() when its only a simple API call.

sinsi

Fairly straightforward  :badgrin:
Quote from: MSDN
Exiting a process causes the following:

    All of the threads in the process, except the calling thread, terminate their execution without receiving a DLL_THREAD_DETACH notification.
    The states of all of the threads terminated in step 1 become signaled.
    The entry-point functions of all loaded dynamic-link libraries (DLLs) are called with DLL_PROCESS_DETACH.
    After all attached DLLs have executed any process termination code, the ExitProcess function terminates the current process, including the calling thread.
    The state of the calling thread becomes signaled.
    All of the object handles opened by the process are closed.
    The termination status of the process changes from STILL_ACTIVE to the exit value of the process.
    The state of the process object becomes signaled, satisfying any threads that had been waiting for the process to terminate.
https://msdn.microsoft.com/en-us/library/windows/desktop/ms682658%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396

jj2007

Hutch, Sinsi,

Thanks. In my own proggies, Dll & Declare perform the FreeLibrary; perhaps it's not needed... OTOH, I never indulge in DeleteObject orgies in a WM_DESTROY handler, although you may find such stuff in Windows programming tutorials.

Below a small collection of related sources.

The Old New Thing: Quick overview of how processes exit on Windows XP
3 May 2007
The kernel will close all your open handles to kernel objects. Any memory you allocated will be freed automatically when the process's address space is torn down.

ExitProcess function
6. All of the object handles opened by the process are closed.

Terminating a process has the following results:
    Any remaining threads in the process are marked for termination.
    Any resources allocated by the process are freed.
    All kernel objects are closed.
    The process code is removed from memory.
    The process exit code is set.
    The process object is signaled.

While open handles to kernel objects are closed automatically when a process terminates, the objects themselves exist until all open handles to them are closed. Therefore, an object will remain valid after a process that is using it terminates if another process has an open handle to it.


CodeGuru Alexey B
Regardless of how the process terminates, the system guarantees that all allocated memory, all User and GDI objects are freed, all open files are closed and the usage count on all kernel objects is decremented.

Mark Russinovich's Blog: Pushing the Limits of Windows: USER and GDI Objects

User Objects
With the fundamental concepts in hand, let's turn our attention first to USER objects. USER objects get their name from the fact that they represent user interface elements like desktops, windows, menus, cursors, icons, and accelerator tables (menu keyboard shortcuts). Despite the fact that USER objects are associated with a specific desktop, they must be accessible from all the desktops of a session, for example to allow a process on one desktop to register for a hotkey that can be entered on any of them. For that reason, the window manager assigns USER object identifiers that are scoped to a window station.

A basic limitation imposed by the window manager is that no process can create more than 10,000 USER objects. That limitation attempts to prevent a single process from exhausting the resources associated with USER objects, either because it's programmed with algorithms that can create excessive number of objects or because it leaks objects by allocating them and not deleting them when it's through using them. You can easily verify this limit by running the Sysinternals Testlimit utility with the –u switch, which directs Testlimit to create as many USER objects as it can


Debugging a GDI Resource Leak

--Definitions--

GDI Objects are resources that are managed by GDI32.DLL on behalf of an application. Some of the most common types of GDI objects are Device Contexts (DCs), Bitmaps, Brushes, Fonts, Metafiles, Pens, and Regions.  GDI Objects are stored in Kernel Memory (specifically the Paged Pool or Session Pool portions of kernel memory – more on this later).

GDI Handles are unique identifiers of a GDI Object.  Each GDI Object can have only one handle.  Each GDI Handle is process-specific (cannot be used by other processes).

The GDI Handle Table is a table (array) of GDI entries.  Each entry contains 32-bits of information about a GDI object, including the handle, the type of the object (i.e. bitmap, DC, pen, font, etc.), the process for which that handle is valid, and a pointer to the actual GDI object in Kernel Memory. This table exists in User Memory.

dedndave

and, yet, it's not hard to get a memory leak
seems to me that everything documented is not always closed
and, it is likely to be an OS version sensitive issue


Quote from: hutch-- on November 07, 2015, 07:53:14 PM
I am yet to see what the gain is in omitting FreeLibrary() when its only a simple API call.

well - i write a library function that gets the proc address for some function the first time it's used
i can provide a special exit clean-up routine, but there's no guarantee that the user calls it before exit

let me cite a specific example
GlobalMemoryStatusEx is not supported by earlier OS versions
so - the library function that calls it tries to initialize the address via GetProcAddress
this is better than calling it directly, causing it to crash on older OS's
in the event that it is not supported, the routine can fall back to GlobalMemoryStatus (no Ex)

however, if i have to use LoadLibrary to ensure that Kernel32.dll is loaded,
i have incremented the reference count on that module

when it comes time to exit, the user must call a library function to decrement the count (FreeLibrary call)
if the user omits that call - what happens ?
it would be nice to eliminate that requirement by some sort of trickery
well - as we've discussed before, GetModuleHandle does not increment the reference count
but, Hutch claims it may not be safe to assume that Kernel32 is always loaded

hutch--

> but, Hutch claims it may not be safe to assume that Kernel32 is always loaded

No, I have made the point that you risk unreliable results in other OS versions if you don't clean up correctly. If you load a library, you free it after, whether the procedure exists or not, it really that simple.

In sequence,

LoadLibrary()         ; load the library address
GetProcAddress(), ; whoops, the function does not exist
FreeLibrary()          ; you have still loaded the library.

Try this.

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤
    include \masm32\include\masm32rt.inc
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    .data?
      pMessageBox dd ?
      pDudFunction dd ?

    .data
      titl db "Msgbox Call",0
      tmsg db "Howdy from the MessageBox address",0

    .code

start:
   
; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

    call main

    exit

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

main proc

    LOCAL pKernel :DWORD
    LOCAL pUser   :DWORD

    fn LoadLibrary,"user32.dll"
    mov pUser, eax

    fn GetProcAddress,pUser,"MessageBoxA"
    mov pMessageBox, eax

    fn FreeLibrary, pUser

    push 0
    push OFFSET titl
    push OFFSET tmsg
    push 0
    call pMessageBox

    fn LoadLibrary,"kernel32.dll"
    mov pKernel, eax
    fn GetProcAddress,pKernel,"DudFunction"
    .if eax == 0
      fn MessageBox,0,"Sorry Cannot find that function",str$(eax),MB_OK
    .endif

    fn FreeLibrary, pKernel
  ; ----------------------------------------------------
  ; non zero means the FreeLibrary call worked correctly
  ; ----------------------------------------------------
    fn MessageBox,0,str$(eax),"FreeLibrary return value",MB_OK

    ret

main endp

; ¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤¤

end start

nidud

#25
deleted

TWell

Dilemma
If someone use GetProcAddress , kernel32.dll is already loaded ;)
So other functions from kernel32.dll are available too? GetModuleHandleA is enough?

jj2007

Quote from: TWell on November 08, 2015, 02:07:24 AM
Dilemma
If someone use GetProcAddress , kernel32.dll is already loaded ;)
So other functions from kernel32.dll are available too? GetModuleHandleA is enough?

Who knows?

include \masm32\include\masm32rt.inc

.code
start:
  mov esi, rv(GetProcAddress, rv(GetModuleHandle, "Kernel32"), "CreateFileA")
  print LastError$()
  inkey hex$(esi)
  exit

end start


Adamanteus

 The imported libraries in exe header, makes library loaded during program execution - so need count also this.

nidud

#29
deleted