2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS/Win32 Base enviroment Subsystem Server
4 * FILE: subsystems/win/basesrv/init.c
5 * PURPOSE: Initialization
6 * PROGRAMMERS: Hermes Belusca-Maito (hermes.belusca@sfr.fr)
14 HANDLE DllHandle
= NULL
;
15 HANDLE BaseApiPort
= NULL
;
18 HANDLE BaseSrvHeap
= NULL
; // Our own heap.
19 HANDLE BaseSrvSharedHeap
= NULL
; // Shared heap with CSR. (CsrSrvSharedSectionHeap)
20 PBASE_STATIC_SERVER_DATA BaseStaticServerData
= NULL
; // Data that we can share amongst processes. Initialized inside BaseSrvSharedHeap.
22 extern LIST_ENTRY DosDeviceHistory
;
23 extern RTL_CRITICAL_SECTION BaseDefineDosDeviceCritSec
;
25 // Windows NT 4 tables, adapted from http://j00ru.vexillium.org/csrss_list/api_list.html#Windows_NT
26 // It is for testing purposes. After that I will update it to 2k3 version and add stubs.
27 // Some names are also deduced from the subsystems/win32/csrss/csrsrv/server.c ones.
28 PCSR_API_ROUTINE BaseServerApiDispatchTable
[BasepMaxApiNumber
] =
34 // BaseSrvDebugProcess,
36 // BaseSrvUpdateVDMEntry,
37 // BaseSrvGetNextVDMCommand,
40 // BaseSrvGetVDMExitCode,
41 // BaseSrvSetReenterCount,
42 BaseSrvSetProcessShutdownParam
,
43 BaseSrvGetProcessShutdownParam
,
44 // BaseSrvNlsSetUserInfo,
45 // BaseSrvNlsSetMultipleUserInfo,
46 // BaseSrvNlsCreateSortSection,
47 // BaseSrvNlsPreserveSection,
48 // BaseSrvSetVDMCurDirs,
49 // BaseSrvGetVDMCurDirs,
50 // BaseSrvBatNotification,
51 // BaseSrvRegisterWowExec,
52 BaseSrvSoundSentryNotification
,
53 // BaseSrvRefreshIniFileMapping,
54 BaseSrvDefineDosDevice
57 BOOLEAN BaseServerApiServerValidTable
[BasepMaxApiNumber
] =
59 TRUE
, // SrvCreateProcess,
60 TRUE
, // SrvCreateThread,
61 TRUE
, // SrvGetTempFile,
62 FALSE
, // SrvExitProcess,
63 // FALSE, // SrvDebugProcess,
64 // TRUE, // SrvCheckVDM,
65 // TRUE, // SrvUpdateVDMEntry
66 // TRUE, // SrvGetNextVDMCommand
67 // TRUE, // SrvExitVDM
68 // TRUE, // SrvIsFirstVDM
69 // TRUE, // SrvGetVDMExitCode
70 // TRUE, // SrvSetReenterCount
71 TRUE
, // SrvSetProcessShutdownParam
72 TRUE
, // SrvGetProcessShutdownParam
73 // TRUE, // SrvNlsSetUserInfo
74 // TRUE, // SrvNlsSetMultipleUserInfo
75 // TRUE, // SrvNlsCreateSortSection
76 // TRUE, // SrvNlsPreserveSection
77 // TRUE, // SrvSetVDMCurDirs
78 // TRUE, // SrvGetVDMCurDirs
79 // TRUE, // SrvBatNotification
80 // TRUE, // SrvRegisterWowExec
81 TRUE
, // SrvSoundSentryNotification
82 // TRUE, // SrvRefreshIniFileMapping
83 TRUE
, // SrvDefineDosDevice
87 PCHAR BaseServerApiNameTable
[BasepMaxApiNumber
] =
93 // "BaseDebugProcess",
95 // "BaseUpdateVDMEntry",
96 // "BaseGetNextVDMCommand",
99 // "BaseGetVDMExitCode",
100 // "BaseSetReenterCount",
101 "BaseSetProcessShutdownParam",
102 "BaseGetProcessShutdownParam",
103 // "BaseNlsSetUserInfo",
104 // "BaseNlsSetMultipleUserInfo",
105 // "BaseNlsCreateSortSection",
106 // "BaseNlsPreserveSection",
107 // "BaseSetVDMCurDirs",
108 // "BaseGetVDMCurDirs",
109 // "BaseBatNotification",
110 // "BaseRegisterWowExec",
111 "BaseSoundSentryNotification",
112 // "BaseSrvRefreshIniFileMapping"
113 "BaseDefineDosDevice",
118 /* FUNCTIONS ******************************************************************/
122 CreateBaseAcls(OUT PACL
* Dacl
,
123 OUT PACL
* RestrictedDacl
)
125 PSID SystemSid
, WorldSid
, RestrictedSid
;
126 SID_IDENTIFIER_AUTHORITY NtAuthority
= {SECURITY_NT_AUTHORITY
};
127 SID_IDENTIFIER_AUTHORITY WorldAuthority
= {SECURITY_WORLD_SID_AUTHORITY
};
129 // UCHAR KeyValueBuffer[0x40];
130 // PKEY_VALUE_PARTIAL_INFORMATION KeyValuePartialInfo;
131 // UNICODE_STRING KeyName;
132 // ULONG ProtectionMode = 0;
133 ULONG AclLength
; // , ResultLength;
135 // OBJECT_ATTRIBUTES ObjectAttributes;
137 /* Open the Session Manager Key */
139 RtlInitUnicodeString(&KeyName, SM_REG_KEY);
140 InitializeObjectAttributes(&ObjectAttributes,
142 OBJ_CASE_INSENSITIVE,
145 Status = NtOpenKey(&hKey, KEY_READ, &ObjectAttributes);
146 if (NT_SUCCESS(Status))
148 /\* Read the key value *\/
149 RtlInitUnicodeString(&KeyName, L"ProtectionMode");
150 Status = NtQueryValueKey(hKey,
152 KeyValuePartialInformation,
154 sizeof(KeyValueBuffer),
157 /\* Make sure it's what we expect it to be *\/
158 KeyValuePartialInfo = (PKEY_VALUE_PARTIAL_INFORMATION)KeyValueBuffer;
159 if ((NT_SUCCESS(Status)) && (KeyValuePartialInfo->Type == REG_DWORD) &&
160 (*(PULONG)KeyValuePartialInfo->Data))
162 /\* Save the Protection Mode *\/
163 // ProtectionMode = *(PULONG)KeyValuePartialInfo->Data;
166 /\* Close the handle *\/
171 /* Allocate the System SID */
172 Status
= RtlAllocateAndInitializeSid(&NtAuthority
,
173 1, SECURITY_LOCAL_SYSTEM_RID
,
176 ASSERT(NT_SUCCESS(Status
));
178 /* Allocate the World SID */
179 Status
= RtlAllocateAndInitializeSid(&WorldAuthority
,
180 1, SECURITY_WORLD_RID
,
183 ASSERT(NT_SUCCESS(Status
));
185 /* Allocate the restricted SID */
186 Status
= RtlAllocateAndInitializeSid(&NtAuthority
,
187 1, SECURITY_RESTRICTED_CODE_RID
,
190 ASSERT(NT_SUCCESS(Status
));
192 /* Allocate one ACL with 3 ACEs each for one SID */
193 AclLength
= sizeof(ACL
) + 3 * sizeof(ACCESS_ALLOWED_ACE
) +
194 RtlLengthSid(SystemSid
) +
195 RtlLengthSid(RestrictedSid
) +
196 RtlLengthSid(WorldSid
);
197 *Dacl
= RtlAllocateHeap(BaseSrvHeap
, 0, AclLength
);
198 ASSERT(*Dacl
!= NULL
);
200 /* Set the correct header fields */
201 Status
= RtlCreateAcl(*Dacl
, AclLength
, ACL_REVISION2
);
202 ASSERT(NT_SUCCESS(Status
));
204 /* Give the appropriate rights to each SID */
205 /* FIXME: Should check SessionId/ProtectionMode */
206 Status
= RtlAddAccessAllowedAce(*Dacl
, ACL_REVISION2
, DIRECTORY_QUERY
| DIRECTORY_TRAVERSE
| DIRECTORY_CREATE_OBJECT
| DIRECTORY_CREATE_SUBDIRECTORY
| READ_CONTROL
, WorldSid
);
207 ASSERT(NT_SUCCESS(Status
));
208 Status
= RtlAddAccessAllowedAce(*Dacl
, ACL_REVISION2
, DIRECTORY_ALL_ACCESS
, SystemSid
);
209 ASSERT(NT_SUCCESS(Status
));
210 Status
= RtlAddAccessAllowedAce(*Dacl
, ACL_REVISION2
, DIRECTORY_TRAVERSE
, RestrictedSid
);
211 ASSERT(NT_SUCCESS(Status
));
213 /* Now allocate the restricted DACL */
214 *RestrictedDacl
= RtlAllocateHeap(BaseSrvHeap
, 0, AclLength
);
215 ASSERT(*RestrictedDacl
!= NULL
);
218 Status
= RtlCreateAcl(*RestrictedDacl
, AclLength
, ACL_REVISION2
);
219 ASSERT(NT_SUCCESS(Status
));
221 /* And add the same ACEs as before */
222 /* FIXME: Not really fully correct */
223 Status
= RtlAddAccessAllowedAce(*RestrictedDacl
, ACL_REVISION2
, DIRECTORY_QUERY
| DIRECTORY_TRAVERSE
| DIRECTORY_CREATE_OBJECT
| DIRECTORY_CREATE_SUBDIRECTORY
| READ_CONTROL
, WorldSid
);
224 ASSERT(NT_SUCCESS(Status
));
225 Status
= RtlAddAccessAllowedAce(*RestrictedDacl
, ACL_REVISION2
, DIRECTORY_ALL_ACCESS
, SystemSid
);
226 ASSERT(NT_SUCCESS(Status
));
227 Status
= RtlAddAccessAllowedAce(*RestrictedDacl
, ACL_REVISION2
, DIRECTORY_TRAVERSE
, RestrictedSid
);
228 ASSERT(NT_SUCCESS(Status
));
230 /* The SIDs are captured, can free them now */
231 RtlFreeHeap(BaseSrvHeap
, 0, SystemSid
);
232 RtlFreeHeap(BaseSrvHeap
, 0, WorldSid
);
233 RtlFreeHeap(BaseSrvHeap
, 0, RestrictedSid
);
239 BaseInitializeStaticServerData(IN PCSR_SERVER_DLL LoadedServerDll
)
242 WCHAR Buffer
[MAX_PATH
];
244 UNICODE_STRING SystemRootString
;
245 UNICODE_STRING UnexpandedSystemRootString
= RTL_CONSTANT_STRING(L
"%SystemRoot%");
246 UNICODE_STRING BaseSrvCSDString
;
247 UNICODE_STRING BaseSrvWindowsDirectory
;
248 UNICODE_STRING BaseSrvWindowsSystemDirectory
;
249 UNICODE_STRING BnoString
;
250 OBJECT_ATTRIBUTES ObjectAttributes
;
252 HANDLE BaseSrvNamedObjectDirectory
;
253 HANDLE BaseSrvRestrictedObjectDirectory
;
254 PACL BnoDacl
, BnoRestrictedDacl
;
255 PSECURITY_DESCRIPTOR BnoSd
;
257 UNICODE_STRING DirectoryName
, SymlinkName
;
259 RTL_QUERY_REGISTRY_TABLE BaseServerRegistryConfigurationTable
[2] =
263 RTL_QUERY_REGISTRY_DIRECT
,
272 /* Initialize memory */
273 BaseSrvHeap
= RtlGetProcessHeap(); // Initialize our own heap.
274 BaseSrvSharedHeap
= LoadedServerDll
->SharedSection
; // Get the CSR shared heap.
276 /* Get the session ID */
277 SessionId
= NtCurrentPeb()->SessionId
;
279 /* Get the Windows directory */
280 RtlInitEmptyUnicodeString(&SystemRootString
, Buffer
, sizeof(Buffer
));
281 Status
= RtlExpandEnvironmentStrings_U(NULL
,
282 &UnexpandedSystemRootString
,
285 ASSERT(NT_SUCCESS(Status
));
287 /* Create the base directory */
288 Buffer
[SystemRootString
.Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
289 Status
= RtlCreateUnicodeString(&BaseSrvWindowsDirectory
,
290 SystemRootString
.Buffer
);
291 ASSERT(NT_SUCCESS(Status
));
293 /* Create the system directory */
294 wcscat(SystemRootString
.Buffer
, L
"\\System32");
295 Status
= RtlCreateUnicodeString(&BaseSrvWindowsSystemDirectory
,
296 SystemRootString
.Buffer
);
297 ASSERT(NT_SUCCESS(Status
));
299 /* FIXME: Check Session ID */
300 wcscpy(Buffer
, L
"\\BaseNamedObjects");
301 RtlInitUnicodeString(&BnoString
, Buffer
);
303 /* Allocate the server data */
304 BaseStaticServerData
= RtlAllocateHeap(BaseSrvSharedHeap
,
306 sizeof(BASE_STATIC_SERVER_DATA
));
307 ASSERT(BaseStaticServerData
!= NULL
);
309 /* Process timezone information */
310 BaseStaticServerData
->TermsrvClientTimeZoneId
= TIME_ZONE_ID_INVALID
;
311 BaseStaticServerData
->TermsrvClientTimeZoneChangeNum
= 0;
312 Status
= NtQuerySystemInformation(SystemTimeOfDayInformation
,
313 &BaseStaticServerData
->TimeOfDay
,
314 sizeof(BaseStaticServerData
->TimeOfDay
),
316 ASSERT(NT_SUCCESS(Status
));
318 /* Make a shared heap copy of the Windows directory */
319 BaseStaticServerData
->WindowsDirectory
= BaseSrvWindowsDirectory
;
320 HeapBuffer
= RtlAllocateHeap(BaseSrvSharedHeap
,
322 BaseSrvWindowsDirectory
.MaximumLength
);
324 RtlCopyMemory(HeapBuffer
,
325 BaseStaticServerData
->WindowsDirectory
.Buffer
,
326 BaseSrvWindowsDirectory
.MaximumLength
);
327 BaseStaticServerData
->WindowsDirectory
.Buffer
= HeapBuffer
;
329 /* Make a shared heap copy of the System directory */
330 BaseStaticServerData
->WindowsSystemDirectory
= BaseSrvWindowsSystemDirectory
;
331 HeapBuffer
= RtlAllocateHeap(BaseSrvSharedHeap
,
333 BaseSrvWindowsSystemDirectory
.MaximumLength
);
335 RtlCopyMemory(HeapBuffer
,
336 BaseStaticServerData
->WindowsSystemDirectory
.Buffer
,
337 BaseSrvWindowsSystemDirectory
.MaximumLength
);
338 BaseStaticServerData
->WindowsSystemDirectory
.Buffer
= HeapBuffer
;
340 /* This string is not used */
341 RtlInitEmptyUnicodeString(&BaseStaticServerData
->WindowsSys32x86Directory
,
345 /* Make a shared heap copy of the BNO directory */
346 BaseStaticServerData
->NamedObjectDirectory
= BnoString
;
347 BaseStaticServerData
->NamedObjectDirectory
.MaximumLength
= BnoString
.Length
+
348 sizeof(UNICODE_NULL
);
349 HeapBuffer
= RtlAllocateHeap(BaseSrvSharedHeap
,
351 BaseStaticServerData
->NamedObjectDirectory
.MaximumLength
);
353 RtlCopyMemory(HeapBuffer
,
354 BaseStaticServerData
->NamedObjectDirectory
.Buffer
,
355 BaseStaticServerData
->NamedObjectDirectory
.MaximumLength
);
356 BaseStaticServerData
->NamedObjectDirectory
.Buffer
= HeapBuffer
;
359 * Confirmed that in Windows, CSDNumber and RCNumber are actually Length
360 * and MaximumLength of the CSD String, since the same UNICODE_STRING is
361 * being queried twice, the first time as a ULONG!
363 * Somehow, in Windows this doesn't cause a buffer overflow, but it might
364 * in ReactOS, so this code is disabled until someone figures out WTF.
366 BaseStaticServerData
->CSDNumber
= 0;
367 BaseStaticServerData
->RCNumber
= 0;
369 /* Initialize the CSD string and query its value from the registry */
370 RtlInitEmptyUnicodeString(&BaseSrvCSDString
, Buffer
, sizeof(Buffer
));
371 Status
= RtlQueryRegistryValues(RTL_REGISTRY_WINDOWS_NT
,
373 BaseServerRegistryConfigurationTable
,
376 if (NT_SUCCESS(Status
))
378 /* Copy into the shared buffer */
379 wcsncpy(BaseStaticServerData
->CSDVersion
,
380 BaseSrvCSDString
.Buffer
,
381 BaseSrvCSDString
.Length
/ sizeof(WCHAR
));
385 /* NULL-terminate to indicate nothing is there */
386 BaseStaticServerData
->CSDVersion
[0] = UNICODE_NULL
;
389 /* Cache the system information */
390 Status
= NtQuerySystemInformation(SystemBasicInformation
,
391 &BaseStaticServerData
->SysInfo
,
392 sizeof(BaseStaticServerData
->SysInfo
),
394 ASSERT(NT_SUCCESS(Status
));
396 /* FIXME: Should query the registry for these */
397 BaseStaticServerData
->DefaultSeparateVDM
= FALSE
;
398 BaseStaticServerData
->IsWowTaskReady
= FALSE
;
400 /* Allocate a security descriptor and create it */
401 BnoSd
= RtlAllocateHeap(BaseSrvHeap
, 0, 1024);
403 Status
= RtlCreateSecurityDescriptor(BnoSd
, SECURITY_DESCRIPTOR_REVISION
);
404 ASSERT(NT_SUCCESS(Status
));
406 /* Create the BNO and \Restricted DACLs */
407 Status
= CreateBaseAcls(&BnoDacl
, &BnoRestrictedDacl
);
408 ASSERT(NT_SUCCESS(Status
));
410 /* Set the BNO DACL as active for now */
411 Status
= RtlSetDaclSecurityDescriptor(BnoSd
, TRUE
, BnoDacl
, FALSE
);
412 ASSERT(NT_SUCCESS(Status
));
414 /* Create the BNO directory */
415 RtlInitUnicodeString(&BnoString
, L
"\\BaseNamedObjects");
416 InitializeObjectAttributes(&ObjectAttributes
,
418 OBJ_OPENIF
| OBJ_PERMANENT
| OBJ_CASE_INSENSITIVE
,
421 Status
= NtCreateDirectoryObject(&BaseSrvNamedObjectDirectory
,
422 DIRECTORY_ALL_ACCESS
,
424 ASSERT(NT_SUCCESS(Status
));
426 /* Check if we are session 0 */
429 /* Mark this as a session 0 directory */
430 Status
= NtSetInformationObject(BaseSrvNamedObjectDirectory
,
431 ObjectSessionInformation
,
434 ASSERT(NT_SUCCESS(Status
));
437 /* Check if LUID device maps are enabled */
438 Status
= NtQueryInformationProcess(NtCurrentProcess(),
439 ProcessLUIDDeviceMapsEnabled
,
443 ASSERT(NT_SUCCESS(Status
));
444 BaseStaticServerData
->LUIDDeviceMapsEnabled
= LuidEnabled
;
445 if (!BaseStaticServerData
->LUIDDeviceMapsEnabled
)
447 /* Make Global point back to BNO */
448 RtlInitUnicodeString(&DirectoryName
, L
"Global");
449 RtlInitUnicodeString(&SymlinkName
, L
"\\BaseNamedObjects");
450 InitializeObjectAttributes(&ObjectAttributes
,
452 OBJ_OPENIF
| OBJ_PERMANENT
| OBJ_CASE_INSENSITIVE
,
453 BaseSrvNamedObjectDirectory
,
455 Status
= NtCreateSymbolicLinkObject(&SymHandle
,
456 SYMBOLIC_LINK_ALL_ACCESS
,
459 if ((NT_SUCCESS(Status
)) && SessionId
== 0) NtClose(SymHandle
);
461 /* Make local point back to \Sessions\x\BNO */
462 RtlInitUnicodeString(&DirectoryName
, L
"Local");
463 ASSERT(SessionId
== 0);
464 InitializeObjectAttributes(&ObjectAttributes
,
466 OBJ_OPENIF
| OBJ_PERMANENT
| OBJ_CASE_INSENSITIVE
,
467 BaseSrvNamedObjectDirectory
,
469 Status
= NtCreateSymbolicLinkObject(&SymHandle
,
470 SYMBOLIC_LINK_ALL_ACCESS
,
473 if ((NT_SUCCESS(Status
)) && SessionId
== 0) NtClose(SymHandle
);
475 /* Make Session point back to BNOLINKS */
476 RtlInitUnicodeString(&DirectoryName
, L
"Session");
477 RtlInitUnicodeString(&SymlinkName
, L
"\\Sessions\\BNOLINKS");
478 InitializeObjectAttributes(&ObjectAttributes
,
480 OBJ_OPENIF
| OBJ_PERMANENT
| OBJ_CASE_INSENSITIVE
,
481 BaseSrvNamedObjectDirectory
,
483 Status
= NtCreateSymbolicLinkObject(&SymHandle
,
484 SYMBOLIC_LINK_ALL_ACCESS
,
487 if ((NT_SUCCESS(Status
)) && SessionId
== 0) NtClose(SymHandle
);
489 /* Create the BNO\Restricted directory and set the restricted DACL */
490 RtlInitUnicodeString(&DirectoryName
, L
"Restricted");
491 Status
= RtlSetDaclSecurityDescriptor(BnoSd
, TRUE
, BnoRestrictedDacl
, FALSE
);
492 ASSERT(NT_SUCCESS(Status
));
493 InitializeObjectAttributes(&ObjectAttributes
,
495 OBJ_OPENIF
| OBJ_PERMANENT
| OBJ_CASE_INSENSITIVE
,
496 BaseSrvNamedObjectDirectory
,
498 Status
= NtCreateDirectoryObject(&BaseSrvRestrictedObjectDirectory
,
499 DIRECTORY_ALL_ACCESS
,
501 ASSERT(NT_SUCCESS(Status
));
504 /* Finally, set the pointer */
505 // CsrSrvSharedStaticServerData[CSR_CONSOLE] = BaseStaticServerData;
506 LoadedServerDll
->SharedSection
= BaseStaticServerData
;
510 VOID WINAPI
BaseStaticServerThread(PVOID x
)
512 // NTSTATUS Status = STATUS_SUCCESS;
513 PPORT_MESSAGE Request
= (PPORT_MESSAGE
)x
;
514 PPORT_MESSAGE Reply
= NULL
;
515 ULONG MessageType
= 0;
517 DPRINT("BASESRV: %s called\n", __FUNCTION__
);
519 MessageType
= Request
->u2
.s2
.Type
;
520 DPRINT("BASESRV: %s received a message (Type=%d)\n",
521 __FUNCTION__
, MessageType
);
526 /* Status =*/ NtReplyPort(BaseApiPort
, Reply
);
532 CSR_SERVER_DLL_INIT(ServerDllInitialization
)
534 // NTSTATUS Status = STATUS_SUCCESS;
537 DPRINT("BASSRV: %s(%ld,...) called\n", __FUNCTION__, ArgumentCount);
539 BaseApiPort = CsrQueryApiPort ();
540 Status = CsrAddStaticServerThread(BaseStaticServerThread);
541 if (NT_SUCCESS(Status))
543 //TODO initialize the BASE server
545 return STATUS_SUCCESS;
548 /* Setup the DLL Object */
549 LoadedServerDll
->ApiBase
= BASESRV_FIRST_API_NUMBER
; // ApiNumberBase
550 LoadedServerDll
->HighestApiSupported
= BasepMaxApiNumber
; // MaxApiNumber
551 LoadedServerDll
->DispatchTable
= BaseServerApiDispatchTable
;
552 LoadedServerDll
->ValidTable
= BaseServerApiServerValidTable
;
553 LoadedServerDll
->NameTable
= BaseServerApiNameTable
;
554 LoadedServerDll
->SizeOfProcessData
= 0;
555 LoadedServerDll
->ConnectCallback
= NULL
;
556 LoadedServerDll
->DisconnectCallback
= NULL
;
557 BaseInitializeStaticServerData(LoadedServerDll
);
559 RtlInitializeCriticalSection(&BaseDefineDosDeviceCritSec
);
560 InitializeListHead(&DosDeviceHistory
);
563 return STATUS_SUCCESS
;
568 DllMain(IN HANDLE hDll
,
570 IN LPVOID lpReserved
)
572 UNREFERENCED_PARAMETER(dwReason
);
573 UNREFERENCED_PARAMETER(lpReserved
);
575 if (DLL_PROCESS_ATTACH
== dwReason
)
579 else if (DLL_PROCESS_DETACH
== dwReason
)
581 BaseCleanupDefineDosDevice();