[USERSRV] Hard-error improvements 5/7 - Implement STATUS_SERVICE_NOTIFICATION handling.

- In UserpCaptureStringParameters(), always capture the parameter
  strings with a terminating NULL. Also, no need to convert them to
  ANSI if they are used with a STATUS_SERVICE_NOTIFICATION message.
- Determine within UserpFormatMessages() the message box flags to be
  used, either from the message parameters (if STATUS_SERVICE_NOTIFICATION)
  or from the proposed response options and message status severity.
  These flags are then used by both UserpShowInformationBalloon()/UserpShellHardError()
  and by UserpMessageBox().
- Improve Message validation (especially for STATUS_SERVICE_NOTIFICATION).
- Try to display the hard error status number if it's an unknown one.
This commit is contained in:
Hermès Bélusca-Maïto 2018-03-19 02:22:20 +01:00
parent 9ea2783e56
commit 08d70697a3
No known key found for this signature in database
GPG key ID: 3B2539C65E7B93D0

View file

@ -114,8 +114,8 @@ UserpCaptureStringParameters(
continue; continue;
} }
/* Allocate a buffer for the string */ /* Allocate a buffer for the string and reserve a NULL terminator */
TempStringU.MaximumLength = ParamStringU.Length; TempStringU.MaximumLength = ParamStringU.Length + sizeof(UNICODE_NULL);
TempStringU.Length = ParamStringU.Length; TempStringU.Length = ParamStringU.Length;
TempStringU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(), TempStringU.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY, HEAP_ZERO_MEMORY,
@ -140,36 +140,47 @@ UserpCaptureStringParameters(
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
continue; continue;
} }
/* NULL-terminate the string */
TempStringU.Buffer[TempStringU.Length / sizeof(WCHAR)] = UNICODE_NULL;
DPRINT("ParamString = \'%wZ\'\n", &TempStringU); DPRINT("ParamString = \'%wZ\'\n", &TempStringU);
/* Allocate a buffer for converted to ANSI string */ if (Message->Status == STATUS_SERVICE_NOTIFICATION)
TempStringA.MaximumLength = RtlUnicodeStringToAnsiSize(&TempStringU);
TempStringA.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
TempStringA.MaximumLength);
if (!TempStringA.Buffer)
{ {
/* We failed, skip this string */ /* Just keep the allocated NULL-terminated UNICODE string */
DPRINT1("Cannot allocate memory with size %u, skipping.\n", TempStringA.MaximumLength); Parameters[nParam] = (ULONG_PTR)TempStringU.Buffer;
Size += TempStringU.Length;
}
else
{
/* Allocate a buffer for converted to ANSI string */
TempStringA.MaximumLength = RtlUnicodeStringToAnsiSize(&TempStringU);
TempStringA.Buffer = RtlAllocateHeap(RtlGetProcessHeap(),
HEAP_ZERO_MEMORY,
TempStringA.MaximumLength);
if (!TempStringA.Buffer)
{
/* We failed, skip this string */
DPRINT1("Cannot allocate memory with size %u, skipping.\n", TempStringA.MaximumLength);
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
continue;
}
/* Convert string to ANSI and free temporary buffer */
Status = RtlUnicodeStringToAnsiString(&TempStringA, &TempStringU, FALSE);
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer); RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer);
continue; if (!NT_SUCCESS(Status))
} {
/* We failed, skip this string */
DPRINT1("RtlUnicodeStringToAnsiString() failed, Status 0x%lx, skipping.\n", Status);
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringA.Buffer);
continue;
}
/* Convert string to ANSI and free temporary buffer */ /* Note: RtlUnicodeStringToAnsiString() returns a NULL-terminated string */
Status = RtlUnicodeStringToAnsiString(&TempStringA, &TempStringU, FALSE); Parameters[nParam] = (ULONG_PTR)TempStringA.Buffer;
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringU.Buffer); Size += TempStringU.Length;
if (!NT_SUCCESS(Status))
{
/* We failed, skip this string */
DPRINT1("RtlUnicodeStringToAnsiString() failed, Status 0x%lx, skipping.\n", Status);
RtlFreeHeap(RtlGetProcessHeap(), 0, TempStringA.Buffer);
continue;
} }
/* Note: RtlUnicodeStringToAnsiString returns NULL terminated string */
Parameters[nParam] = (ULONG_PTR)TempStringA.Buffer;
Size += TempStringU.Length;
} }
else else
{ {
@ -293,11 +304,48 @@ UserpGetClientFileName(
return STATUS_SUCCESS; return STATUS_SUCCESS;
} }
static
VOID
UserpDuplicateParamStringToUnicodeString(
IN OUT PUNICODE_STRING UnicodeString,
IN PCWSTR ParamString)
{
UNICODE_STRING FormatU, TempStringU;
/* Calculate buffer length for the text message */
RtlInitUnicodeString(&FormatU, (PWSTR)ParamString);
if (UnicodeString->MaximumLength < FormatU.MaximumLength)
{
/* Duplicate the text message in a larger buffer */
if (NT_SUCCESS(RtlDuplicateUnicodeString(RTL_DUPLICATE_UNICODE_STRING_NULL_TERMINATE,
&FormatU, &TempStringU)))
{
*UnicodeString = TempStringU;
}
else
{
/* We could not allocate a larger buffer; continue using the smaller original buffer */
DPRINT1("Cannot allocate memory for UnicodeString, use original buffer.\n");
/* Copy the truncated string, NULL-terminate it */
FormatU.MaximumLength = UnicodeString->MaximumLength;
FormatU.Length = FormatU.MaximumLength - sizeof(UNICODE_NULL);
RtlCopyUnicodeString(UnicodeString, &FormatU);
}
}
else
{
/* Copy the string, NULL-terminate it */
RtlCopyUnicodeString(UnicodeString, &FormatU);
}
}
static static
VOID VOID
UserpFormatMessages( UserpFormatMessages(
IN OUT PUNICODE_STRING TextStringU, IN OUT PUNICODE_STRING TextStringU,
IN OUT PUNICODE_STRING CaptionStringU, IN OUT PUNICODE_STRING CaptionStringU,
OUT PUINT pdwType,
IN PHARDERROR_MSG Message) IN PHARDERROR_MSG Message)
{ {
NTSTATUS Status; NTSTATUS Status;
@ -312,7 +360,7 @@ UserpFormatMessages(
PMESSAGE_RESOURCE_ENTRY MessageResource; PMESSAGE_RESOURCE_ENTRY MessageResource;
PWSTR FormatString, pszBuffer; PWSTR FormatString, pszBuffer;
size_t cszBuffer; size_t cszBuffer;
ULONG Severity; ULONG Severity = (ULONG)(Message->Status) >> 30;
ULONG Size; ULONG Size;
/* Open client process */ /* Open client process */
@ -330,6 +378,90 @@ UserpFormatMessages(
/* Capture all string parameters from the process memory */ /* Capture all string parameters from the process memory */
UserpCaptureStringParameters(Parameters, &SizeOfStrings, Message, hProcess); UserpCaptureStringParameters(Parameters, &SizeOfStrings, Message, hProcess);
/*
* Check whether it is a service notification, in which case
* we format the parameters and take the short route.
*/
if (Message->Status == STATUS_SERVICE_NOTIFICATION)
{
/* Close the process handle */
if (hProcess) NtClose(hProcess);
/*
* Retrieve the message box flags. Note that we filter out
* MB_SERVICE_NOTIFICATION to not enter an infinite recursive
* loop when we will call MessageBox() later on.
*/
*pdwType = (UINT)Parameters[2] & ~MB_SERVICE_NOTIFICATION;
/* Duplicate the UNICODE text message */
if (Message->UnicodeStringParameterMask & 0x1)
{
/* A string has been provided: duplicate it */
UserpDuplicateParamStringToUnicodeString(TextStringU, (PCWSTR)Parameters[0]);
}
else
{
/* No string (or invalid one) has been provided: keep the original buffer and reset the string length to zero */
TextStringU->Length = 0;
}
/* Duplicate the UNICODE caption */
if (Message->UnicodeStringParameterMask & 0x2)
{
/* A string has been provided: duplicate it */
UserpDuplicateParamStringToUnicodeString(CaptionStringU, (PCWSTR)Parameters[1]);
}
else
{
/* No string (or invalid one) has been provided: keep the original buffer and reset the string length to zero */
CaptionStringU->Length = 0;
}
goto Quit;
}
/* Set the message box type */
*pdwType = 0;
switch (Message->ValidResponseOptions)
{
case OptionAbortRetryIgnore:
*pdwType = MB_ABORTRETRYIGNORE;
break;
case OptionOk:
*pdwType = MB_OK;
break;
case OptionOkCancel:
*pdwType = MB_OKCANCEL;
break;
case OptionRetryCancel:
*pdwType = MB_RETRYCANCEL;
break;
case OptionYesNo:
*pdwType = MB_YESNO;
break;
case OptionYesNoCancel:
*pdwType = MB_YESNOCANCEL;
break;
case OptionShutdownSystem:
*pdwType = MB_OK;
break;
case OptionOkNoWait:
*pdwType = MB_OK;
break;
case OptionCancelTryContinue:
*pdwType = MB_CANCELTRYCONTINUE;
break;
}
/* Set the severity icon */
// STATUS_SEVERITY_SUCCESS
if (Severity == STATUS_SEVERITY_INFORMATIONAL) *pdwType |= MB_ICONINFORMATION;
else if (Severity == STATUS_SEVERITY_WARNING) *pdwType |= MB_ICONWARNING;
else if (Severity == STATUS_SEVERITY_ERROR) *pdwType |= MB_ICONERROR;
*pdwType |= MB_SYSTEMMODAL | MB_SETFOREGROUND;
/* Copy the Parameters array locally */ /* Copy the Parameters array locally */
RtlCopyMemory(&CopyParameters, Parameters, sizeof(CopyParameters)); RtlCopyMemory(&CopyParameters, Parameters, sizeof(CopyParameters));
@ -351,8 +483,6 @@ UserpFormatMessages(
FileNameU = g_SystemProcessU; FileNameU = g_SystemProcessU;
} }
Severity = (ULONG)(Message->Status) >> 30;
/* Get text string of the error code */ /* Get text string of the error code */
Status = RtlFindMessage(GetModuleHandleW(L"ntdll"), Status = RtlFindMessage(GetModuleHandleW(L"ntdll"),
(ULONG_PTR)RT_MESSAGETABLE, (ULONG_PTR)RT_MESSAGETABLE,
@ -368,14 +498,25 @@ UserpFormatMessages(
} }
else else
{ {
RtlInitAnsiString(&FormatA, (PCHAR)MessageResource->Text); RtlInitAnsiString(&FormatA, (PSTR)MessageResource->Text);
/* Status = */ RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE); /* Status = */ RtlAnsiStringToUnicodeString(&FormatU, &FormatA, TRUE);
} }
} }
else else
{ {
/* Fall back to hardcoded value */ /*
RtlInitUnicodeString(&FormatU, L"Unknown Hard Error"); * Fall back to hardcoded value.
* NOTE: The value used here is ReactOS-specific: it allows specifying
* the exact hard error status value and the parameters. The version
* used on Windows only says "Unknown Hard Error".
*/
#if 0
RtlInitUnicodeString(&FormatU, L"Unknown Hard Error 0x%08lx\n"
L"Parameters: 0x%p 0x%p 0x%p 0x%p");
#else
RtlInitUnicodeString(&FormatU, L"Unknown Hard Error 0x%08lx");
CopyParameters[0] = Message->Status;
#endif
FormatA.Buffer = NULL; FormatA.Buffer = NULL;
} }
@ -444,7 +585,7 @@ UserpFormatMessages(
/* Calculate buffer length for the caption */ /* Calculate buffer length for the caption */
cszBuffer = WindowTitleU.Length + FileNameU.Length + TempStringU.Length + cszBuffer = WindowTitleU.Length + FileNameU.Length + TempStringU.Length +
3 * sizeof(WCHAR) + sizeof(UNICODE_NULL); 3 * sizeof(WCHAR) + sizeof(UNICODE_NULL);
if (cszBuffer > CaptionStringU->MaximumLength) if (CaptionStringU->MaximumLength < cszBuffer)
{ {
/* Allocate a larger buffer for the caption */ /* Allocate a larger buffer for the caption */
pszBuffer = RtlAllocateHeap(RtlGetProcessHeap(), pszBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
@ -452,8 +593,8 @@ UserpFormatMessages(
cszBuffer); cszBuffer);
if (!pszBuffer) if (!pszBuffer)
{ {
/* We could not allocate a larger buffer; continue using the smaller static buffer */ /* We could not allocate a larger buffer; continue using the smaller original buffer */
DPRINT1("Cannot allocate memory for CaptionStringU, use static buffer.\n"); DPRINT1("Cannot allocate memory for CaptionStringU, use original buffer.\n");
} }
else else
{ {
@ -496,7 +637,7 @@ UserpFormatMessages(
} }
else else
{ {
RtlInitAnsiString(&Format2A, (PCHAR)MessageResource->Text); RtlInitAnsiString(&Format2A, (PSTR)MessageResource->Text);
/* Status = */ RtlAnsiStringToUnicodeString(&Format2U, &Format2A, TRUE); /* Status = */ RtlAnsiStringToUnicodeString(&Format2U, &Format2A, TRUE);
} }
@ -552,6 +693,7 @@ UserpFormatMessages(
CopyParameters[0] = (ULONG_PTR)L"unknown software exception"; CopyParameters[0] = (ULONG_PTR)L"unknown software exception";
} }
/* Add explanation text for dialog buttons */
if (Message->ValidResponseOptions == OptionOk || if (Message->ValidResponseOptions == OptionOk ||
Message->ValidResponseOptions == OptionOkCancel) Message->ValidResponseOptions == OptionOkCancel)
{ {
@ -568,7 +710,7 @@ UserpFormatMessages(
/* Calculate buffer length for the text message */ /* Calculate buffer length for the text message */
cszBuffer = FormatU.Length + SizeOfStrings + Size * sizeof(WCHAR) + cszBuffer = FormatU.Length + SizeOfStrings + Size * sizeof(WCHAR) +
sizeof(UNICODE_NULL); sizeof(UNICODE_NULL);
if (cszBuffer > TextStringU->MaximumLength) if (TextStringU->MaximumLength < cszBuffer)
{ {
/* Allocate a larger buffer for the text message */ /* Allocate a larger buffer for the text message */
pszBuffer = RtlAllocateHeap(RtlGetProcessHeap(), pszBuffer = RtlAllocateHeap(RtlGetProcessHeap(),
@ -576,8 +718,8 @@ UserpFormatMessages(
cszBuffer); cszBuffer);
if (!pszBuffer) if (!pszBuffer)
{ {
/* We could not allocate a larger buffer; continue using the smaller static buffer */ /* We could not allocate a larger buffer; continue using the smaller original buffer */
DPRINT1("Cannot allocate memory for TextStringU, use static buffer.\n"); DPRINT1("Cannot allocate memory for TextStringU, use original buffer.\n");
} }
else else
{ {
@ -599,6 +741,7 @@ UserpFormatMessages(
CopyParameters[0], CopyParameters[1], CopyParameters[0], CopyParameters[1],
CopyParameters[2], CopyParameters[3]); CopyParameters[2], CopyParameters[3]);
/* Add explanation text for dialog buttons */
if (Message->Status == STATUS_UNHANDLED_EXCEPTION) if (Message->Status == STATUS_UNHANDLED_EXCEPTION)
{ {
if (Message->ValidResponseOptions == OptionOk || if (Message->ValidResponseOptions == OptionOk ||
@ -642,6 +785,7 @@ UserpFormatMessages(
if (Format2A.Buffer) RtlFreeUnicodeString(&Format2U); if (Format2A.Buffer) RtlFreeUnicodeString(&Format2U);
if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU); if (FormatA.Buffer) RtlFreeUnicodeString(&FormatU);
Quit:
/* Final cleanup */ /* Final cleanup */
UserpFreeStringParameters(Parameters, Message); UserpFreeStringParameters(Parameters, Message);
} }
@ -695,9 +839,11 @@ GetRegInt(
} }
static BOOL static BOOL
UserpShowInformationBalloon(PWSTR Text, UserpShowInformationBalloon(
PWSTR Caption, IN PCWSTR Text,
PHARDERROR_MSG Message) IN PCWSTR Caption,
IN UINT Type,
IN PHARDERROR_MSG Message)
{ {
ULONG ShellErrorMode; ULONG ShellErrorMode;
HWND hwnd; HWND hwnd;
@ -741,7 +887,7 @@ UserpShowInformationBalloon(PWSTR Text,
if (NT_SUCCESS(Message->Status)) if (NT_SUCCESS(Message->Status))
pdata->dwType = MB_OK; pdata->dwType = MB_OK;
else if (Message->Status == STATUS_SERVICE_NOTIFICATION) else if (Message->Status == STATUS_SERVICE_NOTIFICATION)
pdata->dwType = Message->Parameters[2]; pdata->dwType = Type;
else else
pdata->dwType = MB_ICONINFORMATION; pdata->dwType = MB_ICONINFORMATION;
@ -768,68 +914,17 @@ UserpShowInformationBalloon(PWSTR Text,
} }
static static
ULONG HARDERROR_RESPONSE
UserpMessageBox( UserpMessageBox(
IN PCWSTR Text, IN PCWSTR Text,
IN PCWSTR Caption, IN PCWSTR Caption,
IN ULONG ValidResponseOptions, IN UINT Type,
IN ULONG Severity, IN ULONG Timeout)
IN ULONG Timeout)
{ {
ULONG Type, MessageBoxResponse; ULONG MessageBoxResponse;
/* Set the message box type */ DPRINT("Text = '%S', Caption = '%S', Type = 0x%lx\n",
switch (ValidResponseOptions) Text, Caption, Type);
{
case OptionAbortRetryIgnore:
Type = MB_ABORTRETRYIGNORE;
break;
case OptionOk:
Type = MB_OK;
break;
case OptionOkCancel:
Type = MB_OKCANCEL;
break;
case OptionRetryCancel:
Type = MB_RETRYCANCEL;
break;
case OptionYesNo:
Type = MB_YESNO;
break;
case OptionYesNoCancel:
Type = MB_YESNOCANCEL;
break;
case OptionShutdownSystem:
Type = MB_RETRYCANCEL; // FIXME???
break;
case OptionOkNoWait:
/*
* At that point showing the balloon failed. Is that correct?
*/
Type = MB_OK; // FIXME!
break;
case OptionCancelTryContinue:
Type = MB_CANCELTRYCONTINUE;
break;
/* Anything else is invalid */
default:
{
DPRINT1("Unknown ValidResponseOptions = %d\n", ValidResponseOptions);
return ResponseNotHandled;
}
}
/* Set severity */
// STATUS_SEVERITY_SUCCESS
if (Severity == STATUS_SEVERITY_INFORMATIONAL) Type |= MB_ICONINFORMATION;
else if (Severity == STATUS_SEVERITY_WARNING) Type |= MB_ICONWARNING;
else if (Severity == STATUS_SEVERITY_ERROR) Type |= MB_ICONERROR;
Type |= MB_SYSTEMMODAL | MB_SETFOREGROUND;
DPRINT("Text = '%S', Caption = '%S', Severity = %d, Type = 0x%lx\n",
Text, Caption, Severity, Type);
/* Display a message box */ /* Display a message box */
MessageBoxResponse = MessageBoxTimeoutW(NULL, Text, Caption, Type, 0, Timeout); MessageBoxResponse = MessageBoxTimeoutW(NULL, Text, Caption, Type, 0, Timeout);
@ -846,6 +941,7 @@ UserpMessageBox(
case IDRETRY: return ResponseRetry; case IDRETRY: return ResponseRetry;
case IDTRYAGAIN: return ResponseTryAgain; case IDTRYAGAIN: return ResponseTryAgain;
case IDCONTINUE: return ResponseContinue; case IDCONTINUE: return ResponseContinue;
default: return ResponseNotHandled;
} }
return ResponseNotHandled; return ResponseNotHandled;
@ -895,6 +991,7 @@ UserServerHardError(
IN PHARDERROR_MSG Message) IN PHARDERROR_MSG Message)
{ {
ULONG ErrorMode; ULONG ErrorMode;
UINT dwType = 0;
UNICODE_STRING TextU, CaptionU; UNICODE_STRING TextU, CaptionU;
WCHAR LocalTextBuffer[256]; WCHAR LocalTextBuffer[256];
WCHAR LocalCaptionBuffer[256]; WCHAR LocalCaptionBuffer[256];
@ -907,16 +1004,24 @@ UserServerHardError(
/* Make sure we don't have too many parameters */ /* Make sure we don't have too many parameters */
if (Message->NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS) if (Message->NumberOfParameters > MAXIMUM_HARDERROR_PARAMETERS)
{ {
// FIXME: Windows just fails (STATUS_INVALID_PARAMETER) & returns ResponseNotHandled. // NOTE: Windows just fails (STATUS_INVALID_PARAMETER) & returns ResponseNotHandled.
DPRINT1("Invalid NumberOfParameters = %d\n", Message->NumberOfParameters);
Message->NumberOfParameters = MAXIMUM_HARDERROR_PARAMETERS; Message->NumberOfParameters = MAXIMUM_HARDERROR_PARAMETERS;
} }
if (Message->ValidResponseOptions > OptionCancelTryContinue) if (Message->ValidResponseOptions > OptionCancelTryContinue)
{ {
// STATUS_INVALID_PARAMETER; DPRINT1("Unknown ValidResponseOptions = %d\n", Message->ValidResponseOptions);
Message->Response = ResponseNotHandled; return; // STATUS_INVALID_PARAMETER;
return; }
if (Message->Status == STATUS_SERVICE_NOTIFICATION)
{
if (Message->NumberOfParameters < 3)
{
DPRINT1("Invalid NumberOfParameters = %d for STATUS_SERVICE_NOTIFICATION\n", Message->NumberOfParameters);
return; // STATUS_INVALID_PARAMETER;
}
// (Message->UnicodeStringParameterMask & 0x3)
} }
// TODO: More message validation: check NumberOfParameters wrt. Message Status code.
/* Re-initialize the hard errors cache */ /* Re-initialize the hard errors cache */
UserInitHardErrorsCache(); UserInitHardErrorsCache();
@ -924,7 +1029,7 @@ UserServerHardError(
/* Format the message caption and text */ /* Format the message caption and text */
RtlInitEmptyUnicodeString(&TextU, LocalTextBuffer, sizeof(LocalTextBuffer)); RtlInitEmptyUnicodeString(&TextU, LocalTextBuffer, sizeof(LocalTextBuffer));
RtlInitEmptyUnicodeString(&CaptionU, LocalCaptionBuffer, sizeof(LocalCaptionBuffer)); RtlInitEmptyUnicodeString(&CaptionU, LocalCaptionBuffer, sizeof(LocalCaptionBuffer));
UserpFormatMessages(&TextU, &CaptionU, Message); UserpFormatMessages(&TextU, &CaptionU, &dwType, /* &Timeout, */ Message);
/* Log the hard error message */ /* Log the hard error message */
UserpLogHardError(&TextU, &CaptionU); UserpLogHardError(&TextU, &CaptionU);
@ -948,6 +1053,7 @@ UserServerHardError(
Message->Response = ResponseOk; Message->Response = ResponseOk;
if (UserpShowInformationBalloon(TextU.Buffer, if (UserpShowInformationBalloon(TextU.Buffer,
CaptionU.Buffer, CaptionU.Buffer,
dwType,
Message)) Message))
{ {
Message->Response = ResponseOk; Message->Response = ResponseOk;
@ -958,8 +1064,7 @@ UserServerHardError(
/* Display the message box */ /* Display the message box */
Message->Response = UserpMessageBox(TextU.Buffer, Message->Response = UserpMessageBox(TextU.Buffer,
CaptionU.Buffer, CaptionU.Buffer,
Message->ValidResponseOptions, dwType,
(ULONG)(Message->Status) >> 30,
(ULONG)-1); (ULONG)-1);
Quit: Quit: