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
;
27 extern HANDLE InputWaitHandle
;
29 static HMODULE ConsoleApplet
= NULL
;
30 static BOOL AlreadyDisplayingProps
= FALSE
;
32 static const PWSTR DefaultConsoleTitle
= L
"ReactOS Console";
35 /* FUNCTIONS ******************************************************************/
39 PropDialogHandler(IN LPVOID lpThreadParameter
)
41 // NOTE: lpThreadParameter corresponds to the client shared section handle.
43 NTSTATUS Status
= STATUS_SUCCESS
;
47 * Do not launch more than once the console property dialog applet,
48 * or (albeit less probable), if we are not initialized.
50 if (!ConsoleInitialized
|| AlreadyDisplayingProps
)
52 /* Close the associated client shared section handle if needed */
53 if (lpThreadParameter
)
55 CloseHandle((HANDLE
)lpThreadParameter
);
57 return STATUS_UNSUCCESSFUL
;
60 AlreadyDisplayingProps
= TRUE
;
62 /* Load the Control Applet if needed */
63 if (ConsoleApplet
== NULL
)
65 WCHAR szBuffer
[MAX_PATH
];
67 GetSystemDirectoryW(szBuffer
, MAX_PATH
);
68 wcscat(szBuffer
, L
"\\console.dll");
69 ConsoleApplet
= LoadLibraryW(szBuffer
);
70 if (ConsoleApplet
== NULL
)
72 DPRINT1("Failed to load console.dll\n");
73 Status
= STATUS_UNSUCCESSFUL
;
78 /* Load its main function */
79 CPLFunc
= (APPLET_PROC
)GetProcAddress(ConsoleApplet
, "CPlApplet");
82 DPRINT1("Error: Console.dll misses CPlApplet export\n");
83 Status
= STATUS_UNSUCCESSFUL
;
87 if (CPLFunc(NULL
, CPL_INIT
, 0, 0) == FALSE
)
89 DPRINT1("Error: failed to initialize console.dll\n");
90 Status
= STATUS_UNSUCCESSFUL
;
94 if (CPLFunc(NULL
, CPL_GETCOUNT
, 0, 0) != 1)
96 DPRINT1("Error: console.dll returned unexpected CPL count\n");
97 Status
= STATUS_UNSUCCESSFUL
;
101 CPLFunc(NULL
, CPL_DBLCLK
, (LPARAM
)lpThreadParameter
, 0);
102 CPLFunc(NULL
, CPL_EXIT
, 0, 0);
105 AlreadyDisplayingProps
= FALSE
;
111 ParseShellInfo(LPCWSTR lpszShellInfo
,
114 DPRINT("ParseShellInfo is UNIMPLEMENTED\n");
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
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
)
139 PRTL_USER_PROCESS_PARAMETERS Parameters
= NtCurrentPeb()->ProcessParameters
;
142 /* Initialize the fields */
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();
156 /* If we don't have any title, use the default one */
157 if (Parameters
->WindowTitle
.Buffer
== NULL
)
159 Title
= DefaultConsoleTitle
;
160 Length
= lstrlenW(DefaultConsoleTitle
) * sizeof(WCHAR
); // sizeof(DefaultConsoleTitle);
164 Title
= Parameters
->WindowTitle
.Buffer
;
165 Length
= Parameters
->WindowTitle
.Length
;
168 /* Retrieve the needed buffer size */
169 Length
+= sizeof(WCHAR
);
170 if (*pTitleLength
> 0) Length
= min(Length
, *pTitleLength
);
171 *pTitleLength
= Length
;
173 /* Capture the data if needed, or, return a pointer to it */
177 * Length is always >= sizeof(WCHAR). Copy everything but the
178 * possible trailing NULL character, and then NULL-terminate.
180 Length
-= sizeof(WCHAR
);
181 RtlCopyMemory(*lpTitle
, Title
, Length
);
182 (*lpTitle
)[Length
/ sizeof(WCHAR
)] = UNICODE_NULL
;
194 if (lpDesktop
&& Parameters
->DesktopInfo
.Buffer
&& *Parameters
->DesktopInfo
.Buffer
)
196 /* Retrieve the needed buffer size */
197 Length
= Parameters
->DesktopInfo
.Length
+ sizeof(WCHAR
);
198 if (*pDesktopLength
> 0) Length
= min(Length
, *pDesktopLength
);
199 *pDesktopLength
= Length
;
201 /* Return a pointer to the data */
202 *lpDesktop
= Parameters
->DesktopInfo
.Buffer
;
207 if (lpDesktop
) *lpDesktop
= NULL
;
210 if (Parameters
->WindowFlags
& STARTF_USEFILLATTRIBUTE
)
212 ConsoleStartInfo
->wFillAttribute
= (WORD
)Parameters
->FillAttribute
;
214 if (Parameters
->WindowFlags
& STARTF_USECOUNTCHARS
)
216 ConsoleStartInfo
->dwScreenBufferSize
.X
= (SHORT
)Parameters
->CountCharsX
;
217 ConsoleStartInfo
->dwScreenBufferSize
.Y
= (SHORT
)Parameters
->CountCharsY
;
219 if (Parameters
->WindowFlags
& STARTF_USESHOWWINDOW
)
221 ConsoleStartInfo
->wShowWindow
= (WORD
)Parameters
->ShowWindowFlags
;
223 if (Parameters
->WindowFlags
& STARTF_USEPOSITION
)
225 ConsoleStartInfo
->dwWindowOrigin
.X
= (SHORT
)Parameters
->StartingX
;
226 ConsoleStartInfo
->dwWindowOrigin
.Y
= (SHORT
)Parameters
->StartingY
;
228 if (Parameters
->WindowFlags
& STARTF_USESIZE
)
230 ConsoleStartInfo
->dwWindowSize
.X
= (SHORT
)Parameters
->CountX
;
231 ConsoleStartInfo
->dwWindowSize
.Y
= (SHORT
)Parameters
->CountY
;
234 /* Get shell information (ShellInfo.Buffer is NULL-terminated) */
235 if (Parameters
->ShellInfo
.Buffer
!= NULL
)
237 ConsoleStartInfo
->IconIndex
= ParseShellInfo(Parameters
->ShellInfo
.Buffer
, L
"dde.");
239 if ((Parameters
->WindowFlags
& STARTF_USEHOTKEY
) == 0)
240 ConsoleStartInfo
->dwHotKey
= ParseShellInfo(Parameters
->ShellInfo
.Buffer
, L
"hotkey.");
242 ConsoleStartInfo
->dwHotKey
= HandleToUlong(Parameters
->StandardInput
);
248 SetUpHandles(IN PCONSOLE_START_INFO ConsoleStartInfo
)
250 PRTL_USER_PROCESS_PARAMETERS Parameters
= NtCurrentPeb()->ProcessParameters
;
252 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_USEHOTKEY
)
254 Parameters
->WindowFlags
&= ~STARTF_USEHOTKEY
;
256 if (ConsoleStartInfo
->dwStartupFlags
& STARTF_SHELLPRIVATE
)
258 Parameters
->WindowFlags
&= ~STARTF_SHELLPRIVATE
;
261 /* We got the handles, let's set them */
262 Parameters
->ConsoleHandle
= ConsoleStartInfo
->ConsoleHandle
;
264 if ((ConsoleStartInfo
->dwStartupFlags
& STARTF_USESTDHANDLES
) == 0)
266 Parameters
->StandardInput
= ConsoleStartInfo
->InputHandle
;
267 Parameters
->StandardOutput
= ConsoleStartInfo
->OutputHandle
;
268 Parameters
->StandardError
= ConsoleStartInfo
->ErrorHandle
;
276 PIMAGE_NT_HEADERS ImageNtHeader
= RtlImageNtHeader(NtCurrentPeb()->ImageBaseAddress
);
277 return (ImageNtHeader
&& (ImageNtHeader
->OptionalHeader
.Subsystem
==
278 IMAGE_SUBSYSTEM_WINDOWS_CUI
));
283 ConnectConsole(IN PWSTR SessionDir
,
284 IN PCONSRV_API_CONNECTINFO ConnectInfo
,
285 OUT PBOOLEAN InServerProcess
)
288 ULONG ConnectInfoSize
= sizeof(*ConnectInfo
);
292 /* Connect to the Console Server */
293 DPRINT("Connecting to the Console Server...\n");
294 Status
= CsrClientConnectToServer(SessionDir
,
295 CONSRV_SERVERDLL_INDEX
,
299 if (!NT_SUCCESS(Status
))
301 DPRINT1("Failed to connect to the Console Server (Status %lx)\n", Status
);
305 /* Nothing to do for server-to-server */
306 if (*InServerProcess
) return TRUE
;
308 /* Nothing to do if this is not a console app */
309 if (!ConnectInfo
->IsConsoleApp
) return TRUE
;
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
))
318 BaseSetLastNTError(Status
);
322 NtClose(ConnectInfo
->ConsoleStartInfo
.InitEvents
[INIT_SUCCESS
]);
323 NtClose(ConnectInfo
->ConsoleStartInfo
.InitEvents
[INIT_FAILURE
]);
324 if (Status
!= INIT_SUCCESS
)
326 NtCurrentPeb()->ProcessParameters
->ConsoleHandle
= NULL
;
336 ConDllInitialize(IN ULONG Reason
,
340 PRTL_USER_PROCESS_PARAMETERS Parameters
= NtCurrentPeb()->ProcessParameters
;
341 BOOLEAN InServerProcess
= FALSE
;
342 CONSRV_API_CONNECTINFO ConnectInfo
;
345 if (Reason
!= DLL_PROCESS_ATTACH
)
347 if ((Reason
== DLL_THREAD_ATTACH
) && IsConsoleApp())
349 /* Sets the current console locale for the new thread */
352 else if (Reason
== DLL_PROCESS_DETACH
)
354 /* Free our resources */
355 if (ConsoleInitialized
== TRUE
)
357 if (ConsoleApplet
) FreeLibrary(ConsoleApplet
);
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
);