1 /**
2 * This module provides OS specific helper function for DLL support
3 *
4 * Copyright: Copyright Digital Mars 2010 - 2012.
5 * License: Distributed under the
6 * $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost Software License 1.0).
7 * (See accompanying file LICENSE)
8 * Authors: Rainer Schuetze
9 * Source: $(DRUNTIMESRC core/sys/windows/_dll.d)
10 */
11
12 /* NOTE: This file has been patched from the original DMD distribution to
13 * work with the GDC compiler.
14 */
15 module core.sys.windows.dll;
16 version (Windows):
17 @system:
18
19 import core.sys.windows.winbase;
20 import core.sys.windows.winnt;
21 import core.stdc.string;
22 import core.runtime;
23
24 public import core.sys.windows.threadaux;
25
26 ///////////////////////////////////////////////////////////////////
27 // support fixing implicit TLS for dynamically loaded DLLs on Windows XP
28
29 // in this special case, we have to treat _tlsstart and _tlsend as non-TLS variables
30 // as they are used to simulate TLS when it is not set up under XP. In this case we must
31 // not access tls_array[tls_index] as needed for thread local _tlsstart and _tlsend
32 extern (C)
33 {
version(Win32)34 version (Win32)
35 {
36 version (CRuntime_DigitalMars)
37 {
38 extern __gshared byte _tlsstart;
39 extern __gshared byte _tlsend;
40 extern __gshared void* _tls_callbacks_a;
41 }
42 else version (CRuntime_Microsoft)
43 {
44 extern __gshared byte _tls_start;
45 extern __gshared byte _tls_end;
46 extern __gshared void* __xl_a;
47 alias _tls_start _tlsstart;
48 alias _tls_end _tlsend;
49 alias __xl_a _tls_callbacks_a;
50 }
51 extern __gshared int _tls_index;
52 }
53 }
54
55 extern (C) // rt.minfo
56 {
57 void rt_moduleTlsCtor();
58 void rt_moduleTlsDtor();
59 }
60
61 private:
62 struct dll_aux
63 {
64 // don't let symbols leak into other modules
versiondll_aux65 version (Win32)
66 {
67 struct LdrpTlsListEntry
68 {
69 LdrpTlsListEntry* next;
70 LdrpTlsListEntry* prev;
71 void* tlsstart;
72 void* tlsend;
73 void* ptr_tlsindex;
74 void* callbacks;
75 void* zerofill;
76 int tlsindex;
77 }
78
79 alias fnRtlAllocateHeap = extern(Windows)
80 void* function(void* HeapHandle, uint Flags, size_t Size) nothrow;
81
82 // find a code sequence and return the address after the sequence
83 static void* findCodeSequence( void* adr, int len, ref ubyte[] pattern ) nothrow
84 {
85 if ( !adr )
86 return null;
87
88 ubyte* code = cast(ubyte*) adr;
89 for ( int p = 0; p < len; p++ )
90 {
91 if ( code[ p .. p + pattern.length ] == pattern[ 0 .. $ ] )
92 {
93 ubyte* padr = code + p + pattern.length;
94 return padr;
95 }
96 }
97 return null;
98 }
99
100 // find a code sequence and return the (relative) address that follows
101 static void* findCodeReference( void* adr, int len, ref ubyte[] pattern, bool relative ) nothrow
102 {
103 if ( !adr )
104 return null;
105
106 ubyte* padr = cast(ubyte*) findCodeSequence( adr, len, pattern );
107 if ( padr )
108 {
109 if ( relative )
110 return padr + 4 + *cast(int*) padr;
111 return *cast(void**) padr;
112 }
113 return null;
114 }
115
116 // crawl through ntdll to find function _LdrpAllocateTls@0 and references
117 // to _LdrpNumberOfTlsEntries, _NtdllBaseTag and _LdrpTlsList
118 // LdrInitializeThunk
119 // -> _LdrpInitialize@12
120 // -> _LdrpInitializeThread@4
121 // -> _LdrpAllocateTls@0
122 // -> je chunk
123 // _LdrpNumberOfTlsEntries - number of entries in TlsList
124 // _NtdllBaseTag - tag used for RtlAllocateHeap
125 // _LdrpTlsList - root of the double linked list with TlsList entries
126
127 static __gshared int* pNtdllBaseTag; // remembered for reusage in addTlsData
128
129 static __gshared ubyte[] jmp_LdrpInitialize = [ 0x33, 0xED, 0xE9 ]; // xor ebp,ebp; jmp _LdrpInitialize
130 static __gshared ubyte[] jmp__LdrpInitialize = [ 0x5D, 0xE9 ]; // pop ebp; jmp __LdrpInitialize
131 static __gshared ubyte[] jmp__LdrpInitialize_xp64 = [ 0x5D, 0x90, 0x90, 0x90, 0x90, 0x90 ]; // pop ebp; nop; nop; nop; nop; nop;
132 static __gshared ubyte[] call_LdrpInitializeThread = [ 0xFF, 0x75, 0x08, 0xE8 ]; // push [ebp+8]; call _LdrpInitializeThread
133 static __gshared ubyte[] call_LdrpAllocateTls = [ 0x00, 0x00, 0xE8 ]; // jne 0xc3; call _LdrpAllocateTls
134 static __gshared ubyte[] call_LdrpAllocateTls_svr03 = [ 0x65, 0xfc, 0x00, 0xE8 ]; // and [ebp+fc], 0; call _LdrpAllocateTls
135 static __gshared ubyte[] jne_LdrpAllocateTls = [ 0x0f, 0x85 ]; // jne body_LdrpAllocateTls
136 static __gshared ubyte[] mov_LdrpNumberOfTlsEntries = [ 0x8B, 0x0D ]; // mov ecx, _LdrpNumberOfTlsEntries
137 static __gshared ubyte[] mov_NtdllBaseTag = [ 0x51, 0x8B, 0x0D ]; // push ecx; mov ecx, _NtdllBaseTag
138 static __gshared ubyte[] mov_NtdllBaseTag_srv03 = [ 0x50, 0xA1 ]; // push eax; mov eax, _NtdllBaseTag
139 static __gshared ubyte[] mov_LdrpTlsList = [ 0x8B, 0x3D ]; // mov edi, _LdrpTlsList
140
141 static LdrpTlsListEntry* addTlsListEntry( void** peb, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex ) nothrow
142 {
143 HANDLE hnd = GetModuleHandleA( "NTDLL" );
144 assert( hnd, "cannot get module handle for ntdll" );
145 ubyte* fn = cast(ubyte*) GetProcAddress( hnd, "LdrInitializeThunk" );
146 assert( fn, "cannot find LdrInitializeThunk in ntdll" );
147
148 void* pLdrpInitialize = findCodeReference( fn, 20, jmp_LdrpInitialize, true );
149 void* p_LdrpInitialize = findCodeReference( pLdrpInitialize, 40, jmp__LdrpInitialize, true );
150 if ( !p_LdrpInitialize )
151 p_LdrpInitialize = findCodeSequence( pLdrpInitialize, 40, jmp__LdrpInitialize_xp64 );
152 void* pLdrpInitializeThread = findCodeReference( p_LdrpInitialize, 200, call_LdrpInitializeThread, true );
153 void* pLdrpAllocateTls = findCodeReference( pLdrpInitializeThread, 40, call_LdrpAllocateTls, true );
154 if (!pLdrpAllocateTls)
155 pLdrpAllocateTls = findCodeReference( pLdrpInitializeThread, 100, call_LdrpAllocateTls_svr03, true );
156 void* pBodyAllocateTls = findCodeReference( pLdrpAllocateTls, 40, jne_LdrpAllocateTls, true );
157
158 int* pLdrpNumberOfTlsEntries = cast(int*) findCodeReference( pBodyAllocateTls, 60, mov_LdrpNumberOfTlsEntries, false );
159 pNtdllBaseTag = cast(int*) findCodeReference( pBodyAllocateTls, 30, mov_NtdllBaseTag, false );
160 if (!pNtdllBaseTag)
161 pNtdllBaseTag = cast(int*) findCodeReference( pBodyAllocateTls, 30, mov_NtdllBaseTag_srv03, false );
162 LdrpTlsListEntry* pLdrpTlsList = cast(LdrpTlsListEntry*)findCodeReference( pBodyAllocateTls, 80, mov_LdrpTlsList, false );
163
164 if ( !pLdrpNumberOfTlsEntries || !pNtdllBaseTag || !pLdrpTlsList )
165 return null;
166
167 fnRtlAllocateHeap fnAlloc = cast(fnRtlAllocateHeap) GetProcAddress( hnd, "RtlAllocateHeap" );
168 if ( !fnAlloc )
169 return null;
170
171 // allocate new TlsList entry (adding 0xC0000 to the tag is obviously a flag also usesd by
172 // the nt-loader, could be the result of HEAP_MAKE_TAG_FLAGS(0,HEAP_NO_SERIALIZE|HEAP_GROWABLE)
173 // but this is not documented in the msdn entry for RtlAlloateHeap
174 void* heap = peb[6];
175 LdrpTlsListEntry* entry = cast(LdrpTlsListEntry*) (*fnAlloc)( heap, *pNtdllBaseTag | 0xc0000, LdrpTlsListEntry.sizeof );
176 if ( !entry )
177 return null;
178
179 // fill entry
180 entry.tlsstart = tlsstart;
181 entry.tlsend = tlsend;
182 entry.ptr_tlsindex = tlsindex;
183 entry.callbacks = tls_callbacks_a;
184 entry.zerofill = null;
185 entry.tlsindex = *pLdrpNumberOfTlsEntries;
186
187 // and add it to the end of TlsList
188 *tlsindex = *pLdrpNumberOfTlsEntries;
189 entry.next = pLdrpTlsList;
190 entry.prev = pLdrpTlsList.prev;
191 pLdrpTlsList.prev.next = entry;
192 pLdrpTlsList.prev = entry;
193 (*pLdrpNumberOfTlsEntries)++;
194
195 return entry;
196 }
197
198 // reallocate TLS array and create a copy of the TLS data section
199 static bool addTlsData( void** teb, void* tlsstart, void* tlsend, int tlsindex ) nothrow
200 {
201 HANDLE hnd = GetModuleHandleA( "NTDLL" );
202 assert( hnd, "cannot get module handle for ntdll" );
203
204 fnRtlAllocateHeap fnAlloc = cast(fnRtlAllocateHeap) GetProcAddress( hnd, "RtlAllocateHeap" );
205 if ( !fnAlloc || !pNtdllBaseTag )
206 return false;
207
208 void** peb = cast(void**) teb[12];
209 void* heap = peb[6];
210
211 auto sz = tlsend - tlsstart;
212 void* tlsdata = cast(void*) (*fnAlloc)( heap, *pNtdllBaseTag | 0xc0000, sz );
213 if ( !tlsdata )
214 return false;
215
216 // no relocations! not even self-relocations. Windows does not do them.
217 core.stdc.string.memcpy( tlsdata, tlsstart, sz );
218
219 // create copy of tls pointer array
220 void** array = cast(void**) (*fnAlloc)( heap, *pNtdllBaseTag | 0xc0000, (tlsindex + 1) * (void*).sizeof );
221 if ( !array )
222 return false;
223
224 if ( tlsindex > 0 && teb[11] )
225 core.stdc.string.memcpy( array, teb[11], tlsindex * (void*).sizeof);
226 array[tlsindex] = tlsdata;
227 teb[11] = cast(void*) array;
228
229 // let the old array leak, in case a oncurrent thread is still relying on it
230 return true;
231 }
232 } // Win32
233
234 alias bool BOOLEAN;
235
236 struct UNICODE_STRING
237 {
238 short Length;
239 short MaximumLength;
240 wchar* Buffer;
241 }
242
243 struct LIST_ENTRY
244 {
245 LIST_ENTRY* next;
246 LIST_ENTRY* prev;
247 }
248
249 // the following structures can be found here:
250 // https://www.geoffchappell.com/studies/windows/win32/ntdll/structs/ldr_data_table_entry.htm
251 // perhaps this should be same as LDR_DATA_TABLE_ENTRY, which is introduced with PEB_LDR_DATA
252 struct LDR_MODULE
253 {
254 LIST_ENTRY InLoadOrderModuleList;
255 LIST_ENTRY InMemoryOrderModuleList;
256 LIST_ENTRY InInitializationOrderModuleList;
257 PVOID BaseAddress;
258 PVOID EntryPoint;
259 SIZE_T SizeOfImage;
260 UNICODE_STRING FullDllName;
261 UNICODE_STRING BaseDllName;
262 ULONG Flags;
263 SHORT LoadCount; // obsolete after Version 6.1
264 SHORT TlsIndex;
265 LIST_ENTRY HashTableEntry;
266 ULONG TimeDateStamp;
267 PVOID EntryPointActivationContext;
268 PVOID PatchInformation;
269 LDR_DDAG_NODE *DdagNode; // starting with Version 6.2
270 }
271
272 struct LDR_DDAG_NODE
273 {
274 LIST_ENTRY Modules;
275 void* ServiceTagList; // LDR_SERVICE_TAG_RECORD
276 ULONG LoadCount;
277 ULONG ReferenceCount; // Version 10: ULONG LoadWhileUnloadingCount;
278 ULONG DependencyCount; // Version 10: ULONG LowestLink;
279 }
280
281 struct PEB_LDR_DATA
282 {
283 ULONG Length;
284 BOOLEAN Initialized;
285 PVOID SsHandle;
286 LIST_ENTRY InLoadOrderModuleList;
287 LIST_ENTRY InMemoryOrderModuleList;
288 LIST_ENTRY InInitializationOrderModuleList;
289 }
290
findLdrModuledll_aux291 static LDR_MODULE* findLdrModule( HINSTANCE hInstance, void** peb ) nothrow @nogc
292 {
293 PEB_LDR_DATA* ldrData = cast(PEB_LDR_DATA*) peb[3];
294 LIST_ENTRY* root = &ldrData.InLoadOrderModuleList;
295 for (LIST_ENTRY* entry = root.next; entry != root; entry = entry.next)
296 {
297 LDR_MODULE *ldrMod = cast(LDR_MODULE*) entry;
298 if (ldrMod.BaseAddress == hInstance)
299 return ldrMod;
300 }
301 return null;
302 }
303
setDllTlsUsagedll_aux304 static bool setDllTlsUsage( HINSTANCE hInstance, void** peb ) nothrow
305 {
306 LDR_MODULE *thisMod = findLdrModule( hInstance, peb );
307 if ( !thisMod )
308 return false;
309
310 thisMod.TlsIndex = -1; // uses TLS (not the index itself)
311 thisMod.LoadCount = -1; // never unload
312 return true;
313 }
314 }
315
316 public:
317 /* *****************************************************
318 * Fix implicit thread local storage for the case when a DLL is loaded
319 * dynamically after process initialization.
320 * The link time variables are passed to allow placing this function into
321 * an RTL DLL itself.
322 * The problem is described in Bugzilla 3342 and
323 * http://www.nynaeve.net/?p=187, to quote from the latter:
324 *
325 * "When a DLL using implicit TLS is loaded, because the loader doesn't process the TLS
326 * directory, the _tls_index value is not initialized by the loader, nor is there space
327 * allocated for module's TLS data in the ThreadLocalStoragePointer arrays of running
328 * threads. The DLL continues to load, however, and things will appear to work... until the
329 * first access to a __declspec(thread) variable occurs, that is."
330 *
331 * _tls_index is initialized by the compiler to 0, so we can use this as a test.
332 */
dll_fixTLS(HINSTANCE hInstance,void * tlsstart,void * tlsend,void * tls_callbacks_a,int * tlsindex)333 bool dll_fixTLS( HINSTANCE hInstance, void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex ) nothrow
334 {
335 version (GNU_EMUTLS)
336 return true;
337 else version (Win64)
338 return true; // fixed
339 else version (Win32)
340 {
341 /* If the OS has allocated a TLS slot for us, we don't have to do anything
342 * tls_index 0 means: the OS has not done anything, or it has allocated slot 0
343 * Vista and later Windows systems should do this correctly and not need
344 * this function.
345 */
346 if ( *tlsindex != 0 )
347 return true;
348
349 void** peb;
350 asm pure nothrow @nogc
351 {
352 mov EAX,FS:[0x30];
353 mov peb, EAX;
354 }
355 dll_aux.LDR_MODULE *ldrMod = dll_aux.findLdrModule( hInstance, peb );
356 if ( !ldrMod )
357 return false; // not in module list, bail out
358 if ( ldrMod.TlsIndex != 0 )
359 return true; // the OS has already setup TLS
360
361 dll_aux.LdrpTlsListEntry* entry = dll_aux.addTlsListEntry( peb, tlsstart, tlsend, tls_callbacks_a, tlsindex );
362 if ( !entry )
363 return false;
364
365 scope (failure) assert(0); // enforce nothrow, Bugzilla 13561
366
367 if ( !enumProcessThreads(
368 function (uint id, void* context) nothrow {
369 dll_aux.LdrpTlsListEntry* entry = cast(dll_aux.LdrpTlsListEntry*) context;
370 return dll_aux.addTlsData( getTEB( id ), entry.tlsstart, entry.tlsend, entry.tlsindex );
371 }, entry ) )
372 return false;
373
374 ldrMod.TlsIndex = -1; // flag TLS usage (not the index itself)
375 ldrMod.LoadCount = -1; // prevent unloading of the DLL,
376 // since XP does not keep track of used TLS entries
377 return true;
378 }
379 }
380
381 private extern (Windows) ULONGLONG VerSetConditionMask(ULONGLONG, DWORD, BYTE) nothrow @nogc;
382
isWindows8OrLater()383 private bool isWindows8OrLater() nothrow @nogc
384 {
385 OSVERSIONINFOEXW osvi;
386 osvi.dwOSVersionInfoSize = osvi.sizeof;
387 DWORDLONG dwlConditionMask = VerSetConditionMask(
388 VerSetConditionMask(
389 VerSetConditionMask(
390 0, VER_MAJORVERSION, VER_GREATER_EQUAL),
391 VER_MINORVERSION, VER_GREATER_EQUAL),
392 VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
393
394 osvi.dwMajorVersion = 6;
395 osvi.dwMinorVersion = 2;
396 osvi.wServicePackMajor = 0;
397
398 return VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
399 }
400
401 /* *****************************************************
402 * Get the process reference count for the given DLL handle
403 * Params:
404 * hInstance = DLL instance handle
405 * Returns:
406 * the reference count for the DLL in the current process,
407 * -1 if the DLL is implicitely loaded with the process
408 * or -2 if the DLL handle is invalid
409 */
dll_getRefCount(HINSTANCE hInstance)410 int dll_getRefCount( HINSTANCE hInstance ) nothrow @nogc
411 {
412 void** peb;
413 version (Win64)
414 {
415 version (GNU_InlineAsm)
416 {
417 asm pure nothrow @nogc { "movq %%gs:0x60, %0;" : "=r" (peb); }
418 }
419 else
420 {
421 asm pure nothrow @nogc
422 {
423 mov RAX, 0x60;
424 mov RAX,GS:[RAX];
425 mov peb, RAX;
426 }
427 }
428 }
429 else version (Win32)
430 {
431 version (GNU_InlineAsm)
432 {
433 asm pure nothrow @nogc { "movl %%fs:0x30, %0;" : "=r" (peb); }
434 }
435 else
436 {
437 asm pure nothrow @nogc
438 {
439 mov EAX,FS:[0x30];
440 mov peb, EAX;
441 }
442 }
443 }
444 dll_aux.LDR_MODULE *ldrMod = dll_aux.findLdrModule( hInstance, peb );
445 if ( !ldrMod )
446 return -2; // not in module list, bail out
447 if (isWindows8OrLater())
448 return ldrMod.DdagNode.LoadCount;
449 return ldrMod.LoadCount;
450 }
451
452 // fixup TLS storage, initialize runtime and attach to threads
453 // to be called from DllMain with reason DLL_PROCESS_ATTACH
dll_process_attach(HINSTANCE hInstance,bool attach_threads,void * tlsstart,void * tlsend,void * tls_callbacks_a,int * tlsindex)454 bool dll_process_attach( HINSTANCE hInstance, bool attach_threads,
455 void* tlsstart, void* tlsend, void* tls_callbacks_a, int* tlsindex )
456 {
457 version (Win32)
458 {
459 if ( !dll_fixTLS( hInstance, tlsstart, tlsend, tls_callbacks_a, tlsindex ) )
460 return false;
461 }
462
463 Runtime.initialize();
464
465 if ( !attach_threads )
466 return true;
467
468 // attach to all other threads
469 return enumProcessThreads(
470 function (uint id, void* context) {
471 if ( !thread_findByAddr( id ) && !findLowLevelThread( id ) )
472 {
473 // if the OS has not prepared TLS for us, don't attach to the thread
474 if ( GetTlsDataAddress( id ) )
475 {
476 thread_attachByAddr( id );
477 thread_moduleTlsCtor( id );
478 }
479 }
480 return true;
481 }, null );
482 }
483
484 // same as above, but only usable if druntime is linked statically
485 bool dll_process_attach( HINSTANCE hInstance, bool attach_threads = true )
486 {
version(Win64)487 version (Win64)
488 {
489 return dll_process_attach( hInstance, attach_threads,
490 null, null, null, null );
491 }
version(Win32)492 else version (Win32)
493 {
494 return dll_process_attach( hInstance, attach_threads,
495 &_tlsstart, &_tlsend, &_tls_callbacks_a, &_tls_index );
496 }
497 }
498
499 // to be called from DllMain with reason DLL_PROCESS_DETACH
500 void dll_process_detach( HINSTANCE hInstance, bool detach_threads = true )
501 {
502 // notify core.thread.joinLowLevelThread that the DLL is about to be unloaded
503 thread_DLLProcessDetaching = true;
504
505 // detach from all other threads
506 if ( detach_threads )
507 enumProcessThreads(
508 function (uint id, void* context)
509 {
510 if ( id != GetCurrentThreadId() )
511 {
512 if ( auto t = thread_findByAddr( id ) )
513 {
514 thread_moduleTlsDtor( id );
515 if ( !t.isMainThread() )
516 thread_detachByAddr( id );
517 }
518 }
519 return true;
520 }, null );
521
522 Runtime.terminate();
523 }
524
525 /* Make sure that tlsCtorRun is itself a tls variable
526 */
527 static bool tlsCtorRun;
this()528 static this() { tlsCtorRun = true; }
~this()529 static ~this() { tlsCtorRun = false; }
530
531 // to be called from DllMain with reason DLL_THREAD_ATTACH
532 bool dll_thread_attach( bool attach_thread = true, bool initTls = true )
533 {
534 // if the OS has not prepared TLS for us, don't attach to the thread
535 // (happened when running under x64 OS)
536 auto tid = GetCurrentThreadId();
537 if ( !GetTlsDataAddress( tid ) )
538 return false;
539 if ( !thread_findByAddr( tid ) && !findLowLevelThread( tid ) )
540 {
541 // only attach to thread and initalize it if it is not in the thread list (so it's not created by "new Thread")
542 if ( attach_thread )
543 thread_attachThis();
544 if ( initTls && !tlsCtorRun ) // avoid duplicate calls
545 rt_moduleTlsCtor();
546 }
547 return true;
548 }
549
550 // to be called from DllMain with reason DLL_THREAD_DETACH
551 bool dll_thread_detach( bool detach_thread = true, bool exitTls = true )
552 {
553 // if the OS has not prepared TLS for us, we did not attach to the thread
554 if ( !GetTlsDataAddress( GetCurrentThreadId() ) )
555 return false;
556 if ( thread_findByAddr( GetCurrentThreadId() ) )
557 {
558 if ( exitTls && tlsCtorRun ) // avoid dtors to be run twice
559 rt_moduleTlsDtor();
560 if ( detach_thread )
561 thread_detachThis();
562 }
563 return true;
564 }
565
566 /// A simple mixin to provide a $(D DllMain) which calls the necessary
567 /// runtime initialization and termination functions automatically.
568 ///
569 /// Instead of writing a custom $(D DllMain), simply write:
570 ///
571 /// ---
572 /// mixin SimpleDllMain;
573 /// ---
SimpleDllMain()574 mixin template SimpleDllMain()
575 {
576 import core.sys.windows.windef : HINSTANCE, BOOL, DWORD, LPVOID;
577
578 extern(Windows)
579 BOOL DllMain(HINSTANCE hInstance, DWORD ulReason, LPVOID reserved)
580 {
581 import core.sys.windows.winnt;
582 import core.sys.windows.dll :
583 dll_process_attach, dll_process_detach,
584 dll_thread_attach, dll_thread_detach;
585 switch (ulReason)
586 {
587 default: assert(0);
588 case DLL_PROCESS_ATTACH:
589 return dll_process_attach( hInstance, true );
590
591 case DLL_PROCESS_DETACH:
592 dll_process_detach( hInstance, true );
593 return true;
594
595 case DLL_THREAD_ATTACH:
596 return dll_thread_attach( true, true );
597
598 case DLL_THREAD_DETACH:
599 return dll_thread_detach( true, true );
600 }
601 }
602 }
603