f4d1544a562f530f1493dad594fef949570f5876
[reactos.git] / reactos / lib / 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 /* Cache the PEB and Session ID */
256 Peb = NtCurrentPeb();
257 SessionId = Peb->SessionId;
258
259 switch (dwReason)
260 {
261 case DLL_PROCESS_ATTACH:
262
263 /* OK, yes, this is really retarded but it works for now */
264 InWindows = NtCurrentPeb()->BeingDebugged;
265
266 /*
267 * CreateProcess will run in the real kernel32 and it will write
268 * its own BaseProcessStartThunk EIP in the CONTEXT that ZwContinue
269 * will get. We'll be first called by Ldr while initializing, and we'll
270 * be wrapped in 3 layers of SEH, followed by two frames, finally
271 * followed by our CONTEXT on the stack. We'll modify the EIP in it
272 * to match the correct one (our own) and then everything works.
273 * Tested on XP and 2K3, probably doesn't work in 2K.
274 */
275 if (InWindows)
276 {
277 /*
278 * Due to yet another bug in how Windows handles .local, LDR will
279 * actually end up loading us twice. The second time will be the
280 * "official" load, at a totally different address. It will be,
281 * it will be at -that- address that all the APIs will be called.
282 * However, that address is dynamic while this one will be static,
283 * so we'll do initilization with this one. Plus, at this one,
284 * we know exactly that we're within 3 SEH layers.
285 */
286 if (hDll == (HANDLE)0x7c800000)
287 {
288 PULONG Eip;
289 Eip = (PULONG)*(PULONG)*(PULONG)NtCurrentTeb()->Tib.ExceptionList +
290 0x9 +
291 FIELD_OFFSET(CONTEXT, Eip) / sizeof(ULONG);
292 *Eip = (ULONG)BaseProcessStartThunk;
293 }
294 }
295
296 /* Don't bother us for each thread */
297 LdrDisableThreadCalloutsForDll((PVOID)hDll);
298
299 /* Setup the right Object Directory path */
300 if (!SessionId)
301 {
302 /* Use the raw path */
303 wcscpy(SessionDir, WIN_OBJ_DIR);
304 }
305 else
306 {
307 /* Use the session path */
308 swprintf(SessionDir,
309 L"%ws\\%ld%ws",
310 SESSION_DIR,
311 SessionId,
312 WIN_OBJ_DIR);
313 }
314
315 /* Connect to the base server */
316 DPRINT("Connecting to CSR...\n");
317 Status = CsrClientConnectToServer(SessionDir,
318 InWindows ? 1 : 0,
319 &Dummy,
320 &DummySize,
321 &IsServer);
322 if (!NT_SUCCESS(Status))
323 {
324 DPRINT1("Failed to connect to CSR (Status %lx)\n", Status);
325 ZwTerminateProcess(NtCurrentProcess(), Status);
326 return FALSE;
327 }
328
329 /* Check if we are running a CSR Server */
330 if (!IsServer)
331 {
332 /* Set the termination port for the thread */
333 DPRINT("Creating new thread for CSR\n");
334 CsrNewThread();
335 }
336
337 hProcessHeap = RtlGetProcessHeap();
338 hCurrentModule = hDll;
339 DPRINT("Heap: %p\n", hProcessHeap);
340
341 /*
342 * Initialize WindowsDirectory and SystemDirectory
343 */
344 DPRINT("NtSystemRoot: %S\n", SharedUserData->NtSystemRoot);
345 RtlCreateUnicodeString (&WindowsDirectory, SharedUserData->NtSystemRoot);
346 SystemDirectory.MaximumLength = WindowsDirectory.MaximumLength + 18;
347 SystemDirectory.Length = WindowsDirectory.Length + 18;
348 SystemDirectory.Buffer = RtlAllocateHeap(hProcessHeap,
349 0,
350 SystemDirectory.MaximumLength);
351 wcscpy(SystemDirectory.Buffer, WindowsDirectory.Buffer);
352 wcscat(SystemDirectory.Buffer, L"\\System32");
353
354 /* Open object base directory */
355 Status = OpenBaseDirectory(&hBaseDir);
356 if (!NT_SUCCESS(Status))
357 {
358 DPRINT1("Failed to open object base directory (Status %lx)\n", Status);
359 return FALSE;
360 }
361
362 /* Initialize the DLL critical section */
363 RtlInitializeCriticalSection(&DllLock);
364
365 /* Initialize the National Language Support routines */
366 if (!NlsInit())
367 {
368 DPRINT1("NLS Init failed\n");
369 return FALSE;
370 }
371
372 /* Initialize Console Support */
373 if (!BasepInitConsole())
374 {
375 DPRINT1("Failure to set up console\n");
376 return FALSE;
377 }
378
379 /* Insert more dll attach stuff here! */
380 DllInitialized = TRUE;
381 DPRINT("Initialization complete\n");
382 break;
383
384 case DLL_PROCESS_DETACH:
385
386 DPRINT("DLL_PROCESS_DETACH\n");
387 if (DllInitialized == TRUE)
388 {
389 /* Insert more dll detach stuff here! */
390 NlsUninit();
391
392 /* Delete DLL critical section */
393 if (ConsoleInitialized == TRUE)
394 {
395 RtlDeleteCriticalSection (&ConsoleLock);
396 }
397 RtlDeleteCriticalSection (&DllLock);
398
399 /* Close object base directory */
400 NtClose(hBaseDir);
401
402 RtlFreeUnicodeString (&SystemDirectory);
403 RtlFreeUnicodeString (&WindowsDirectory);
404 }
405 break;
406
407 default:
408 break;
409 }
410
411 return TRUE;
412 }
413
414 /* EOF */