- Make ROS buildable with GCC 4.1
[reactos.git] / reactos / dll / win32 / kernel32 / misc / dllmain.c
1 /* $Id$
2 *
3 * COPYRIGHT: See COPYING in the top level directory
4 * PROJECT: ReactOS system libraries
5 * FILE: lib/kernel32/misc/dllmain.c
6 * PURPOSE: Initialization
7 * PROGRAMMER: Ariadne ( ariadne@xs4all.nl)
8 * UPDATE HISTORY:
9 * Created 01/11/98
10 */
11
12 /* INCLUDES ******************************************************************/
13
14 #include <k32.h>
15
16 #define NDEBUG
17 #include "../include/debug.h"
18
19 /* GLOBALS *******************************************************************/
20
21 extern UNICODE_STRING SystemDirectory;
22 extern UNICODE_STRING WindowsDirectory;
23
24 HANDLE hProcessHeap = NULL;
25 HMODULE hCurrentModule = NULL;
26 HANDLE hBaseDir = NULL;
27 PPEB Peb;
28 ULONG SessionId;
29
30 static BOOL DllInitialized = FALSE;
31 static BOOL ConsoleInitialized = FALSE;
32
33 BOOL STDCALL
34 DllMain(HANDLE hInst,
35 DWORD dwReason,
36 LPVOID lpReserved);
37
38 /* Critical section for various kernel32 data structures */
39 RTL_CRITICAL_SECTION DllLock;
40 RTL_CRITICAL_SECTION ConsoleLock;
41
42 extern BOOL WINAPI DefaultConsoleCtrlHandler(DWORD Event);
43 extern __declspec(noreturn) VOID CALLBACK ConsoleControlDispatcher(DWORD CodeAndFlag);
44
45 extern BOOL FASTCALL NlsInit();
46 extern VOID FASTCALL NlsUninit();
47 BOOLEAN InWindows = FALSE;
48
49 HANDLE
50 STDCALL
51 DuplicateConsoleHandle(HANDLE hConsole,
52 DWORD dwDesiredAccess,
53 BOOL bInheritHandle,
54 DWORD dwOptions);
55
56 #define WIN_OBJ_DIR L"\\Windows"
57 #define SESSION_DIR L"\\Sessions"
58
59 /* FUNCTIONS *****************************************************************/
60
61 NTSTATUS
62 WINAPI
63 OpenBaseDirectory(PHANDLE DirHandle)
64 {
65 OBJECT_ATTRIBUTES ObjectAttributes;
66 UNICODE_STRING Name = RTL_CONSTANT_STRING(L"\\BaseNamedObjects");
67 NTSTATUS Status;
68
69 InitializeObjectAttributes(&ObjectAttributes,
70 &Name,
71 OBJ_CASE_INSENSITIVE,
72 NULL,
73 NULL);
74
75 Status = NtOpenDirectoryObject(DirHandle,
76 DIRECTORY_ALL_ACCESS &
77 ~(DELETE | WRITE_DAC | WRITE_OWNER),
78 &ObjectAttributes);
79 if (!NT_SUCCESS(Status))
80 {
81 /* FIXME: It's not our job to create the BNO directory, csr does it */
82 Status = NtCreateDirectoryObject(DirHandle,
83 DIRECTORY_ALL_ACCESS,
84 &ObjectAttributes);
85 if (!NT_SUCCESS(Status))
86 {
87 DPRINT1("NtCreateDirectoryObject() failed\n");
88 }
89 }
90
91 DPRINT("Opened BNO: %lx\n", *DirHandle);
92 return Status;
93 }
94
95 /*
96 * @unimplemented
97 */
98 BOOL
99 WINAPI
100 BaseQueryModuleData(IN LPSTR ModuleName,
101 IN LPSTR Unknown,
102 IN PVOID Unknown2,
103 IN PVOID Unknown3,
104 IN PVOID Unknown4)
105 {
106 DPRINT1("BaseQueryModuleData called: %s %s %x %x %x\n",
107 ModuleName,
108 Unknown,
109 Unknown2,
110 Unknown3,
111 Unknown4);
112 return FALSE;
113 }
114
115 /*
116 * @unimplemented
117 */
118 NTSTATUS
119 WINAPI
120 BaseProcessInitPostImport(VOID)
121 {
122 /* FIXME: Initialize TS pointers */
123 return STATUS_SUCCESS;
124 }
125
126 BOOL
127 STDCALL
128 BasepInitConsole(VOID)
129 {
130 CSR_API_MESSAGE Request;
131 ULONG CsrRequest;
132 NTSTATUS Status;
133 PRTL_USER_PROCESS_PARAMETERS Parameters = NtCurrentPeb()->ProcessParameters;
134
135 WCHAR lpTest[MAX_PATH];
136 GetModuleFileNameW(NULL, lpTest, MAX_PATH);
137 DPRINT("BasepInitConsole for : %S\n", lpTest);
138 DPRINT("Our current console handles are: %lx, %lx, %lx %lx\n",
139 Parameters->ConsoleHandle, Parameters->StandardInput,
140 Parameters->StandardOutput, Parameters->StandardError);
141
142 /* We have nothing to do if this isn't a console app... */
143 if (RtlImageNtHeader(GetModuleHandle(NULL))->OptionalHeader.Subsystem !=
144 IMAGE_SUBSYSTEM_WINDOWS_CUI)
145 {
146 DPRINT("Image is not a console application\n");
147 Parameters->ConsoleHandle = NULL;
148 return TRUE;
149 }
150
151 /* Assume one is needed */
152 Request.Data.AllocConsoleRequest.ConsoleNeeded = TRUE;
153
154 /* Handle the special flags given to us by BasepInitializeEnvironment */
155 if (Parameters->ConsoleHandle == HANDLE_DETACHED_PROCESS)
156 {
157 /* No console to create */
158 DPRINT("No console to create\n");
159 Parameters->ConsoleHandle = NULL;
160 Request.Data.AllocConsoleRequest.ConsoleNeeded = FALSE;
161 }
162 else if (Parameters->ConsoleHandle == HANDLE_CREATE_NEW_CONSOLE)
163 {
164 /* We'll get the real one soon */
165 DPRINT("Creating new console\n");
166 Parameters->ConsoleHandle = NULL;
167 }
168 else if (Parameters->ConsoleHandle == HANDLE_CREATE_NO_WINDOW)
169 {
170 /* We'll get the real one soon */
171 DPRINT1("NOT SUPPORTED: HANDLE_CREATE_NO_WINDOW\n");
172 Parameters->ConsoleHandle = NULL;
173 }
174 else
175 {
176 if (Parameters->ConsoleHandle == INVALID_HANDLE_VALUE)
177 {
178 Parameters->ConsoleHandle = 0;
179 }
180 DPRINT("Using existing console: %x\n", Parameters->ConsoleHandle);
181 }
182
183 /* Initialize Console Ctrl Handler */
184 ConsoleInitialized = TRUE;
185 RtlInitializeCriticalSection(&ConsoleLock);
186 SetConsoleCtrlHandler(DefaultConsoleCtrlHandler, TRUE);
187
188 /* Now use the proper console handle */
189 Request.Data.AllocConsoleRequest.Console = Parameters->ConsoleHandle;
190
191 /*
192 * Normally, we should be connecting to the Console CSR Server...
193 * but we don't have one yet, so we will instead simply send a create
194 * console message to the Base Server. When we finally have a Console
195 * Server, this code should be changed to send connection data instead.
196 *
197 * Also note that this connection should be made for any console app, even
198 * in the case above where -we- return.
199 */
200 CsrRequest = MAKE_CSR_API(ALLOC_CONSOLE, CSR_CONSOLE);
201 Request.Data.AllocConsoleRequest.CtrlDispatcher = ConsoleControlDispatcher;
202 Status = CsrClientCallServer(&Request,
203 NULL,
204 CsrRequest,
205 sizeof(CSR_API_MESSAGE));
206 if(!NT_SUCCESS(Status) || !NT_SUCCESS(Status = Request.Status))
207 {
208 DPRINT1("CSR Failed to give us a console\n");
209 /* We're lying here, so at least the process can load... */
210 return TRUE;
211 }
212
213 /* We got the handles, let's set them */
214 if ((Parameters->ConsoleHandle = Request.Data.AllocConsoleRequest.Console))
215 {
216 /* If we already had some, don't use the new ones */
217 if (!Parameters->StandardInput)
218 {
219 Parameters->StandardInput = Request.Data.AllocConsoleRequest.InputHandle;
220 }
221 if (!Parameters->StandardOutput)
222 {
223 Parameters->StandardOutput = Request.Data.AllocConsoleRequest.OutputHandle;
224 }
225 if (!Parameters->StandardError)
226 {
227 Parameters->StandardError = Request.Data.AllocConsoleRequest.OutputHandle;
228 }
229 }
230
231 DPRINT("Console setup: %lx, %lx, %lx, %lx\n",
232 Parameters->ConsoleHandle,
233 Parameters->StandardInput,
234 Parameters->StandardOutput,
235 Parameters->StandardError);
236 return TRUE;
237 }
238
239
240 BOOL
241 STDCALL
242 DllMain(HANDLE hDll,
243 DWORD dwReason,
244 LPVOID lpReserved)
245 {
246 NTSTATUS Status;
247 BOOLEAN IsServer;
248 ULONG Dummy;
249 ULONG DummySize = sizeof(Dummy);
250 WCHAR SessionDir[256];
251
252 DPRINT("DllMain(hInst %lx, dwReason %lu)\n",
253 hDll, dwReason);
254
255 Basep8BitStringToUnicodeString = RtlAnsiStringToUnicodeString;
256
257 /* Cache the PEB and Session ID */
258 Peb = NtCurrentPeb();
259 SessionId = Peb->SessionId;
260
261 switch (dwReason)
262 {
263 case DLL_PROCESS_ATTACH:
264
265 /* OK, yes, this is really retarded but it works for now */
266 InWindows = NtCurrentPeb()->BeingDebugged;
267
268 /*
269 * CreateProcess will run in the real kernel32 and it will write
270 * its own BaseProcessStartThunk EIP in the CONTEXT that ZwContinue
271 * will get. We'll be first called by Ldr while initializing, and we'll
272 * be wrapped in 3 layers of SEH, followed by two frames, finally
273 * followed by our CONTEXT on the stack. We'll modify the EIP in it
274 * to match the correct one (our own) and then everything works.
275 * Tested on XP and 2K3, probably doesn't work in 2K.
276 */
277 if (InWindows)
278 {
279 /*
280 * Due to yet another bug in how Windows handles .local, LDR will
281 * actually end up loading us twice. The second time will be the
282 * "official" load, at a totally different address. It will be,
283 * it will be at -that- address that all the APIs will be called.
284 * However, that address is dynamic while this one will be static,
285 * so we'll do initilization with this one. Plus, at this one,
286 * we know exactly that we're within 3 SEH layers.
287 */
288 if (hDll == (HANDLE)0x7c800000)
289 {
290 PULONG Eip;
291 Eip = (PULONG)*(PULONG)*(PULONG)NtCurrentTeb()->Tib.ExceptionList +
292 0x9 +
293 FIELD_OFFSET(CONTEXT, Eip) / sizeof(ULONG);
294 *Eip = (ULONG)BaseProcessStartThunk;
295 }
296 }
297
298 /* Don't bother us for each thread */
299 LdrDisableThreadCalloutsForDll((PVOID)hDll);
300
301 /* Setup the right Object Directory path */
302 if (!SessionId)
303 {
304 /* Use the raw path */
305 wcscpy(SessionDir, WIN_OBJ_DIR);
306 }
307 else
308 {
309 /* Use the session path */
310 swprintf(SessionDir,
311 L"%ws\\%ld%ws",
312 SESSION_DIR,
313 SessionId,
314 WIN_OBJ_DIR);
315 }
316
317 /* Connect to the base server */
318 DPRINT("Connecting to CSR...\n");
319 Status = CsrClientConnectToServer(SessionDir,
320 InWindows ? 1 : 0,
321 &Dummy,
322 &DummySize,
323 &IsServer);
324 if (!NT_SUCCESS(Status))
325 {
326 DPRINT1("Failed to connect to CSR (Status %lx)\n", Status);
327 ZwTerminateProcess(NtCurrentProcess(), Status);
328 return FALSE;
329 }
330
331 /* Check if we are running a CSR Server */
332 if (!IsServer)
333 {
334 /* Set the termination port for the thread */
335 DPRINT("Creating new thread for CSR\n");
336 CsrNewThread();
337 }
338
339 hProcessHeap = RtlGetProcessHeap();
340 hCurrentModule = hDll;
341 DPRINT("Heap: %p\n", hProcessHeap);
342
343 /*
344 * Initialize WindowsDirectory and SystemDirectory
345 */
346 DPRINT("NtSystemRoot: %S\n", SharedUserData->NtSystemRoot);
347 RtlCreateUnicodeString (&WindowsDirectory, SharedUserData->NtSystemRoot);
348 SystemDirectory.MaximumLength = WindowsDirectory.MaximumLength + 18;
349 SystemDirectory.Length = WindowsDirectory.Length + 18;
350 SystemDirectory.Buffer = RtlAllocateHeap(hProcessHeap,
351 0,
352 SystemDirectory.MaximumLength);
353 if(SystemDirectory.Buffer == NULL)
354 {
355 return FALSE;
356 }
357 wcscpy(SystemDirectory.Buffer, WindowsDirectory.Buffer);
358 wcscat(SystemDirectory.Buffer, L"\\System32");
359
360 /* Open object base directory */
361 Status = OpenBaseDirectory(&hBaseDir);
362 if (!NT_SUCCESS(Status))
363 {
364 DPRINT1("Failed to open object base directory (Status %lx)\n", Status);
365 return FALSE;
366 }
367
368 /* Initialize the DLL critical section */
369 RtlInitializeCriticalSection(&DllLock);
370
371 /* Initialize the National Language Support routines */
372 if (!NlsInit())
373 {
374 DPRINT1("NLS Init failed\n");
375 return FALSE;
376 }
377
378 /* Initialize Console Support */
379 if (!BasepInitConsole())
380 {
381 DPRINT1("Failure to set up console\n");
382 return FALSE;
383 }
384
385 /* Insert more dll attach stuff here! */
386 DllInitialized = TRUE;
387 DPRINT("Initialization complete\n");
388 break;
389
390 case DLL_PROCESS_DETACH:
391
392 DPRINT("DLL_PROCESS_DETACH\n");
393 if (DllInitialized == TRUE)
394 {
395 /* Insert more dll detach stuff here! */
396 NlsUninit();
397
398 /* Delete DLL critical section */
399 if (ConsoleInitialized == TRUE)
400 {
401 RtlDeleteCriticalSection (&ConsoleLock);
402 }
403 RtlDeleteCriticalSection (&DllLock);
404
405 /* Close object base directory */
406 NtClose(hBaseDir);
407
408 RtlFreeUnicodeString (&SystemDirectory);
409 RtlFreeUnicodeString (&WindowsDirectory);
410 }
411 break;
412
413 default:
414 break;
415 }
416
417 return TRUE;
418 }
419
420 /* EOF */