Revert r66580 and r66579.
[reactos.git] / reactos / win32ss / base / kernel32 / client / console / init.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: dll/win32/kernel32/client/console/init.c
5 * PURPOSE: Console API Client Initialization
6 * PROGRAMMERS: Alex Ionescu (alex.ionescu@reactos.org)
7 * Aleksey Bragin (aleksey@reactos.org)
8 * Hermes Belusca-Maito (hermes.belusca@sfr.fr)
9 */
10
11 /* INCLUDES *******************************************************************/
12
13 #include <k32.h>
14
15 // For Control Panel Applet
16 #include <cpl.h>
17
18 #define NDEBUG
19 #include <debug.h>
20
21
22 /* GLOBALS ********************************************************************/
23
24 RTL_CRITICAL_SECTION ConsoleLock;
25 BOOLEAN ConsoleInitialized = FALSE;
26
27 extern HANDLE InputWaitHandle;
28
29 static HMODULE ConsoleApplet = NULL;
30 static BOOL AlreadyDisplayingProps = FALSE;
31
32 static const PWSTR DefaultConsoleTitle = L"ReactOS Console";
33
34
35 /* FUNCTIONS ******************************************************************/
36
37 DWORD
38 WINAPI
39 PropDialogHandler(IN LPVOID lpThreadParameter)
40 {
41 // NOTE: lpThreadParameter corresponds to the client shared section handle.
42
43 NTSTATUS Status = STATUS_SUCCESS;
44 APPLET_PROC CPLFunc;
45
46 /*
47 * Do not launch more than once the console property dialog applet,
48 * or (albeit less probable), if we are not initialized.
49 */
50 if (!ConsoleInitialized || AlreadyDisplayingProps)
51 {
52 /* Close the associated client shared section handle if needed */
53 if (lpThreadParameter)
54 {
55 CloseHandle((HANDLE)lpThreadParameter);
56 }
57 return STATUS_UNSUCCESSFUL;
58 }
59
60 AlreadyDisplayingProps = TRUE;
61
62 /* Load the Control Applet if needed */
63 if (ConsoleApplet == NULL)
64 {
65 WCHAR szBuffer[MAX_PATH];
66
67 GetSystemDirectoryW(szBuffer, MAX_PATH);
68 wcscat(szBuffer, L"\\console.dll");
69 ConsoleApplet = LoadLibraryW(szBuffer);
70 if (ConsoleApplet == NULL)
71 {
72 DPRINT1("Failed to load console.dll\n");
73 Status = STATUS_UNSUCCESSFUL;
74 goto Quit;
75 }
76 }
77
78 /* Load its main function */
79 CPLFunc = (APPLET_PROC)GetProcAddress(ConsoleApplet, "CPlApplet");
80 if (CPLFunc == NULL)
81 {
82 DPRINT1("Error: Console.dll misses CPlApplet export\n");
83 Status = STATUS_UNSUCCESSFUL;
84 goto Quit;
85 }
86
87 if (CPLFunc(NULL, CPL_INIT, 0, 0) == FALSE)
88 {
89 DPRINT1("Error: failed to initialize console.dll\n");
90 Status = STATUS_UNSUCCESSFUL;
91 goto Quit;
92 }
93
94 if (CPLFunc(NULL, CPL_GETCOUNT, 0, 0) != 1)
95 {
96 DPRINT1("Error: console.dll returned unexpected CPL count\n");
97 Status = STATUS_UNSUCCESSFUL;
98 goto Quit;
99 }
100
101 CPLFunc(NULL, CPL_DBLCLK, (LPARAM)lpThreadParameter, 0);
102 CPLFunc(NULL, CPL_EXIT , 0, 0);
103
104 Quit:
105 AlreadyDisplayingProps = FALSE;
106 return Status;
107 }
108
109
110 static INT
111 ParseShellInfo(LPCWSTR lpszShellInfo,
112 LPCWSTR lpszKeyword)
113 {
114 DPRINT("ParseShellInfo is UNIMPLEMENTED\n");
115 return 0;
116 }
117
118
119 /*
120 * NOTE:
121 * The "LPDWORD Length" parameters point on input to the maximum size of
122 * the buffers that can hold data (if != 0), and on output they hold the
123 * real size of the data. If "Length" are == 0 on input, then on output
124 * they receive the full size of the data.
125 * The "LPWSTR* lpTitle" parameter has a double meaning:
126 * - when "CaptureTitle" is TRUE, data is copied to the buffer pointed
127 * by the pointer (*lpTitle).
128 * - when "CaptureTitle" is FALSE, "*lpTitle" is set to the address of
129 * the source data.
130 */
131 VOID
132 SetUpConsoleInfo(IN BOOLEAN CaptureTitle,
133 IN OUT LPDWORD pTitleLength,
134 IN OUT LPWSTR* lpTitle OPTIONAL,
135 IN OUT LPDWORD pDesktopLength,
136 IN OUT LPWSTR* lpDesktop OPTIONAL,
137 IN OUT PCONSOLE_START_INFO ConsoleStartInfo)
138 {
139 PRTL_USER_PROCESS_PARAMETERS Parameters = NtCurrentPeb()->ProcessParameters;
140 DWORD Length;
141
142 /* Initialize the fields */
143
144 ConsoleStartInfo->IconIndex = 0;
145 ConsoleStartInfo->hIcon = NULL;
146 ConsoleStartInfo->hIconSm = NULL;
147 ConsoleStartInfo->dwStartupFlags = Parameters->WindowFlags;
148 ConsoleStartInfo->nFont = 0;
149 ConsoleStartInfo->nInputBufferSize = 0;
150 ConsoleStartInfo->uCodePage = GetOEMCP();
151
152 if (lpTitle)
153 {
154 LPWSTR Title;
155
156 /* If we don't have any title, use the default one */
157 if (Parameters->WindowTitle.Buffer == NULL)
158 {
159 Title = DefaultConsoleTitle;
160 Length = lstrlenW(DefaultConsoleTitle) * sizeof(WCHAR); // sizeof(DefaultConsoleTitle);
161 }
162 else
163 {
164 Title = Parameters->WindowTitle.Buffer;
165 Length = Parameters->WindowTitle.Length;
166 }
167
168 /* Retrieve the needed buffer size */
169 Length += sizeof(WCHAR);
170 if (*pTitleLength > 0) Length = min(Length, *pTitleLength);
171 *pTitleLength = Length;
172
173 /* Capture the data if needed, or, return a pointer to it */
174 if (CaptureTitle)
175 {
176 /*
177 * Length is always >= sizeof(WCHAR). Copy everything but the
178 * possible trailing NULL character, and then NULL-terminate.
179 */
180 Length -= sizeof(WCHAR);
181 RtlCopyMemory(*lpTitle, Title, Length);
182 (*lpTitle)[Length / sizeof(WCHAR)] = UNICODE_NULL;
183 }
184 else
185 {
186 *lpTitle = Title;
187 }
188 }
189 else
190 {
191 *pTitleLength = 0;
192 }
193
194 if (lpDesktop && Parameters->DesktopInfo.Buffer && *Parameters->DesktopInfo.Buffer)
195 {
196 /* Retrieve the needed buffer size */
197 Length = Parameters->DesktopInfo.Length + sizeof(WCHAR);
198 if (*pDesktopLength > 0) Length = min(Length, *pDesktopLength);
199 *pDesktopLength = Length;
200
201 /* Return a pointer to the data */
202 *lpDesktop = Parameters->DesktopInfo.Buffer;
203 }
204 else
205 {
206 *pDesktopLength = 0;
207 if (lpDesktop) *lpDesktop = NULL;
208 }
209
210 if (Parameters->WindowFlags & STARTF_USEFILLATTRIBUTE)
211 {
212 ConsoleStartInfo->wFillAttribute = (WORD)Parameters->FillAttribute;
213 }
214 if (Parameters->WindowFlags & STARTF_USECOUNTCHARS)
215 {
216 ConsoleStartInfo->dwScreenBufferSize.X = (SHORT)Parameters->CountCharsX;
217 ConsoleStartInfo->dwScreenBufferSize.Y = (SHORT)Parameters->CountCharsY;
218 }
219 if (Parameters->WindowFlags & STARTF_USESHOWWINDOW)
220 {
221 ConsoleStartInfo->wShowWindow = (WORD)Parameters->ShowWindowFlags;
222 }
223 if (Parameters->WindowFlags & STARTF_USEPOSITION)
224 {
225 ConsoleStartInfo->dwWindowOrigin.X = (SHORT)Parameters->StartingX;
226 ConsoleStartInfo->dwWindowOrigin.Y = (SHORT)Parameters->StartingY;
227 }
228 if (Parameters->WindowFlags & STARTF_USESIZE)
229 {
230 ConsoleStartInfo->dwWindowSize.X = (SHORT)Parameters->CountX;
231 ConsoleStartInfo->dwWindowSize.Y = (SHORT)Parameters->CountY;
232 }
233
234 /* Get shell information (ShellInfo.Buffer is NULL-terminated) */
235 if (Parameters->ShellInfo.Buffer != NULL)
236 {
237 ConsoleStartInfo->IconIndex = ParseShellInfo(Parameters->ShellInfo.Buffer, L"dde.");
238
239 if ((Parameters->WindowFlags & STARTF_USEHOTKEY) == 0)
240 ConsoleStartInfo->dwHotKey = ParseShellInfo(Parameters->ShellInfo.Buffer, L"hotkey.");
241 else
242 ConsoleStartInfo->dwHotKey = HandleToUlong(Parameters->StandardInput);
243 }
244 }
245
246
247 VOID
248 SetUpHandles(IN PCONSOLE_START_INFO ConsoleStartInfo)
249 {
250 PRTL_USER_PROCESS_PARAMETERS Parameters = NtCurrentPeb()->ProcessParameters;
251
252 if (ConsoleStartInfo->dwStartupFlags & STARTF_USEHOTKEY)
253 {
254 Parameters->WindowFlags &= ~STARTF_USEHOTKEY;
255 }
256 if (ConsoleStartInfo->dwStartupFlags & STARTF_SHELLPRIVATE)
257 {
258 Parameters->WindowFlags &= ~STARTF_SHELLPRIVATE;
259 }
260
261 /* We got the handles, let's set them */
262 Parameters->ConsoleHandle = ConsoleStartInfo->ConsoleHandle;
263
264 if ((ConsoleStartInfo->dwStartupFlags & STARTF_USESTDHANDLES) == 0)
265 {
266 Parameters->StandardInput = ConsoleStartInfo->InputHandle;
267 Parameters->StandardOutput = ConsoleStartInfo->OutputHandle;
268 Parameters->StandardError = ConsoleStartInfo->ErrorHandle;
269 }
270 }
271
272
273 static BOOLEAN
274 IsConsoleApp(VOID)
275 {
276 PIMAGE_NT_HEADERS ImageNtHeader = RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress);
277 return (ImageNtHeader && (ImageNtHeader->OptionalHeader.Subsystem ==
278 IMAGE_SUBSYSTEM_WINDOWS_CUI));
279 }
280
281
282 static BOOLEAN
283 ConnectConsole(IN PWSTR SessionDir,
284 IN PCONSRV_API_CONNECTINFO ConnectInfo,
285 OUT PBOOLEAN InServerProcess)
286 {
287 NTSTATUS Status;
288 ULONG ConnectInfoSize = sizeof(*ConnectInfo);
289
290 ASSERT(SessionDir);
291
292 /* Connect to the Console Server */
293 DPRINT("Connecting to the Console Server...\n");
294 Status = CsrClientConnectToServer(SessionDir,
295 CONSRV_SERVERDLL_INDEX,
296 ConnectInfo,
297 &ConnectInfoSize,
298 InServerProcess);
299 if (!NT_SUCCESS(Status))
300 {
301 DPRINT1("Failed to connect to the Console Server (Status %lx)\n", Status);
302 return FALSE;
303 }
304
305 /* Nothing to do for server-to-server */
306 if (*InServerProcess) return TRUE;
307
308 /* Nothing to do if this is not a console app */
309 if (!ConnectInfo->IsConsoleApp) return TRUE;
310
311 /* Wait for the connection to finish */
312 // Is ConnectInfo->ConsoleStartInfo.InitEvents aligned on handle boundary ????
313 Status = NtWaitForMultipleObjects(MAX_INIT_EVENTS,
314 ConnectInfo->ConsoleStartInfo.InitEvents,
315 WaitAny, FALSE, NULL);
316 if (!NT_SUCCESS(Status))
317 {
318 BaseSetLastNTError(Status);
319 return FALSE;
320 }
321
322 NtClose(ConnectInfo->ConsoleStartInfo.InitEvents[INIT_SUCCESS]);
323 NtClose(ConnectInfo->ConsoleStartInfo.InitEvents[INIT_FAILURE]);
324 if (Status != INIT_SUCCESS)
325 {
326 NtCurrentPeb()->ProcessParameters->ConsoleHandle = NULL;
327 return FALSE;
328 }
329
330 return TRUE;
331 }
332
333
334 BOOLEAN
335 WINAPI
336 ConDllInitialize(IN ULONG Reason,
337 IN PWSTR SessionDir)
338 {
339 NTSTATUS Status;
340 PRTL_USER_PROCESS_PARAMETERS Parameters = NtCurrentPeb()->ProcessParameters;
341 BOOLEAN InServerProcess = FALSE;
342 CONSRV_API_CONNECTINFO ConnectInfo;
343 LCID lcid;
344
345 if (Reason != DLL_PROCESS_ATTACH)
346 {
347 if ((Reason == DLL_THREAD_ATTACH) && IsConsoleApp())
348 {
349 /* Sets the current console locale for the new thread */
350 SetTEBLangID(lcid);
351 }
352 else if (Reason == DLL_PROCESS_DETACH)
353 {
354 /* Free our resources */
355 if (ConsoleInitialized == TRUE)
356 {
357 if (ConsoleApplet) FreeLibrary(ConsoleApplet);
358
359 ConsoleInitialized = FALSE;
360 RtlDeleteCriticalSection(&ConsoleLock);
361 }
362 }
363
364 return TRUE;
365 }
366
367 DPRINT("ConDllInitialize for: %wZ\n"
368 "Our current console handles are: 0x%p, 0x%p, 0x%p 0x%p\n",
369 &Parameters->ImagePathName,
370 Parameters->ConsoleHandle,
371 Parameters->StandardInput,
372 Parameters->StandardOutput,
373 Parameters->StandardError);
374
375 /* Initialize our global console DLL lock */
376 Status = RtlInitializeCriticalSection(&ConsoleLock);
377 if (!NT_SUCCESS(Status)) return FALSE;
378 ConsoleInitialized = TRUE;
379
380 /* Show by default the console window when applicable */
381 ConnectInfo.IsWindowVisible = TRUE;
382 /* If this is a console app, a console will be created/opened */
383 ConnectInfo.IsConsoleApp = IsConsoleApp();
384
385 /* Do nothing if this is not a console app... */
386 if (!ConnectInfo.IsConsoleApp)
387 {
388 DPRINT("Image is not a console application\n");
389 }
390
391 /*
392 * Handle the special flags given to us by BasePushProcessParameters.
393 */
394 if (Parameters->ConsoleHandle == HANDLE_DETACHED_PROCESS)
395 {
396 /* No console to create */
397 DPRINT("No console to create\n");
398 /*
399 * The new process does not inherit its parent's console and cannot
400 * attach to the console of its parent. The new process can call the
401 * AllocConsole function at a later time to create a console.
402 */
403 Parameters->ConsoleHandle = NULL; // Do not inherit the parent's console.
404 ConnectInfo.IsConsoleApp = FALSE; // Do not create any console.
405 }
406 else if (Parameters->ConsoleHandle == HANDLE_CREATE_NEW_CONSOLE)
407 {
408 /* We'll get the real one soon */
409 DPRINT("Creating a new separate console\n");
410 /*
411 * The new process has a new console, instead of inheriting
412 * its parent's console.
413 */
414 Parameters->ConsoleHandle = NULL; // Do not inherit the parent's console.
415 }
416 else if (Parameters->ConsoleHandle == HANDLE_CREATE_NO_WINDOW)
417 {
418 /* We'll get the real one soon */
419 DPRINT("Creating a new invisible console\n");
420 /*
421 * The process is a console application that is being run
422 * without a console window. Therefore, the console handle
423 * for the application is not set.
424 */
425 Parameters->ConsoleHandle = NULL; // Do not inherit the parent's console.
426 ConnectInfo.IsWindowVisible = FALSE; // A console is created but is not shown to the user.
427 }
428 else
429 {
430 DPRINT("Using existing console: 0x%p\n", Parameters->ConsoleHandle);
431 }
432
433 /* Do nothing if this is not a console app... */
434 if (!ConnectInfo.IsConsoleApp)
435 {
436 /* Do not inherit the parent's console if we are not a console app */
437 Parameters->ConsoleHandle = NULL;
438 }
439
440 /* Now use the proper console handle */
441 ConnectInfo.ConsoleStartInfo.ConsoleHandle = Parameters->ConsoleHandle;
442
443 /* Initialize the console dispatchers */
444 ConnectInfo.CtrlRoutine = ConsoleControlDispatcher;
445 ConnectInfo.PropRoutine = PropDialogHandler;
446 // ConnectInfo.ImeRoutine = ImeRoutine;
447
448 /* Set up the console properties */
449 if (ConnectInfo.IsConsoleApp && Parameters->ConsoleHandle == NULL)
450 {
451 /*
452 * We can set up the console properties only if we create a new one
453 * (we do not inherit it from our parent).
454 */
455
456 LPWSTR ConsoleTitle = ConnectInfo.ConsoleTitle;
457
458 ConnectInfo.TitleLength = sizeof(ConnectInfo.ConsoleTitle);
459 ConnectInfo.DesktopLength = 0; // SetUpConsoleInfo will give us the real length.
460
461 SetUpConsoleInfo(TRUE,
462 &ConnectInfo.TitleLength,
463 &ConsoleTitle,
464 &ConnectInfo.DesktopLength,
465 &ConnectInfo.Desktop,
466 &ConnectInfo.ConsoleStartInfo);
467 DPRINT("ConsoleTitle = '%S' - Desktop = '%S'\n",
468 ConsoleTitle, ConnectInfo.Desktop);
469 }
470 else
471 {
472 ConnectInfo.TitleLength = 0;
473 ConnectInfo.DesktopLength = 0;
474 }
475
476 /* Initialize the Input EXE name */
477 if (ConnectInfo.IsConsoleApp)
478 {
479 LPWSTR CurDir = ConnectInfo.CurDir;
480 LPWSTR AppName = ConnectInfo.AppName;
481
482 InitExeName();
483
484 ConnectInfo.CurDirLength = sizeof(ConnectInfo.CurDir);
485 ConnectInfo.AppNameLength = sizeof(ConnectInfo.AppName);
486
487 SetUpAppName(TRUE,
488 &ConnectInfo.CurDirLength,
489 &CurDir,
490 &ConnectInfo.AppNameLength,
491 &AppName);
492 DPRINT("CurDir = '%S' - AppName = '%S'\n",
493 CurDir, AppName);
494 }
495 else
496 {
497 ConnectInfo.CurDirLength = 0;
498 ConnectInfo.AppNameLength = 0;
499 }
500
501 /*
502 * Initialize Console Ctrl Handling, that needs to be supported by
503 * all applications, especially because it is used at shutdown.
504 */
505 InitializeCtrlHandling();
506
507 /* Connect to the Console Server */
508 if (!ConnectConsole(SessionDir,
509 &ConnectInfo,
510 &InServerProcess))
511 {
512 // DPRINT1("Failed to connect to the Console Server (Status %lx)\n", Status);
513 return FALSE;
514 }
515
516 /* If we are not doing server-to-server init and if this is a console app... */
517 if (!InServerProcess && ConnectInfo.IsConsoleApp)
518 {
519 /* ... set the handles that we got */
520 if (Parameters->ConsoleHandle == NULL)
521 SetUpHandles(&ConnectInfo.ConsoleStartInfo);
522
523 InputWaitHandle = ConnectInfo.ConsoleStartInfo.InputWaitHandle;
524
525 /* Sets the current console locale for this thread */
526 SetTEBLangID(lcid);
527 }
528
529 DPRINT("Console setup: 0x%p, 0x%p, 0x%p, 0x%p\n",
530 Parameters->ConsoleHandle,
531 Parameters->StandardInput,
532 Parameters->StandardOutput,
533 Parameters->StandardError);
534
535 return TRUE;
536 }
537
538 /* EOF */