When in DllMain invoked? (last update: 2013-02-23, created: 2013-02-23) back to the list ↑
Question: Is DllMain invoked at each FreeLibrary?
Answer: No. DllMain is invoked on first load of the module (i.e. when it's loaded into memory), on last free (i.e. when it's unloaded from memory), and when a thread is created or destroyed.

Question: Even with DLL_THREAD_DETACH?
Answer: FreeLibrary and DLL_THREAD_DETACH are not connected in any way. Speaking of FreeLibrary - only the FreeLibrary which decrements the reference counter to 0 invokes DllMain with DLL_PROCESS_DETACH; this reference counter is increased with each load of the module (both dynamic and [delayed] import table based), and decreased with each unload.

Question: Prove it.
Answer: Here's a simple experiment:

File: test_dll.c

// DllMain behavior test, gynvael.coldwind//vx, 2013
#include <windows.h>
#include <stdio.h>

#define UNUSED (void)

__declspec(dllexport) BOOL WINAPI DllMain(
  HINSTANCE hinstDLL,
  DWORD fdwReason,
  LPVOID lpvReserved
) {
  UNUSED hinstDLL; UNUSED lpvReserved;


  const char *reason_to_string[] = {
    "DLL_PROCESS_DETACH",
    "DLL_PROCESS_ATTACH",
    "DLL_THREAD_ATTACH",
    "DLL_THREAD_DETACH"
  };

  printf("DllMain called; fdwReason=%s, threadID=%x\n", 
      reason_to_string[fdwReason],
      (unsigned)GetCurrentThreadId());

  return TRUE;
}


File: test.c

// DllMain behavior test, gynvael.coldwind//vx, 2013
#include <windows.h>
#include <stdio.h>

// Poor mans flags. On x86 access is atomic anyway, so no synchronisation
// is required.
int end_the_thread;
int thread_ready;

#define UNUSED (void)

// Thread that does nothing.
DWORD WINAPI MyThread(LPVOID param) {
  UNUSED param;

  thread_ready = 1;
  while(!end_the_thread) Sleep(0);

  return 0;
}

// Main function.
int main(void) {

  HANDLE hLib[5], hTh;
  int i;

  puts("-- LoadLibrary phase");

  for(i = 0; i < 5; i++) {
    printf("LoadLibrary call #%i\n", i);
    hLib[i] = LoadLibrary("test_dll.dll");
    printf("hLib[%i] is %x\n", i, (unsigned)hLib[i]);
    // Note: hLib will be always the same, since it's the address
    // of the module loaded in memory, and the module is loaded
    // only once (the first time); then just a reference counter
    // is incremented.
  }

  puts("-- CreateThread phase");
  hTh = CreateThread(NULL, 0, MyThread, 0, 0, 0);
  while(!thread_ready) Sleep(0);

  puts("-- Calling FreeLibrary once");
  printf("FreeLibrary call #%i\n", 0);
  FreeLibrary(hLib[0]); // Expect no DllMain calls.

  // Let's sleep here to give the thread some time if it would call
  // the DllMain (which it won't).
  Sleep(500);

  puts("-- Thread Ends phase");
  end_the_thread = 1;
  WaitForSingleObject(hTh, INFINITE);
  
  puts("-- FreeLibrary phase");
  for(i = 1; i < 5; i++) {
    printf("FreeLibrary call #%i\n", i);
    FreeLibrary(hLib[i]);    
  }

  return 0;
}


Console output:

>gcc test_dll.c -c -Wall -Wextra

>dllwrap test_dll.o -o test_dll.dll
dllwrap: no export definition file provided.
Creating one, but that may not be what you want

>gcc test.c -Wall -Wextra

>a
-- LoadLibrary phase
LoadLibrary call #0
DllMain called; fdwReason=DLL_PROCESS_ATTACH, threadID=1ed8
hLib[0] is 6ad40000
LoadLibrary call #1
hLib[1] is 6ad40000
LoadLibrary call #2
hLib[2] is 6ad40000
LoadLibrary call #3
hLib[3] is 6ad40000
LoadLibrary call #4
hLib[4] is 6ad40000
-- CreateThread phase
DllMain called; fdwReason=DLL_THREAD_ATTACH, threadID=1e80
-- Calling FreeLibrary once
FreeLibrary call #0
-- Thread Ends phase
DllMain called; fdwReason=DLL_THREAD_DETACH, threadID=1e80
-- FreeLibrary phase
FreeLibrary call #1
FreeLibrary call #2
FreeLibrary call #3
FreeLibrary call #4
DllMain called; fdwReason=DLL_PROCESS_DETACH, threadID=1ed8

>


Even though FreeLibrary was called 5 times, DllMain was called only 4 times:
1. At first load (LoadLibrary phase, call #0)
2. When a thread was created (CreateThread phase)
3. When it exited (Thread Ends phase)
4. And when the last FreeLibrary was called (FreeLibrary phase, call #4)
【 design & art by Xa / Gynvael Coldwind 】 【 logo font (birdman regular) by utopiafonts / Dale Harris 】