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)
11 /* INCLUDES *******************************************************************/
15 // For Control Panel Applet
22 /* GLOBALS ********************************************************************/
24 RTL_CRITICAL_SECTION ConsoleLock
;
25 BOOLEAN ConsoleInitialized
= FALSE
;
26 extern HANDLE InputWaitHandle
;
28 static const PWSTR DefaultConsoleTitle
= L
"ReactOS Console";
30 /* FUNCTIONS ******************************************************************/
34 PropDialogHandler(IN LPVOID lpThreadParameter
)
36 // NOTE: lpThreadParameter corresponds to the client shared section handle.
38 NTSTATUS Status
= STATUS_SUCCESS
;
39 HMODULE hConsoleApplet
= NULL
;
40 APPLET_PROC CPlApplet
;
41 static BOOL AlreadyDisplayingProps
= FALSE
;
42 WCHAR szBuffer
[MAX_PATH
];
45 * Do not launch more than once the console property dialog applet,
46 * or (albeit less probable), if we are not initialized.
48 if (!ConsoleInitialized
|| AlreadyDisplayingProps
)
50 /* Close the associated client shared section handle if needed */
51 if (lpThreadParameter
)
52 CloseHandle((HANDLE
)lpThreadParameter
);
54 return STATUS_UNSUCCESSFUL
;
57 AlreadyDisplayingProps
= TRUE
;
59 /* Load the control applet */
60 GetSystemDirectoryW(szBuffer
, MAX_PATH
);
61 wcscat(szBuffer
, L
"\\console.dll");
62 hConsoleApplet
= LoadLibraryW(szBuffer
);
63 if (hConsoleApplet
== NULL
)
65 DPRINT1("Failed to load console.dll\n");
66 Status
= STATUS_UNSUCCESSFUL
;
70 /* Load its main function */
71 CPlApplet
= (APPLET_PROC
)GetProcAddress(hConsoleApplet
, "CPlApplet");
72 if (CPlApplet
== NULL
)
74 DPRINT1("Error: console.dll misses CPlApplet export\n");
75 Status
= STATUS_UNSUCCESSFUL
;
79 /* Initialize the applet */
80 if (CPlApplet(NULL
, CPL_INIT
, 0, 0) == FALSE
)
82 DPRINT1("Error: failed to initialize console.dll\n");
83 Status
= STATUS_UNSUCCESSFUL
;
88 if (CPlApplet(NULL
, CPL_GETCOUNT
, 0, 0) != 1)
90 DPRINT1("Error: console.dll returned unexpected CPL count\n");
91 Status
= STATUS_UNSUCCESSFUL
;
96 * Start the applet. For Windows compatibility purposes we need
97 * to pass the client shared section handle (lpThreadParameter)
98 * via the hWnd parameter of the CPlApplet function.
100 CPlApplet((HWND
)lpThreadParameter
, CPL_DBLCLK
, 0, 0);
102 /* We have finished */
103 CPlApplet(NULL
, CPL_EXIT
, 0, 0);
106 if (hConsoleApplet
) FreeLibrary(hConsoleApplet
);
107 AlreadyDisplayingProps
= FALSE
;
113 ParseShellInfo(LPCWSTR lpszShellInfo
,
116 DPRINT("ParseShellInfo is UNIMPLEMENTED\n");
123 * The "LPDWORD Length" parameters point on input to the maximum size of
124 * the buffers that can hold data (if != 0), and on output they hold the
125 * real size of the data. If "Length" are == 0 on input, then on output
126 * they receive the full size of the data.
127 * The "LPWSTR* lpTitle" parameter has a double meaning:
128 * - when "CaptureTitle" is TRUE, data is copied to the buffer pointed
129 * by the pointer (*lpTitle).
130 * - when "CaptureTitle" is FALSE, "*lpTitle" is set to the address of
134 SetUpConsoleInfo(IN BOOLEAN CaptureTitle
,
135 IN OUT LPDWORD pTitleLength
,
136 IN OUT LPWSTR
* lpTitle OPTIONAL
,
137 IN OUT LPDWORD pDesktopLength
,
138 IN OUT LPWSTR
* lpDesktop OPTIONAL
,
139 IN OUT PCONSOLE_START_INFO ConsoleStartInfo
)
141 PRTL_USER_PROCESS_PARAMETERS Parameters
= NtCurrentPeb()->ProcessParameters
;
144 /* Initialize the fields */
146 ConsoleStartInfo
->IconIndex
= 0;
147 ConsoleStartInfo
->hIcon
= NULL
;
148 ConsoleStartInfo
->hIconSm
= NULL
;
149 ConsoleStartInfo
->dwStartupFlags
= Parameters
->WindowFlags
;
150 ConsoleStartInfo
->nFont
= 0;
151 ConsoleStartInfo
->nInputBufferSize
= 0;
152 ConsoleStartInfo
->uCodePage
= GetOEMCP();
158 /* If we don't have any title, use the default one */
159 if (Parameters
->WindowTitle
.Buffer
== NULL
)
161 Title
= DefaultConsoleTitle
;
162 Length
= lstrlenW(DefaultConsoleTitle
) * sizeof(WCHAR
); // sizeof(DefaultConsoleTitle);
166 Title
= Parameters
->WindowTitle
.Buffer
;
167 Length
= Parameters
->WindowTitle
.Length
;
170 /* Retrieve the needed buffer size */
171 Length
+= sizeof(WCHAR
);
172 if (*pTitleLength
> 0) Length
= min(Length
, *pTitleLength
);
173 *pTitleLength
= Length
;
175 /* Capture the data if needed, or, return a pointer to it */
179 * Length is always >= sizeof(WCHAR). Copy everything but the
180 * possible trailing NULL character, and then NULL-terminate.
182 Length
-= sizeof(WCHAR
);
183 RtlCopyMemory(*lpTitle
, Title
, Length
);
184 (*lpTitle
)[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
196 if (lpDesktop
&& Parameters
->DesktopInfo
.Buffer
&& *Parameters
->DesktopInfo
.Buffer
)
198 /* Retrieve the needed buffer size */
199 Length
= Parameters
->DesktopInfo
.Length
+ sizeof(WCHAR
);
200 if (*pDesktopLength
> 0) Length
= min(Length
, *pDesktopLength
);
201 *pDesktopLength
= Length
;
203 /* Return a pointer to the data */
204 *lpDesktop
= Parameters
->DesktopInfo
.Buffer
;
209 if (lpDesktop
) *lpDesktop
= NULL
;
212 if (Parameters
->WindowFlags
& STARTF_USEFILLATTRIBUTE
)
214 ConsoleStartInfo
->wFillAttribute
= (WORD
)Parameters
->FillAttribute
;
216 if (Parameters
->WindowFlags
& STARTF_USECOUNTCHARS
)
218 ConsoleStartInfo
->dwScreenBufferSize
.X
= (SHORT
)Parameters
->CountCharsX
;
219 ConsoleStartInfo
->dwScreenBufferSize
.Y
= (SHORT
)Parameters
->CountCharsY
;
221 if (Parameters
->WindowFlags
& STARTF_USESHOWWINDOW
)
223 ConsoleStartInfo
->wShowWindow
= (WORD
)Parameters
->ShowWindowFlags
;
225 if (Parameters
->WindowFlags
& STARTF_USEPOSITION
)
227 ConsoleStartInfo
->dwWindowOrigin
.X
= (SHORT
)Parameters
->StartingX
;
228 ConsoleStartInfo
->dwWindowOrigin
.Y
= (SHORT
)Parameters
->StartingY
;
230 if (Parameters
->WindowFlags
& STARTF_USESIZE
)
232 ConsoleStartInfo
->dwWindowSize
.X
= (SHORT
)Parameters
->CountX
;
233 ConsoleStartInfo
->dwWindowSize
.Y
= (SHORT
)Parameters
->CountY
;
236 /* Get shell information (ShellInfo.Buffer is NULL-terminated) */
237 if (Parameters
->ShellInfo
.Buffer
!= NULL
)
239 ConsoleStartInfo
->IconIndex
= ParseShellInfo(Parameters
->ShellInfo
.Buffer
, L
"dde.");
241 if ((Parameters
->WindowFlags
& STARTF_USEHOTKEY
) == 0)
242 ConsoleStartInfo
->dwHotKey
= ParseShellInfo(Parameters
->ShellInfo
.Buffer
, L
"hotkey.");
244 ConsoleStartInfo
->dwHotKey
= HandleToUlong(Parameters
->StandardInput
);
250 SetUpHandles(IN PCONSOLE_START_INFO ConsoleStartInfo
)
252 PRTL_USER_PROCESS_PARAMETERS Parameters
= NtCurrentPeb()->ProcessParameters
;
254 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USEHOTKEY
)
256 Parameters
->WindowFlags
&= ~STARTF_USEHOTKEY
;
258 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_SHELLPRIVATE
)
260 Parameters
->WindowFlags
&= ~STARTF_SHELLPRIVATE
;
263 /* We got the handles, let's set them */
264 Parameters
->ConsoleHandle
= ConsoleStartInfo
->ConsoleHandle
;
266 if ((ConsoleStartInfo
->dwStartupFlags
& STARTF_USESTDHANDLES
) == 0)
268 Parameters
->StandardInput
= ConsoleStartInfo
->InputHandle
;
269 Parameters
->StandardOutput
= ConsoleStartInfo
->OutputHandle
;
270 Parameters
->StandardError
= ConsoleStartInfo
->ErrorHandle
;
278 PIMAGE_NT_HEADERS ImageNtHeader
= RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress
);
279 return (ImageNtHeader
&& (ImageNtHeader
->OptionalHeader
.Subsystem
==
280 IMAGE_SUBSYSTEM_WINDOWS_CUI
));
285 ConnectConsole(IN PWSTR SessionDir
,
286 IN PCONSRV_API_CONNECTINFO ConnectInfo
,
287 OUT PBOOLEAN InServerProcess
)
290 ULONG ConnectInfoSize
= sizeof(*ConnectInfo
);
294 /* Connect to the Console Server */
295 DPRINT("Connecting to the Console Server...\n");
296 Status
= CsrClientConnectToServer(SessionDir
,
297 CONSRV_SERVERDLL_INDEX
,
301 if (!NT_SUCCESS(Status
))
303 DPRINT1("Failed to connect to the Console Server (Status %lx)\n", Status
);
307 /* Nothing to do for server-to-server */
308 if (*InServerProcess
) return TRUE
;
310 /* Nothing to do if this is not a console app */
311 if (!ConnectInfo
->IsConsoleApp
) return TRUE
;
313 /* Wait for the connection to finish */
314 // Is ConnectInfo->ConsoleStartInfo.InitEvents aligned on handle boundary ????
315 Status
= NtWaitForMultipleObjects(MAX_INIT_EVENTS
,
316 ConnectInfo
->ConsoleStartInfo
.InitEvents
,
317 WaitAny
, FALSE
, NULL
);
318 if (!NT_SUCCESS(Status
))
320 BaseSetLastNTError(Status
);
324 NtClose(ConnectInfo
->ConsoleStartInfo
.InitEvents
[INIT_SUCCESS
]);
325 NtClose(ConnectInfo
->ConsoleStartInfo
.InitEvents
[INIT_FAILURE
]);
326 if (Status
!= INIT_SUCCESS
)
328 NtCurrentPeb()->ProcessParameters
->ConsoleHandle
= NULL
;
338 ConDllInitialize(IN ULONG Reason
,
342 PRTL_USER_PROCESS_PARAMETERS Parameters
= NtCurrentPeb()->ProcessParameters
;
343 BOOLEAN InServerProcess
= FALSE
;
344 CONSRV_API_CONNECTINFO ConnectInfo
;
347 if (Reason
!= DLL_PROCESS_ATTACH
)
349 if ((Reason
== DLL_THREAD_ATTACH
) && IsConsoleApp())
351 /* Sets the current console locale for the new thread */
354 else if (Reason
== DLL_PROCESS_DETACH
)
356 /* Free our resources */
357 if (ConsoleInitialized
!= FALSE
)
359 ConsoleInitialized
= FALSE
;
360 RtlDeleteCriticalSection(&ConsoleLock
);
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
);
375 /* Initialize our global console DLL lock */
376 Status
= RtlInitializeCriticalSection(&ConsoleLock
);
377 if (!NT_SUCCESS(Status
)) return FALSE
;
378 ConsoleInitialized
= TRUE
;
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();
385 /* Do nothing if this is not a console app... */
386 if (!ConnectInfo
.IsConsoleApp
)
388 DPRINT("Image is not a console application\n");
392 * Handle the special flags given to us by BasePushProcessParameters.
394 if (Parameters
->ConsoleHandle
== HANDLE_DETACHED_PROCESS
)
396 /* No console to create */
397 DPRINT("No console to create\n");
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.
403 Parameters
->ConsoleHandle
= NULL
; // Do not inherit the parent's console.
404 ConnectInfo
.IsConsoleApp
= FALSE
; // Do not create any console.
406 else if (Parameters
->ConsoleHandle
== HANDLE_CREATE_NEW_CONSOLE
)
408 /* We'll get the real one soon */
409 DPRINT("Creating a new separate console\n");
411 * The new process has a new console, instead of inheriting
412 * its parent's console.
414 Parameters
->ConsoleHandle
= NULL
; // Do not inherit the parent's console.
416 else if (Parameters
->ConsoleHandle
== HANDLE_CREATE_NO_WINDOW
)
418 /* We'll get the real one soon */
419 DPRINT("Creating a new invisible console\n");
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.
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.
430 DPRINT("Using existing console: 0x%p\n", Parameters
->ConsoleHandle
);
433 /* Do nothing if this is not a console app... */
434 if (!ConnectInfo
.IsConsoleApp
)
436 /* Do not inherit the parent's console if we are not a console app */
437 Parameters
->ConsoleHandle
= NULL
;
440 /* Now use the proper console handle */
441 ConnectInfo
.ConsoleStartInfo
.ConsoleHandle
= Parameters
->ConsoleHandle
;
443 /* Initialize the console dispatchers */
444 ConnectInfo
.CtrlRoutine
= ConsoleControlDispatcher
;
445 ConnectInfo
.PropRoutine
= PropDialogHandler
;
446 // ConnectInfo.ImeRoutine = ImeRoutine;
448 /* Set up the console properties */
449 if (ConnectInfo
.IsConsoleApp
&& Parameters
->ConsoleHandle
== NULL
)
452 * We can set up the console properties only if we create a new one
453 * (we do not inherit it from our parent).
456 LPWSTR ConsoleTitle
= ConnectInfo
.ConsoleTitle
;
458 ConnectInfo
.TitleLength
= sizeof(ConnectInfo
.ConsoleTitle
);
459 ConnectInfo
.DesktopLength
= 0; // SetUpConsoleInfo will give us the real length.
461 SetUpConsoleInfo(TRUE
,
462 &ConnectInfo
.TitleLength
,
464 &ConnectInfo
.DesktopLength
,
465 &ConnectInfo
.Desktop
,
466 &ConnectInfo
.ConsoleStartInfo
);
467 DPRINT("ConsoleTitle = '%S' - Desktop = '%S'\n",
468 ConsoleTitle
, ConnectInfo
.Desktop
);
472 ConnectInfo
.TitleLength
= 0;
473 ConnectInfo
.DesktopLength
= 0;
476 /* Initialize the Input EXE name */
477 if (ConnectInfo
.IsConsoleApp
)
479 LPWSTR CurDir
= ConnectInfo
.CurDir
;
480 LPWSTR AppName
= ConnectInfo
.AppName
;
484 ConnectInfo
.CurDirLength
= sizeof(ConnectInfo
.CurDir
);
485 ConnectInfo
.AppNameLength
= sizeof(ConnectInfo
.AppName
);
488 &ConnectInfo
.CurDirLength
,
490 &ConnectInfo
.AppNameLength
,
492 DPRINT("CurDir = '%S' - AppName = '%S'\n",
497 ConnectInfo
.CurDirLength
= 0;
498 ConnectInfo
.AppNameLength
= 0;
502 * Initialize Console Ctrl Handling, that needs to be supported by
503 * all applications, especially because it is used at shutdown.
505 InitializeCtrlHandling();
507 /* Connect to the Console Server */
508 if (!ConnectConsole(SessionDir
,
512 // DPRINT1("Failed to connect to the Console Server (Status %lx)\n", Status);
516 /* If we are not doing server-to-server init and if this is a console app... */
517 if (!InServerProcess
&& ConnectInfo
.IsConsoleApp
)
519 /* ... set the handles that we got */
520 if (Parameters
->ConsoleHandle
== NULL
)
521 SetUpHandles(&ConnectInfo
.ConsoleStartInfo
);
523 InputWaitHandle
= ConnectInfo
.ConsoleStartInfo
.InputWaitHandle
;
525 /* Sets the current console locale for this thread */
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
);