[CSRSRV/BASESRV/CONSRV/WIN32CSR/WINSRV]
[reactos.git] / win32ss / user / win32csr / harderror.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS system libraries
4 * FILE: subsys/csrss/win32csr/dllmain.c
5 * PURPOSE: Initialization
6 * PROGRAMMERS: Dmitry Philippov (shedon@mail.ru)
7 * Timo Kreuzer (timo.kreuzer@reactos.org)
8 */
9
10 /* INCLUDES ******************************************************************/
11
12 #include "w32csr.h"
13
14 #define NDEBUG
15 #include <debug.h>
16
17 #include <strsafe.h>
18
19 #define IDTRYAGAIN 10
20 #define IDCONTINUE 11
21
22 /* FUNCTIONS *****************************************************************/
23
24 static
25 NTSTATUS
26 CsrpGetClientFileName(
27 OUT PUNICODE_STRING ClientFileNameU,
28 HANDLE hProcess)
29 {
30 PLIST_ENTRY ModuleListHead;
31 PLIST_ENTRY Entry;
32 PLDR_DATA_TABLE_ENTRY Module;
33 PPEB_LDR_DATA Ldr;
34 PROCESS_BASIC_INFORMATION ClientBasicInfo;
35 LDR_DATA_TABLE_ENTRY ModuleData;
36 PVOID ClientDllBase;
37 NTSTATUS Status;
38 PPEB Peb;
39
40 /* Initialize string */
41 ClientFileNameU->MaximumLength = 0;
42 ClientFileNameU->Length = 0;
43 ClientFileNameU->Buffer = NULL;
44
45 /* Query process information */
46 Status = NtQueryInformationProcess(hProcess,
47 ProcessBasicInformation,
48 &ClientBasicInfo,
49 sizeof(ClientBasicInfo),
50 NULL);
51 if (!NT_SUCCESS(Status)) return Status;
52
53 Peb = ClientBasicInfo.PebBaseAddress;
54 if (!Peb) return STATUS_UNSUCCESSFUL;
55
56 Status = NtReadVirtualMemory(hProcess, &Peb->Ldr, &Ldr, sizeof(Ldr), NULL);
57 if (!NT_SUCCESS(Status)) return Status;
58
59 ModuleListHead = &Ldr->InLoadOrderModuleList;
60 Status = NtReadVirtualMemory(hProcess,
61 &ModuleListHead->Flink,
62 &Entry,
63 sizeof(Entry),
64 NULL);
65 if (!NT_SUCCESS(Status)) return Status;
66
67 if (Entry == ModuleListHead) return STATUS_UNSUCCESSFUL;
68
69 Module = CONTAINING_RECORD(Entry, LDR_DATA_TABLE_ENTRY, InLoadOrderLinks);
70
71 Status = NtReadVirtualMemory(hProcess,
72 Module,
73 &ModuleData,
74 sizeof(ModuleData),
75 NULL);
76 if (!NT_SUCCESS(Status)) return Status;
77
78 Status = NtReadVirtualMemory(hProcess,
79 &Peb->ImageBaseAddress,
80 &ClientDllBase,
81 sizeof(ClientDllBase),
82 NULL);
83 if (!NT_SUCCESS(Status)) return Status;
84
85 if (ClientDllBase != ModuleData.DllBase) return STATUS_UNSUCCESSFUL;
86
87 ClientFileNameU->MaximumLength = ModuleData.BaseDllName.MaximumLength;
88 ClientFileNameU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
89 HEAP_ZERO_MEMORY,
90 ClientFileNameU->MaximumLength);
91
92 Status = NtReadVirtualMemory(hProcess,
93 ModuleData.BaseDllName.Buffer,
94 ClientFileNameU->Buffer,
95 ClientFileNameU->MaximumLength,
96 NULL);
97 if (!NT_SUCCESS(Status))
98 {
99 RtlFreeHeap(RtlGetProcessHeap(), 0, ClientFileNameU->Buffer);
100 ClientFileNameU->Buffer = NULL;
101 ClientFileNameU->MaximumLength = 0;
102 return Status;
103 }
104
105 ClientFileNameU->Length = wcslen(ClientFileNameU->Buffer)*sizeof(wchar_t);
106 DPRINT("ClientFileNameU=\'%wZ\'\n", &ClientFileNameU);
107
108 return STATUS_SUCCESS;
109 }
110
111 static
112 VOID
113 CsrpFreeStringParameters(
114 IN OUT PULONG_PTR Parameters,
115 IN PHARDERROR_MSG HardErrorMessage)
116 {
117 ULONG nParam;
118
119 /* Loop all parameters */
120 for (nParam = 0; nParam < HardErrorMessage->NumberOfParameters; nParam++)
121 {
122 /* Check if the current parameter is a string */
123 if (HardErrorMessage->UnicodeStringParameterMask & (1 << nParam) && Parameters[nParam])
124 {
125 /* Free the string buffer */
126 RtlFreeHeap(RtlGetProcessHeap(), 0, (PVOID)Parameters[nParam]);
127 }
128 }
129 }
130
131 static
132 NTSTATUS
133 CsrpCaptureStringParameters(
134 OUT PULONG_PTR Parameters,
135 OUT PULONG SizeOfAllUnicodeStrings,
136 IN PHARDERROR_MSG HardErrorMessage,
137 HANDLE hProcess)
138 {
139 ULONG nParam, Size = 0;
140 NTSTATUS Status = STATUS_SUCCESS;
141 UNICODE_STRING TempStringU, ParamStringU;
142 ANSI_STRING TempStringA;
143
144 /* Read all strings from client space */
145 for (nParam = 0; nParam < HardErrorMessage->NumberOfParameters; nParam++)
146 {
147 Parameters[nParam] = 0;
148
149 /* Check if the current parameter is a unicode string */
150 if (HardErrorMessage->UnicodeStringParameterMask & (1 << nParam))
151 {
152 /* Read the UNICODE_STRING from the process memory */
153 Status = NtReadVirtualMemory(hProcess,
154 (PVOID)HardErrorMessage->Parameters[nParam],
155 &ParamStringU,
156 sizeof(ParamStringU),
157 NULL);
158
159 if (!NT_SUCCESS(Status))
160 break;
161
162 /* Allocate a buffer for the string */
163 TempStringU.MaximumLength = ParamStringU.Length;
164 TempStringU.Length = ParamStringU.Length;
165 TempStringU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
166 HEAP_ZERO_MEMORY,
167 TempStringU.MaximumLength);
168
169 if (!TempStringU.Buffer)
170 {
171 DPRINT1("Cannot allocate memory %u\n", TempStringU.MaximumLength);
172 Status = STATUS_NO_MEMORY;
173 }
174
175 /* Read the string buffer from the process memory */
176 Status = NtReadVirtualMemory(hProcess,
177 ParamStringU.Buffer,
178 TempStringU.Buffer,
179 ParamStringU.Length,
180 NULL);
181 if (!NT_SUCCESS(Status))
182 {
183 DPRINT1("NtReadVirtualMemory failed with code: %lx\n", Status);
184 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
185 break;
186 }
187
188 DPRINT("ParamString=\'%wZ\'\n", &TempStringU);
189
190 /* Allocate a buffer for converted to ANSI string */
191 TempStringA.MaximumLength = RtlUnicodeStringToAnsiSize(&TempStringU);
192 TempStringA.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
193 HEAP_ZERO_MEMORY,
194 TempStringA.MaximumLength);
195
196 if (!TempStringA.Buffer)
197 {
198 DPRINT1("Cannot allocate memory %u\n", TempStringA.MaximumLength);
199 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
200 Status = STATUS_NO_MEMORY;
201 break;
202 }
203
204 /* Convert string to ANSI and free temporary buffer */
205 Status = RtlUnicodeStringToAnsiString(&TempStringA, &TempStringU, FALSE);
206 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
207 if (!NT_SUCCESS(Status))
208 {
209 RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringA.Buffer);
210 break;
211 }
212
213 /* Note: RtlUnicodeStringToAnsiString returns NULL terminated string */
214 Parameters[nParam] = (ULONG_PTR)TempStringA.Buffer;
215 Size += TempStringU.Length;
216 }
217 else
218 {
219 /* It's not a unicode string */
220 Parameters[nParam] = HardErrorMessage->Parameters[nParam];
221 }
222 }
223
224 if (!NT_SUCCESS(Status))
225 {
226 CsrpFreeStringParameters(Parameters, HardErrorMessage);
227 return Status;
228 }
229
230 *SizeOfAllUnicodeStrings = Size;
231 return Status;
232 }
233
234 static
235 NTSTATUS
236 CsrpFormatMessages(
237 OUT PUNICODE_STRING TextStringU,
238 OUT PUNICODE_STRING CaptionStringU,
239 IN PULONG_PTR Parameters,
240 IN ULONG SizeOfStrings,
241 IN PHARDERROR_MSG Message,
242 IN HANDLE hProcess)
243 {
244 NTSTATUS Status;
245 UNICODE_STRING FileNameU, TempStringU, FormatU;
246 ANSI_STRING FormatA;
247 PMESSAGE_RESOURCE_ENTRY MessageResource;
248 PWSTR FormatString;
249 ULONG Size, ExceptionCode;
250
251 /* Get the file name of the client process */
252 CsrpGetClientFileName(&FileNameU, hProcess);
253
254 /* Check if we have a file name */
255 if (!FileNameU.Buffer)
256 {
257 /* No, use system */
258 RtlInitUnicodeString(&FileNameU, L"System");
259 }
260
261 /* Get text string of the error code */
262 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
263 (ULONG_PTR)RT_MESSAGETABLE,
264 LANG_NEUTRAL,
265 Message->Status,
266 &MessageResource);
267
268 if (NT_SUCCESS(Status))
269 {
270 if (MessageResource->Flags)
271 {
272 RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text);
273 FormatA.Buffer = NULL;
274 }
275 else
276 {
277 RtlInitAnsiString(&FormatA, (PCHAR)MessageResource->Text);
278 RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE);
279 }
280 }
281 else
282 {
283 /* Fall back to hardcoded value */
284 RtlInitUnicodeString(&FormatU, L"Unknown Hard Error");
285 FormatA.Buffer = NULL;
286 }
287
288 FormatString = FormatU.Buffer;
289
290 /* Check whether a caption exists */
291 if (FormatString[0] == L'{')
292 {
293 /* Set caption start */
294 TempStringU.Buffer = ++FormatString;
295
296 /* Get size of the caption */
297 for (Size = 0; *FormatString != 0 && *FormatString != L'}'; Size++)
298 FormatString++;
299
300 /* Skip '}', '\r', '\n' */
301 FormatString += 3;
302
303 TempStringU.Length = Size * sizeof(WCHAR);
304 TempStringU.MaximumLength = TempStringU.Length;
305 }
306 else
307 {
308 /* FIXME: Set string based on severity */
309 RtlInitUnicodeString(&TempStringU, L"Application Error");
310 }
311
312 /* Calculate buffer length for the caption */
313 CaptionStringU->MaximumLength = FileNameU.Length + TempStringU.Length +
314 4 * sizeof(WCHAR);
315
316 /* Allocate a buffer for the caption */
317 CaptionStringU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
318 HEAP_ZERO_MEMORY,
319 CaptionStringU->MaximumLength);
320
321 /* Append the file name, seperator and the caption text */
322 CaptionStringU->Length = 0;
323 RtlAppendUnicodeStringToString(CaptionStringU, &FileNameU);
324 RtlAppendUnicodeToString(CaptionStringU, L" - ");
325 RtlAppendUnicodeStringToString(CaptionStringU, &TempStringU);
326
327 /* Zero terminate the buffer */
328 CaptionStringU->Buffer[CaptionStringU->Length / sizeof(WCHAR)] = 0;
329
330 /* Free the file name buffer */
331 RtlFreeUnicodeString(&FileNameU);
332
333 /* Check if this is an exception message */
334 if (Message->Status == STATUS_UNHANDLED_EXCEPTION)
335 {
336 ExceptionCode = Parameters[0];
337
338 /* Handle special cases */
339 if (ExceptionCode == STATUS_ACCESS_VIOLATION)
340 {
341 Parameters[0] = Parameters[1];
342 Parameters[1] = Parameters[3];
343 if (Parameters[2]) Parameters[2] = (ULONG_PTR)L"written";
344 else Parameters[2] = (ULONG_PTR)L"read";
345 MessageResource = NULL;
346 }
347 else if (ExceptionCode == STATUS_IN_PAGE_ERROR)
348 {
349 Parameters[0] = Parameters[1];
350 Parameters[1] = Parameters[3];
351 MessageResource = NULL;
352 }
353 else
354 {
355 /* Fall back to hardcoded value */
356 Parameters[2] = Parameters[1];
357 Parameters[1] = Parameters[0];
358 Parameters[0] = (ULONG_PTR)L"unknown software exception";
359 }
360
361 if (!MessageResource)
362 {
363 /* Get text string of the exception code */
364 Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
365 (ULONG_PTR)RT_MESSAGETABLE,
366 LANG_NEUTRAL,
367 ExceptionCode,
368 &MessageResource);
369
370 if (NT_SUCCESS(Status))
371 {
372 if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
373
374 if (MessageResource->Flags)
375 {
376 RtlInitUnicodeString(&FormatU, (PWSTR)MessageResource->Text);
377 FormatA.Buffer = NULL;
378 }
379 else
380 {
381 RtlInitAnsiString(&FormatA, (PCHAR)MessageResource->Text);
382 RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE);
383 }
384 FormatString = FormatU.Buffer;
385 }
386 else
387 {
388 /* Fall back to hardcoded value */
389 Parameters[2] = Parameters[1];
390 Parameters[1] = Parameters[0];
391 Parameters[0] = (ULONG_PTR)L"unknown software exception";
392 }
393 }
394 }
395
396 /* Calculate length of text buffer */
397 TextStringU->MaximumLength = FormatU.Length + SizeOfStrings + 42 * sizeof(WCHAR);
398
399 /* Allocate a buffer for the text */
400 TextStringU->Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
401 HEAP_ZERO_MEMORY,
402 TextStringU->MaximumLength);
403
404 /* Wrap in SEH to protect from invalid string parameters */
405 _SEH2_TRY
406 {
407 /* Print the string into the buffer */
408 StringCbPrintfW(TextStringU->Buffer,
409 TextStringU->MaximumLength,
410 FormatString,
411 Parameters[0],
412 Parameters[1],
413 Parameters[2],
414 Parameters[3]);
415 Status = STATUS_SUCCESS;
416 }
417 _SEH2_EXCEPT(EXCEPTION_EXECUTE_HANDLER)
418 {
419 /* Set error and free buffers */
420 Status = _SEH2_GetExceptionCode();
421 RtlFreeHeap(RtlGetProcessHeap(), 0, TextStringU->Buffer);
422 RtlFreeHeap(RtlGetProcessHeap(), 0, CaptionStringU->Buffer);
423 }
424 _SEH2_END
425
426 if (NT_SUCCESS(Status))
427 {
428 TextStringU->Length = wcslen(TextStringU->Buffer) * sizeof(WCHAR);
429 }
430
431 if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
432
433 return Status;
434 }
435
436 static
437 ULONG
438 CsrpMessageBox(
439 PWSTR Text,
440 PWSTR Caption,
441 ULONG ValidResponseOptions,
442 ULONG Severity)
443 {
444 ULONG Type, MessageBoxResponse;
445
446 /* Set the message box type */
447 switch (ValidResponseOptions)
448 {
449 case OptionAbortRetryIgnore:
450 Type = MB_ABORTRETRYIGNORE;
451 break;
452 case OptionOk:
453 Type = MB_OK;
454 break;
455 case OptionOkCancel:
456 Type = MB_OKCANCEL;
457 break;
458 case OptionRetryCancel:
459 Type = MB_RETRYCANCEL;
460 break;
461 case OptionYesNo:
462 Type = MB_YESNO;
463 break;
464 case OptionYesNoCancel:
465 Type = MB_YESNOCANCEL;
466 break;
467 case OptionShutdownSystem:
468 Type = MB_RETRYCANCEL; // FIXME???
469 break;
470 /* Anything else is invalid */
471 default:
472 return ResponseNotHandled;
473 }
474
475 /* Set severity */
476 if (Severity == STATUS_SEVERITY_INFORMATIONAL) Type |= MB_ICONINFORMATION;
477 else if (Severity == STATUS_SEVERITY_WARNING) Type |= MB_ICONWARNING;
478 else if (Severity == STATUS_SEVERITY_ERROR) Type |= MB_ICONERROR;
479
480 Type |= MB_SYSTEMMODAL | MB_SETFOREGROUND;
481
482 DPRINT("Text = '%S', Caption = '%S', Severity = %d, Type = 0x%lx\n",
483 Text, Caption, Severity, Type);
484
485 /* Display a message box */
486 MessageBoxResponse = MessageBoxW(0, Text, Caption, Type);
487
488 /* Return response value */
489 switch (MessageBoxResponse)
490 {
491 case IDOK: return ResponseOk;
492 case IDCANCEL: return ResponseCancel;
493 case IDYES: return ResponseYes;
494 case IDNO: return ResponseNo;
495 case IDABORT: return ResponseAbort;
496 case IDIGNORE: return ResponseIgnore;
497 case IDRETRY: return ResponseRetry;
498 case IDTRYAGAIN: return ResponseTryAgain;
499 case IDCONTINUE: return ResponseContinue;
500 }
501
502 return ResponseNotHandled;
503 }
504
505 VOID
506 WINAPI
507 Win32CsrHardError(
508 IN PCSR_THREAD ThreadData,
509 IN PHARDERROR_MSG Message)
510 {
511 #if DBG
512 PCSR_PROCESS ProcessData = ThreadData->Process;
513 #endif
514 ULONG_PTR Parameters[MAXIMUM_HARDERROR_PARAMETERS];
515 OBJECT_ATTRIBUTES ObjectAttributes;
516 UNICODE_STRING TextU, CaptionU;
517 NTSTATUS Status;
518 HANDLE hProcess;
519 ULONG Size;
520
521 /* Default to not handled */
522 ASSERT(ProcessData != NULL);
523 Message->Response = ResponseNotHandled;
524
525 /* Make sure we don't have too many parameters */
526 if (Message->NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
527 Message->NumberOfParameters = MAXIMUM_HARDERROR_PARAMETERS;
528
529 /* Initialize object attributes */
530 InitializeObjectAttributes(&ObjectAttributes, NULL, 0, NULL, NULL);
531
532 /* Open client process */
533 Status = NtOpenProcess(&hProcess,
534 PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
535 &ObjectAttributes,
536 &Message->h.ClientId);
537
538 if (!NT_SUCCESS(Status))
539 {
540 DPRINT1("NtOpenProcess failed with code: %lx\n", Status);
541 return;
542 }
543
544 /* Capture all string parameters from the process memory */
545 Status = CsrpCaptureStringParameters(Parameters, &Size, Message, hProcess);
546 if (!NT_SUCCESS(Status))
547 {
548 NtClose(hProcess);
549 return;
550 }
551
552 /* Format the caption and message box text */
553 Status = CsrpFormatMessages(&TextU,
554 &CaptionU,
555 Parameters,
556 Size,
557 Message,
558 hProcess);
559
560 /* Cleanup */
561 CsrpFreeStringParameters(Parameters, Message);
562 NtClose(hProcess);
563
564 if (!NT_SUCCESS(Status))
565 {
566 return;
567 }
568
569 /* Display the message box */
570 Message->Response = CsrpMessageBox(TextU.Buffer,
571 CaptionU.Buffer,
572 Message->ValidResponseOptions,
573 (ULONG)Message->Status >> 30);
574
575 RtlFreeUnicodeString(&TextU);
576 RtlFreeUnicodeString(&CaptionU);
577
578 return;
579 }
580