You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2335 lines
72 KiB
2335 lines
72 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: msgbox.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* This module contains the MessageBox API and related functions.
|
|
*
|
|
* History:
|
|
* 10-23-90 DarrinM Created.
|
|
* 02-08-91 IanJa HWND revalidation added
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
//
|
|
// Dimension constants -- D.U. == dialog units
|
|
//
|
|
#define DU_OUTERMARGIN 7
|
|
#define DU_INNERMARGIN 10
|
|
|
|
#define DU_BTNGAP 4 // D.U. of space between buttons
|
|
#define DU_BTNHEIGHT 14 // D.U. of button height
|
|
// This is used only in kernel\inctlpan.c, so move it there
|
|
//
|
|
// #define DU_BTNWIDTH 50 // D.U. of button width, minimum
|
|
//
|
|
|
|
LPBYTE MB_UpdateDlgHdr(LPDLGTEMPLATE lpDlgTmp, long lStyle, long lExtendedStyle, BYTE bItemCount,
|
|
int iX, int iY, int iCX, int iCY, LPWSTR lpszCaption, int iCaptionLen);
|
|
LPBYTE MB_UpdateDlgItem(LPDLGITEMTEMPLATE lpDlgItem, int iCtrlId, long lStyle, long lExtendedStyle,
|
|
int iX, int iY, int iCX, int iCY, LPWSTR lpszText, UINT wTextLen,
|
|
int iControlClass);
|
|
UINT MB_GetIconOrdNum(UINT rgBits);
|
|
LPBYTE MB_AddPushButtons(
|
|
LPDLGITEMTEMPLATE lpDlgTmp,
|
|
LPMSGBOXDATA lpmb,
|
|
UINT wLEdge,
|
|
UINT wBEdge);
|
|
UINT MB_FindDlgTemplateSize( LPMSGBOXDATA lpmb );
|
|
int MessageBoxWorker(LPMSGBOXDATA pMsgBoxParams);
|
|
VOID EndTaskModalDialog(HWND hwndDlg);
|
|
VOID StartTaskModalDialog(HWND hwndDlg);
|
|
|
|
#ifdef _JANUS_
|
|
|
|
#include "strid.h"
|
|
#include <imagehlp.h>
|
|
|
|
// constant strings
|
|
CONST WCHAR szEMIKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Control\\Error Message Instrument\\";
|
|
CONST WCHAR szEMIEnable[] = L"EnableLogging";
|
|
CONST WCHAR szEMISeverity[] = L"LogSeverity";
|
|
CONST WCHAR szDMREnable[] = L"EnableDefaultReply";
|
|
CONST WCHAR szEventKey[] = L"\\Registry\\Machine\\System\\CurrentControlSet\\Services\\EventLog\\Application\\Error Instrument\\";
|
|
CONST WCHAR szEventMsgFile[] = L"EventMessageFile";
|
|
CONST WCHAR szEventType[] = L"TypesSupported";
|
|
|
|
#define TITLE_SIZE 64
|
|
#define DATETIME_SIZE 32
|
|
|
|
#define EMI_SEVERITY_ALL 0
|
|
#define EMI_SEVERITY_USER 1
|
|
#define EMI_SEVERITY_INFORMATION 2
|
|
#define EMI_SEVERITY_QUESTION 3
|
|
#define EMI_SEVERITY_WARNING 4
|
|
#define EMI_SEVERITY_ERROR 5
|
|
#define EMI_SEVERITY_MAX_VALUE 5
|
|
|
|
// element of error message
|
|
PVOID gpReturnAddr = 0;
|
|
HANDLE gdwEMIThreadID = 0;
|
|
typedef struct _ERROR_ELEMENT {
|
|
WCHAR ProcessName[MAX_PATH];
|
|
WCHAR WindowTitle[TITLE_SIZE];
|
|
DWORD dwStyle;
|
|
DWORD dwErrorCode;
|
|
WCHAR CallerModuleName[MAX_PATH];
|
|
PVOID BaseAddr;
|
|
DWORD dwImageSize;
|
|
PVOID ReturnAddr;
|
|
LPWSTR lpszCaption;
|
|
LPWSTR lpszText;
|
|
} ERROR_ELEMENT, *LPERROR_ELEMENT;
|
|
|
|
BOOL ErrorMessageInst(LPMSGBOXDATA pMsgBoxParams);
|
|
BOOL InitInstrument(LPDWORD lpEMIControl);
|
|
|
|
// eventlog stuff
|
|
HANDLE gEventSource;
|
|
NTSTATUS CreateLogSource();
|
|
BOOL LogMessageBox(LPERROR_ELEMENT lpErrEle);
|
|
|
|
#define EMIGETRETURNADDRESS() \
|
|
{ \
|
|
if (gfEMIEnable) { \
|
|
if (InterlockedCompareExchangePointer(&gdwEMIThreadID, \
|
|
GETTHREADID(), \
|
|
0) \
|
|
== 0) { \
|
|
gpReturnAddr = (PVOID) 1; \
|
|
} \
|
|
} \
|
|
}
|
|
|
|
// _ReturnAddress();
|
|
#else
|
|
#define EMIGETRETURNADDRESS()
|
|
#endif //_JANUS_
|
|
|
|
|
|
|
|
#define MB_MASKSHIFT 4
|
|
|
|
static CONST WCHAR szEmpty[] = L"";
|
|
WCHAR szERROR[10];
|
|
|
|
/***************************************************************************\
|
|
* SendHelpMessage
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
VOID
|
|
SendHelpMessage(
|
|
HWND hwnd,
|
|
int iType,
|
|
int iCtrlId,
|
|
HANDLE hItemHandle,
|
|
DWORD dwContextId,
|
|
MSGBOXCALLBACK lpfnCallback)
|
|
{
|
|
HELPINFO HelpInfo;
|
|
long lValue;
|
|
|
|
HelpInfo.cbSize = sizeof(HELPINFO);
|
|
HelpInfo.iContextType = iType;
|
|
HelpInfo.iCtrlId = iCtrlId;
|
|
HelpInfo.hItemHandle = hItemHandle;
|
|
HelpInfo.dwContextId = dwContextId;
|
|
|
|
lValue = NtUserGetMessagePos();
|
|
HelpInfo.MousePos.x = GET_X_LPARAM(lValue);
|
|
HelpInfo.MousePos.y = GET_Y_LPARAM(lValue);
|
|
|
|
// Check if there is an app supplied callback.
|
|
if (lpfnCallback != NULL) {
|
|
if (IsWOWProc(lpfnCallback)) {
|
|
(*pfnWowMsgBoxIndirectCallback)(PtrToUlong(lpfnCallback), &HelpInfo);
|
|
} else {
|
|
(*lpfnCallback)(&HelpInfo);
|
|
}
|
|
} else {
|
|
SendMessage(hwnd, WM_HELP, 0, (LPARAM)&HelpInfo);
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* ServiceMessageBox
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
CONST int aidReturn[] = { 0, 0, IDABORT, IDCANCEL, IDIGNORE, IDNO, IDOK, IDRETRY, IDYES };
|
|
|
|
int ServiceMessageBox(
|
|
LPCWSTR pText,
|
|
LPCWSTR pCaption,
|
|
UINT wType,
|
|
DWORD dwTimeout)
|
|
{
|
|
NTSTATUS Status;
|
|
ULONG_PTR Parameters[4];
|
|
ULONG Response = ResponseNotHandled;
|
|
UNICODE_STRING Text, Caption;
|
|
|
|
/*
|
|
* For Terminal Services we must decided the session in which this message
|
|
* box should be displayed. We do this by looking at the impersonation token
|
|
* and use the session on which the client is running.
|
|
*/
|
|
if (ISTS()) {
|
|
HANDLE TokenHandle;
|
|
ULONG ClientSessionId;
|
|
ULONG ProcessSessionId;
|
|
ULONG ReturnLength;
|
|
BOOLEAN bResult;
|
|
|
|
/*
|
|
* Obtain access to the impersonation token if it's present.
|
|
*/
|
|
Status = NtOpenThreadToken (
|
|
GetCurrentThread(),
|
|
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
|
|
TRUE,
|
|
&TokenHandle
|
|
);
|
|
if (NT_SUCCESS(Status)) {
|
|
/*
|
|
* Query the Session ID out of the Token
|
|
*/
|
|
Status = NtQueryInformationToken (
|
|
TokenHandle,
|
|
TokenSessionId,
|
|
(PVOID)&ClientSessionId,
|
|
sizeof(ClientSessionId),
|
|
&ReturnLength
|
|
);
|
|
CloseHandle(TokenHandle);
|
|
if (NT_SUCCESS(Status)) {
|
|
/*
|
|
* Get the process session Id. Use the Kernel32 API first because
|
|
* the PEB is writable in case someone is hacking it.
|
|
*/
|
|
if (!ProcessIdToSessionId(GetCurrentProcessId(), &ProcessSessionId)) {
|
|
ProcessSessionId = NtCurrentPeb()->SessionId;
|
|
}
|
|
|
|
if (ClientSessionId != ProcessSessionId) {
|
|
/*
|
|
* This message box was intended for session other than the
|
|
* one on which this process is running. Forward it to the
|
|
* right session with WinStationSendMessage().
|
|
*/
|
|
/*
|
|
* Handle case where Caption or Title is NULL
|
|
*/
|
|
if (pCaption == NULL) {
|
|
pCaption = szEmpty;
|
|
}
|
|
if (pText == NULL) {
|
|
pText = szEmpty;
|
|
}
|
|
|
|
/*
|
|
* MessageBoxTimeout assumes the timeout value is in milliseconds,
|
|
* but WinStationSendMessageW uses seconds.
|
|
*/
|
|
if (dwTimeout != INFINITE) {
|
|
dwTimeout /= 1000;
|
|
}
|
|
bResult = WinStationSendMessageW(SERVERNAME_CURRENT,
|
|
ClientSessionId,
|
|
(LPWSTR)pCaption,
|
|
wcslen(pCaption) * sizeof(WCHAR),
|
|
(LPWSTR)pText,
|
|
wcslen(pText) * sizeof(WCHAR),
|
|
wType,
|
|
dwTimeout,
|
|
&Response,
|
|
FALSE // always wait
|
|
);
|
|
if (bResult != TRUE) {
|
|
Response = aidReturn[ResponseNotHandled];
|
|
} else {
|
|
if (Response == IDTIMEOUT || Response == IDERROR) {
|
|
Response = aidReturn[ResponseNotHandled];
|
|
}
|
|
}
|
|
|
|
return (int)Response;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* MessageBox is for this session, go call CSR.
|
|
*/
|
|
RtlInitUnicodeString(&Text, pText);
|
|
RtlInitUnicodeString(&Caption, pCaption);
|
|
Parameters[0] = (ULONG_PTR)&Text;
|
|
Parameters[1] = (ULONG_PTR)&Caption;
|
|
Parameters[2] = wType;
|
|
Parameters[3] = dwTimeout;
|
|
|
|
/*
|
|
* Compatibility: Pass the override bit to make sure this box always shows
|
|
*/
|
|
Status = NtRaiseHardError(STATUS_SERVICE_NOTIFICATION | HARDERROR_OVERRIDE_ERRORMODE,
|
|
ARRAY_SIZE(Parameters),
|
|
3,
|
|
Parameters,
|
|
OptionOk,
|
|
&Response);
|
|
|
|
if (!NT_SUCCESS(Status)) {
|
|
RIPNTERR0(Status, RIP_VERBOSE, "");
|
|
}
|
|
|
|
return aidReturn[Response];
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* MessageBox (API)
|
|
*
|
|
* History:
|
|
* 11-20-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
int MessageBoxA(
|
|
HWND hwndOwner,
|
|
LPCSTR lpszText,
|
|
LPCSTR lpszCaption,
|
|
UINT wStyle)
|
|
{
|
|
EMIGETRETURNADDRESS();
|
|
return MessageBoxExA(hwndOwner, lpszText, lpszCaption, wStyle, 0);
|
|
}
|
|
|
|
|
|
int MessageBoxW(
|
|
HWND hwndOwner,
|
|
LPCWSTR lpszText,
|
|
LPCWSTR lpszCaption,
|
|
UINT wStyle)
|
|
{
|
|
EMIGETRETURNADDRESS();
|
|
return MessageBoxExW(hwndOwner, lpszText, lpszCaption, wStyle, 0);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* MessageBoxEx (API)
|
|
*
|
|
* History:
|
|
* 11-20-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
int MessageBoxExA(
|
|
HWND hwndOwner,
|
|
LPCSTR lpszText,
|
|
LPCSTR lpszCaption,
|
|
UINT wStyle,
|
|
WORD wLanguageId)
|
|
{
|
|
return MessageBoxTimeoutA(hwndOwner,
|
|
lpszText,
|
|
lpszCaption,
|
|
wStyle,
|
|
wLanguageId,
|
|
INFINITE);
|
|
}
|
|
|
|
|
|
int MessageBoxExW(
|
|
HWND hwndOwner,
|
|
LPCWSTR lpszText,
|
|
LPCWSTR lpszCaption,
|
|
UINT wStyle,
|
|
WORD wLanguageId)
|
|
{
|
|
return MessageBoxTimeoutW(hwndOwner,
|
|
lpszText,
|
|
lpszCaption,
|
|
wStyle,
|
|
wLanguageId,
|
|
INFINITE);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MessageBoxTimeout (API)
|
|
*
|
|
* History:
|
|
* 04-28-2001 JasonSch Wrote it.
|
|
\***************************************************************************/
|
|
int MessageBoxTimeoutW(
|
|
HWND hwndOwner,
|
|
LPCWSTR lpszText,
|
|
LPCWSTR lpszCaption,
|
|
UINT wStyle,
|
|
WORD wLanguageId,
|
|
DWORD dwTimeout)
|
|
{
|
|
MSGBOXDATA MsgBoxParams;
|
|
|
|
#if DBG
|
|
/*
|
|
* MB_USERICON is valid for MessageBoxIndirect only.
|
|
* MessageBoxWorker validates the other style bits
|
|
*/
|
|
if (wStyle & MB_USERICON) {
|
|
RIPMSG0(RIP_WARNING, "MessageBoxExW: Invalid flag: MB_USERICON");
|
|
}
|
|
#endif
|
|
|
|
RtlZeroMemory(&MsgBoxParams, sizeof(MsgBoxParams));
|
|
MsgBoxParams.cbSize = sizeof(MSGBOXPARAMS);
|
|
MsgBoxParams.hwndOwner = hwndOwner;
|
|
MsgBoxParams.hInstance = NULL;
|
|
MsgBoxParams.lpszText = lpszText;
|
|
MsgBoxParams.lpszCaption = lpszCaption;
|
|
MsgBoxParams.dwStyle = wStyle;
|
|
MsgBoxParams.wLanguageId = wLanguageId;
|
|
MsgBoxParams.dwTimeout = dwTimeout;
|
|
|
|
EMIGETRETURNADDRESS();
|
|
return MessageBoxWorker(&MsgBoxParams);
|
|
}
|
|
|
|
int MessageBoxTimeoutA(
|
|
HWND hwndOwner,
|
|
LPCSTR lpszText,
|
|
LPCSTR lpszCaption,
|
|
UINT wStyle,
|
|
WORD wLanguageId,
|
|
DWORD dwTimeout)
|
|
{
|
|
int retval;
|
|
LPWSTR lpwszText = NULL;
|
|
LPWSTR lpwszCaption = NULL;
|
|
|
|
if (lpszText) {
|
|
if (!MBToWCS(lpszText, -1, &lpwszText, -1, TRUE))
|
|
return 0;
|
|
}
|
|
|
|
if (lpszCaption) {
|
|
if (!MBToWCS(lpszCaption, -1, &lpwszCaption, -1, TRUE)) {
|
|
UserLocalFree(lpwszText);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
EMIGETRETURNADDRESS();
|
|
retval = MessageBoxTimeoutW(hwndOwner,
|
|
lpwszText,
|
|
lpwszCaption,
|
|
wStyle,
|
|
wLanguageId,
|
|
dwTimeout);
|
|
|
|
UserLocalFree(lpwszText);
|
|
if (lpwszCaption) {
|
|
UserLocalFree(lpwszCaption);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
#define MessageBoxIndirectInit(MsgBoxParams, lpmbp) \
|
|
do { \
|
|
if (lpmbp->cbSize != sizeof(MSGBOXPARAMS)) { \
|
|
RIPMSG1(RIP_WARNING, \
|
|
"MessageBoxIndirect: Invalid cbSize 0x%x", \
|
|
lpmbp->cbSize); \
|
|
} \
|
|
\
|
|
RtlZeroMemory(&MsgBoxParams, sizeof(MSGBOXDATA)); \
|
|
RtlCopyMemory(&MsgBoxParams, lpmbp, sizeof(MSGBOXPARAMS)); \
|
|
} while (FALSE);
|
|
|
|
/**************************************************************************\
|
|
* MessageBoxIndirect (API)
|
|
*
|
|
* 09-30-1994 FritzS Created.
|
|
\**************************************************************************/
|
|
int MessageBoxIndirectA(
|
|
CONST MSGBOXPARAMSA *lpmbp)
|
|
{
|
|
int retval;
|
|
MSGBOXDATA MsgBoxParams;
|
|
LPWSTR lpwszText = NULL;
|
|
LPWSTR lpwszCaption = NULL;
|
|
|
|
MessageBoxIndirectInit(MsgBoxParams, lpmbp);
|
|
|
|
if (IS_PTR(MsgBoxParams.lpszText)) {
|
|
if (!MBToWCS((LPSTR)MsgBoxParams.lpszText, -1, &lpwszText, -1, TRUE)) {
|
|
return 0;
|
|
}
|
|
MsgBoxParams.lpszText = lpwszText;
|
|
}
|
|
if (IS_PTR(MsgBoxParams.lpszCaption)) {
|
|
if (!MBToWCS((LPSTR)MsgBoxParams.lpszCaption, -1, &lpwszCaption, -1, TRUE)) {
|
|
UserLocalFree(lpwszText);
|
|
return 0;
|
|
}
|
|
MsgBoxParams.lpszCaption = lpwszCaption;
|
|
}
|
|
|
|
EMIGETRETURNADDRESS();
|
|
retval = MessageBoxWorker(&MsgBoxParams);
|
|
|
|
if (lpwszText) {
|
|
UserLocalFree(lpwszText);
|
|
}
|
|
if (lpwszCaption) {
|
|
UserLocalFree(lpwszCaption);
|
|
}
|
|
|
|
return retval;
|
|
}
|
|
|
|
int MessageBoxIndirectW(
|
|
CONST MSGBOXPARAMSW *lpmbp)
|
|
{
|
|
MSGBOXDATA MsgBoxParams;
|
|
|
|
MessageBoxIndirectInit(MsgBoxParams, lpmbp);
|
|
|
|
EMIGETRETURNADDRESS();
|
|
return MessageBoxWorker(&MsgBoxParams);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MessageBoxWorker (API)
|
|
*
|
|
* History:
|
|
* 03-10-93 JohnL Created
|
|
\***************************************************************************/
|
|
|
|
int MessageBoxWorker(
|
|
LPMSGBOXDATA pMsgBoxParams)
|
|
{
|
|
DWORD dwStyle = pMsgBoxParams->dwStyle;
|
|
UINT wBtnCnt;
|
|
UINT wDefButton;
|
|
UINT i;
|
|
UINT wBtnBeg;
|
|
WCHAR szErrorBuf[64];
|
|
LPWSTR apstrButton[4];
|
|
int aidButton[4];
|
|
BOOL fCancel = FALSE;
|
|
int retValue;
|
|
PMBSTRING pMBString;
|
|
|
|
#if DBG
|
|
if (dwStyle & ~MB_VALID) {
|
|
RIPMSG2(RIP_WARNING, "MessageBoxWorker: Invalid flags, %#lx & ~%#lx != 0",
|
|
dwStyle, MB_VALID);
|
|
}
|
|
#endif
|
|
|
|
/*
|
|
* dwTimeout == 0 means wait forever. This is mostly for compat reasons.
|
|
*/
|
|
if (pMsgBoxParams->dwTimeout == 0) {
|
|
pMsgBoxParams->dwTimeout = INFINITE;
|
|
}
|
|
|
|
/*
|
|
* Be sure that MBStrings are already loaded.
|
|
*/
|
|
UserAssert(gpsi->MBStrings[0].szName[0] != TEXT('\0'));
|
|
|
|
#ifdef _JANUS_
|
|
/*
|
|
* Error message instrument start here
|
|
* Check EMI enable
|
|
*/
|
|
|
|
if (gfEMIEnable) {
|
|
if (!ErrorMessageInst(pMsgBoxParams))
|
|
RIPMSG0(RIP_WARNING, "MessageBoxWorker: Fail to instrument error msg");
|
|
};
|
|
|
|
/*
|
|
* Default Message Return: on unattended systems the default button
|
|
* can be returned automatically without putting up the message box
|
|
*/
|
|
|
|
if (gfDMREnable) {
|
|
/*
|
|
* validate the style and default button as in the main code path
|
|
*/
|
|
|
|
/*
|
|
* Validate the "type" of message box requested.
|
|
*/
|
|
if ((dwStyle & MB_TYPEMASK) > MB_LASTVALIDTYPE) {
|
|
RIPERR0(ERROR_INVALID_MSGBOX_STYLE, RIP_VERBOSE, "");
|
|
return 0;
|
|
}
|
|
|
|
wBtnCnt = mpTypeCcmd[dwStyle & MB_TYPEMASK] +
|
|
((dwStyle & MB_HELP) ? 1 : 0);
|
|
|
|
/*
|
|
* Set the default button value
|
|
*/
|
|
wDefButton = (dwStyle & (UINT)MB_DEFMASK) / (UINT)(MB_DEFMASK & (MB_DEFMASK >> 3));
|
|
|
|
if (wDefButton >= wBtnCnt) /* Check if valid */
|
|
wDefButton = 0; /* Set the first button if error */
|
|
|
|
/*
|
|
* return the default button
|
|
*/
|
|
|
|
wBtnBeg = mpTypeIich[dwStyle & (UINT)MB_TYPEMASK];
|
|
pMBString = &gpsi->MBStrings[ SEBbuttons[wBtnBeg + wDefButton] ];
|
|
return pMBString->uID;
|
|
}
|
|
#endif // _JANUS_
|
|
|
|
/*
|
|
* If lpszCaption is NULL, then use "Error!" string as the caption
|
|
* string.
|
|
* LATER: IanJa localize according to wLanguageId
|
|
*/
|
|
if (pMsgBoxParams->lpszCaption == NULL) {
|
|
/*
|
|
* Load the default error string if we haven't done it yet
|
|
*/
|
|
if (*szERROR == 0) {
|
|
LoadStringW(hmodUser, STR_ERROR, szERROR, ARRAY_SIZE(szERROR));
|
|
}
|
|
if (pMsgBoxParams->wLanguageId == 0) {
|
|
pMsgBoxParams->lpszCaption = szERROR;
|
|
} else {
|
|
LoadStringOrError(hmodUser,
|
|
STR_ERROR,
|
|
szErrorBuf,
|
|
sizeof(szErrorBuf)/sizeof(WCHAR),
|
|
pMsgBoxParams->wLanguageId);
|
|
|
|
/*
|
|
* If it didn't find the string, use the default language
|
|
*/
|
|
if (*szErrorBuf) {
|
|
pMsgBoxParams->lpszCaption = szErrorBuf;
|
|
} else {
|
|
pMsgBoxParams->lpszCaption = szERROR;
|
|
|
|
RIPMSG1(RIP_WARNING, "MessageBoxWorker: STR_ERROR string resource for language %#lx not found",
|
|
pMsgBoxParams->wLanguageId);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* MB_SERVICE_NOTIFICATION had to be redefined because
|
|
* Win95 defined MB_TOPMOST using the same value.
|
|
* So for old apps, we map it to the new value
|
|
*/
|
|
|
|
if ((dwStyle & MB_TOPMOST) && !Is400Compat(GetClientInfo()->dwExpWinVer)) {
|
|
dwStyle &= ~MB_TOPMOST;
|
|
dwStyle |= MB_SERVICE_NOTIFICATION;
|
|
pMsgBoxParams->dwStyle = dwStyle;
|
|
|
|
RIPMSG1(RIP_WARNING, "MessageBoxWorker: MB_SERVICE_NOTIFICATION flag mapped. New dwStyle:%#lx", dwStyle);
|
|
}
|
|
|
|
/*
|
|
* For backward compatiblity, use MB_SERVICE_NOTIFICATION if
|
|
* it's going to the default desktop.
|
|
*/
|
|
if (dwStyle & (MB_DEFAULT_DESKTOP_ONLY | MB_SERVICE_NOTIFICATION)) {
|
|
|
|
/*
|
|
* Allow services to put up popups without getting
|
|
* access to the current desktop.
|
|
*/
|
|
if (pMsgBoxParams->hwndOwner != NULL) {
|
|
RIPERR0(ERROR_INVALID_PARAMETER, RIP_VERBOSE, "");
|
|
return 0;
|
|
}
|
|
|
|
return ServiceMessageBox(pMsgBoxParams->lpszText,
|
|
pMsgBoxParams->lpszCaption,
|
|
dwStyle & ~MB_SERVICE_NOTIFICATION,
|
|
pMsgBoxParams->dwTimeout);
|
|
}
|
|
|
|
/*
|
|
* Make sure we have a valid window handle.
|
|
*/
|
|
if (pMsgBoxParams->hwndOwner && !IsWindow(pMsgBoxParams->hwndOwner)) {
|
|
RIPERR0(ERROR_INVALID_WINDOW_HANDLE, RIP_VERBOSE, "");
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Validate the "type" of message box requested.
|
|
*/
|
|
if ((dwStyle & MB_TYPEMASK) > MB_LASTVALIDTYPE) {
|
|
RIPERR0(ERROR_INVALID_MSGBOX_STYLE, RIP_VERBOSE, "");
|
|
return 0;
|
|
}
|
|
|
|
wBtnCnt = mpTypeCcmd[dwStyle & MB_TYPEMASK] +
|
|
((dwStyle & MB_HELP) ? 1 : 0);
|
|
|
|
/*
|
|
* Set the default button value
|
|
*/
|
|
wDefButton = (dwStyle & (UINT)MB_DEFMASK) / (UINT)(MB_DEFMASK & (MB_DEFMASK >> 3));
|
|
|
|
if (wDefButton >= wBtnCnt) /* Check if valid */
|
|
wDefButton = 0; /* Set the first button if error */
|
|
|
|
/*
|
|
* Calculate the strings to use in the message box
|
|
*/
|
|
wBtnBeg = mpTypeIich[dwStyle & (UINT)MB_TYPEMASK];
|
|
for (i=0; i<wBtnCnt; i++) {
|
|
|
|
pMBString = &gpsi->MBStrings[SEBbuttons[wBtnBeg + i]];
|
|
/*
|
|
* Pick up the string for the button.
|
|
*/
|
|
if (pMsgBoxParams->wLanguageId == 0) {
|
|
apstrButton[i] = KPWSTR_TO_PWSTR(pMBString->szName);
|
|
} else {
|
|
WCHAR szButtonBuf[64];
|
|
// LATER is it possible to have button text greater than 64 chars
|
|
|
|
/*
|
|
* BUG: gpsi->wMaxBtnSize might be too short for the length of this string...
|
|
*/
|
|
LoadStringOrError(hmodUser,
|
|
pMBString->uStr,
|
|
szButtonBuf,
|
|
ARRAY_SIZE(szButtonBuf),
|
|
pMsgBoxParams->wLanguageId);
|
|
|
|
/*
|
|
* If it didn't find the string, use the default language.
|
|
*/
|
|
if (*szButtonBuf) {
|
|
apstrButton[i] = TextAlloc(szButtonBuf);
|
|
} else {
|
|
apstrButton[i] = TextAlloc(KPWSTR_TO_PWSTR(pMBString->szName));
|
|
|
|
RIPMSG2(RIP_WARNING, "MessageBoxWorker: string resource %#lx for language %#lx not found",
|
|
pMBString->uStr,
|
|
pMsgBoxParams->wLanguageId);
|
|
}
|
|
}
|
|
aidButton[i] = pMBString->uID;
|
|
if (aidButton[i] == IDCANCEL) {
|
|
fCancel = TRUE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Hackery: There are some apps that use MessageBox as initial error
|
|
* indicators, such as mplay32, and we want this messagebox to be
|
|
* visible regardless of waht was specified in the StartupInfo->wShowWindow
|
|
* field. ccMail for instance starts all of its embedded objects hidden
|
|
* but on win 3.1 the error message would show because they don't have
|
|
* the startup info.
|
|
*/
|
|
NtUserModifyUserStartupInfoFlags(STARTF_USESHOWWINDOW, 0);
|
|
|
|
pMsgBoxParams->pidButton = aidButton;
|
|
pMsgBoxParams->ppszButtonText = apstrButton;
|
|
pMsgBoxParams->DefButton = wDefButton;
|
|
pMsgBoxParams->cButtons = wBtnCnt;
|
|
pMsgBoxParams->CancelId = ((dwStyle & MB_TYPEMASK) == 0) ? IDOK : (fCancel ? IDCANCEL : 0);
|
|
retValue = SoftModalMessageBox(pMsgBoxParams);
|
|
|
|
if (pMsgBoxParams->wLanguageId != 0) {
|
|
for (i = 0; i < wBtnCnt; i++) {
|
|
UserLocalFree(apstrButton[i]);
|
|
}
|
|
}
|
|
|
|
return retValue;
|
|
}
|
|
|
|
#define MAX_RES_STRING 256
|
|
|
|
/***************************************************************************\
|
|
* SoftModalMessageBox
|
|
\***************************************************************************/
|
|
int SoftModalMessageBox(
|
|
LPMSGBOXDATA lpmb)
|
|
{
|
|
LPBYTE lpDlgTmp;
|
|
int cyIcon, cxIcon;
|
|
int cxButtons;
|
|
int cxMBMax;
|
|
int cxText, cyText, xText;
|
|
int cxBox, cyBox;
|
|
int cxFoo, cxCaption;
|
|
int xMB, yMB;
|
|
HDC hdc;
|
|
DWORD wIconOrdNum;
|
|
DWORD wCaptionLen;
|
|
DWORD wTextLen;
|
|
WORD OrdNum[2]; // Must be an array or WORDs
|
|
RECT rc;
|
|
RECT rcWork;
|
|
HCURSOR hcurOld;
|
|
DWORD dwStyleMsg, dwStyleText;
|
|
DWORD dwExStyleMsg = 0;
|
|
DWORD dwStyleDlg;
|
|
HWND hwndOwner;
|
|
LPWSTR lpsz;
|
|
int iRetVal = 0;
|
|
HICON hIcon;
|
|
HGLOBAL hTemplate = NULL;
|
|
HGLOBAL hCaption = NULL;
|
|
HGLOBAL hText = NULL;
|
|
HINSTANCE hInstMsg = lpmb->hInstance;
|
|
SIZE size;
|
|
HFONT hFontOld = NULL;
|
|
int cntMBox;
|
|
PMONITOR pMonitor;
|
|
|
|
ConnectIfNecessary(0);
|
|
|
|
dwStyleMsg = lpmb->dwStyle;
|
|
|
|
if (dwStyleMsg & MB_RIGHT) {
|
|
dwExStyleMsg |= WS_EX_RIGHT;
|
|
}
|
|
|
|
if (!IS_PTR(lpmb->lpszCaption)) {
|
|
/*
|
|
* Won't ever be NULL because MessageBox sticks "Error!" on error.
|
|
*/
|
|
if (hInstMsg && (hCaption = UserLocalAlloc(HEAP_ZERO_MEMORY, MAX_RES_STRING * sizeof(WCHAR)))) {
|
|
lpsz = (LPWSTR)hCaption;
|
|
LoadString(hInstMsg, PTR_TO_ID(lpmb->lpszCaption), lpsz, MAX_RES_STRING);
|
|
} else {
|
|
lpsz = NULL;
|
|
}
|
|
|
|
lpmb->lpszCaption = lpsz ? lpsz : szEmpty;
|
|
}
|
|
|
|
if (!IS_PTR(lpmb->lpszText)) {
|
|
// NULL not allowed
|
|
if (hInstMsg && (hText = UserLocalAlloc(HEAP_ZERO_MEMORY, MAX_RES_STRING * sizeof(WCHAR)))) {
|
|
lpsz = (LPWSTR)hText;
|
|
LoadString(hInstMsg, PTR_TO_ID(lpmb->lpszText), lpsz, MAX_RES_STRING);
|
|
} else {
|
|
lpsz = NULL;
|
|
}
|
|
|
|
lpmb->lpszText = lpsz ? lpsz : szEmpty;
|
|
}
|
|
|
|
//
|
|
// Mirroring of MessageBox'es is only enabled if :-
|
|
//
|
|
// * MB_RTLREADING style has been specified in the MessageBox styles OR
|
|
// * The first two code points of the MessageBox text are Right-To-Left
|
|
// marks (RLMs = U+200f).
|
|
// The feature of enable RTL mirroring if two consecutive RLMs are found
|
|
// in the MB text is to acheive a no-code-change for localization of
|
|
// of MessageBoxes for BiDi Apps. [samera]
|
|
//
|
|
if ((dwStyleMsg & MB_RTLREADING) ||
|
|
(lpmb->lpszText != NULL && (lpmb->lpszText[0] == UNICODE_RLM) &&
|
|
(lpmb->lpszText[1] == UNICODE_RLM))) {
|
|
//
|
|
// Set Mirroring so that MessageBox and its child controls
|
|
// get mirrored. Otherwise, the message box and its child controls
|
|
// are Left-To-Right.
|
|
//
|
|
dwExStyleMsg |= WS_EX_LAYOUTRTL;
|
|
|
|
//
|
|
// And turn off any conflicting flags.
|
|
//
|
|
dwExStyleMsg &= ~WS_EX_RIGHT;
|
|
if (dwStyleMsg & MB_RTLREADING) {
|
|
dwStyleMsg &= ~MB_RTLREADING;
|
|
dwStyleMsg ^= MB_RIGHT;
|
|
}
|
|
}
|
|
|
|
if ((dwStyleMsg & MB_ICONMASK) == MB_USERICON)
|
|
hIcon = LoadIcon(hInstMsg, lpmb->lpszIcon);
|
|
else
|
|
hIcon = NULL;
|
|
|
|
// For compatibility reasons, we still allow the message box to come up.
|
|
hwndOwner = lpmb->hwndOwner;
|
|
|
|
// For PowerBuilder4.0, we must make their messageboxes owned popups. Or, else
|
|
// they get WM_ACTIVATEAPP and they install multiple keyboard hooks and get into
|
|
// infinite loop later.
|
|
// Bug #15896 -- WIN95B -- 2/17/95 -- SANKAR --
|
|
if (!hwndOwner)
|
|
{
|
|
WCHAR pwszLibFileName[MAX_PATH];
|
|
static WCHAR szPB040[] = L"PB040"; // Module name of PowerBuilder4.0
|
|
WCHAR *pw1;
|
|
|
|
//Is this a win3.1 or older app?
|
|
if (!Is400Compat(GETAPPVER())) {
|
|
if (GetModuleFileName(NULL, pwszLibFileName, sizeof(pwszLibFileName)/sizeof(WCHAR)) == 0) goto getthedc;
|
|
pw1 = pwszLibFileName + wcslen(pwszLibFileName) - 1;
|
|
while (pw1 > pwszLibFileName) {
|
|
if (*pw1 == TEXT('.')) *pw1-- = 0;
|
|
else if (*pw1 == TEXT(':')) {pw1++; break;}
|
|
else if (*pw1 == TEXT('\\')) {pw1++; break;}
|
|
else pw1--;
|
|
}
|
|
// Is this the PowerBuilder 4.0 module?
|
|
if (!_wcsicmp(pw1, szPB040))
|
|
hwndOwner = NtUserGetForegroundWindow(); // Make the MsgBox owned.
|
|
}
|
|
}
|
|
getthedc:
|
|
// Check if we're out of cache DCs until robustness...
|
|
if (!(hdc = NtUserGetDCEx(NULL, NULL, DCX_WINDOW | DCX_CACHE))) {
|
|
|
|
/*
|
|
* The above call might fail for TIF_RESTRICTED processes
|
|
* so check for the DC from the owner window
|
|
*/
|
|
if (!(hdc = NtUserGetDCEx(hwndOwner, NULL, DCX_WINDOW | DCX_CACHE)))
|
|
goto SMB_Exit;
|
|
}
|
|
|
|
// Figure out the types and dimensions of buttons
|
|
|
|
cxButtons = (lpmb->cButtons * gpsi->wMaxBtnSize) + ((lpmb->cButtons - 1) * XPixFromXDU(DU_BTNGAP, gpsi->cxMsgFontChar));
|
|
|
|
// Ditto for the icon, if there is one. If not, cxIcon & cyIcon are 0.
|
|
|
|
if (wIconOrdNum = MB_GetIconOrdNum(dwStyleMsg)) {
|
|
cxIcon = SYSMET(CXICON) + XPixFromXDU(DU_INNERMARGIN, gpsi->cxMsgFontChar);
|
|
cyIcon = SYSMET(CYICON);
|
|
} else
|
|
cxIcon = cyIcon = 0;
|
|
|
|
hFontOld = SelectObject(hdc, KHFONT_TO_HFONT(gpsi->hCaptionFont));
|
|
|
|
// Find the max between the caption text and the buttons
|
|
wCaptionLen = wcslen(lpmb->lpszCaption);
|
|
GetTextExtentPoint(hdc, lpmb->lpszCaption, wCaptionLen, &size);
|
|
cxCaption = size.cx + 2*SYSMET(CXSIZE);
|
|
|
|
//
|
|
// The max width of the message box is 5/8 of the work area for most
|
|
// countries. We will then try 6/8 and 7/8 if it won't fit. Then
|
|
// we will use whole screen.
|
|
//
|
|
pMonitor = GetDialogMonitor(hwndOwner, MONITOR_DEFAULTTOPRIMARY);
|
|
CopyRect(&rcWork, KPRECT_TO_PRECT(&pMonitor->rcWork));
|
|
cxMBMax = MultDiv(rcWork.right - rcWork.left, 5, 8);
|
|
|
|
cxFoo = 2*XPixFromXDU(DU_OUTERMARGIN, gpsi->cxMsgFontChar);
|
|
|
|
SelectObject(hdc, KHFONT_TO_HFONT(gpsi->hMsgFont));
|
|
|
|
//
|
|
// If the text doesn't fit in 5/8, try 7/8 of the screen
|
|
//
|
|
ReSize:
|
|
//
|
|
// The message box is as big as needed to hold the caption/text/buttons,
|
|
// but not bigger than the maximum width.
|
|
//
|
|
|
|
cxBox = cxMBMax - 2*SYSMET(CXFIXEDFRAME);
|
|
|
|
// Ask DrawText for the right cx and cy
|
|
rc.left = 0;
|
|
rc.top = 0;
|
|
rc.right = cxBox - cxFoo - cxIcon;
|
|
rc.bottom = rcWork.bottom - rcWork.top;
|
|
cyText = DrawTextExW(hdc, (LPWSTR)lpmb->lpszText, -1, &rc,
|
|
DT_CALCRECT | DT_WORDBREAK | DT_EXPANDTABS |
|
|
DT_NOPREFIX | DT_EXTERNALLEADING | DT_EDITCONTROL, NULL);
|
|
//
|
|
// Make sure we have enough width to hold the buttons, in addition to
|
|
// the icon+text. Always force the buttons. If they don't fit, it's
|
|
// because the working area is small.
|
|
//
|
|
//
|
|
// The buttons are centered underneath the icon/text.
|
|
//
|
|
cxText = rc.right - rc.left + cxIcon + cxFoo;
|
|
cxBox = min(cxBox, max(cxText, cxCaption));
|
|
cxBox = max(cxBox, cxButtons + cxFoo);
|
|
cxText = cxBox - cxFoo - cxIcon;
|
|
|
|
//
|
|
// Now we know the text width for sure. Really calculate how high the
|
|
// text will be.
|
|
//
|
|
rc.left = 0;
|
|
rc.top = 0;
|
|
rc.right = cxText;
|
|
rc.bottom = rcWork.bottom - rcWork.top;
|
|
cyText = DrawTextExW(hdc, (LPWSTR)lpmb->lpszText, -1, &rc, DT_CALCRECT | DT_WORDBREAK
|
|
| DT_EXPANDTABS | DT_NOPREFIX | DT_EXTERNALLEADING | DT_EDITCONTROL, NULL);
|
|
|
|
// Find the window size.
|
|
cxBox += 2*SYSMET(CXFIXEDFRAME);
|
|
cyBox = 2*SYSMET(CYFIXEDFRAME) + SYSMET(CYCAPTION) + YPixFromYDU(2*DU_OUTERMARGIN +
|
|
DU_INNERMARGIN + DU_BTNHEIGHT, gpsi->cyMsgFontChar);
|
|
|
|
cyBox += max(cyIcon, cyText);
|
|
|
|
//
|
|
// If the message box doesn't fit on the working area, we'll try wider
|
|
// sizes successively: 6/8 of work then 7/8 of screen.
|
|
//
|
|
if (cyBox > rcWork.bottom - rcWork.top) {
|
|
int cxTemp;
|
|
|
|
cxTemp = MultDiv(rcWork.right - rcWork.left, 6, 8);
|
|
|
|
if (cxMBMax == MultDiv(rcWork.right - rcWork.left, 5, 8)) {
|
|
cxMBMax = cxTemp;
|
|
goto ReSize;
|
|
} else if (cxMBMax == cxTemp) {
|
|
// then let's try with rcMonitor
|
|
CopyRect(&rcWork, KPRECT_TO_PRECT(&pMonitor->rcMonitor));
|
|
cxMBMax = MultDiv(rcWork.right - rcWork.left, 7, 8);
|
|
goto ReSize;
|
|
}
|
|
}
|
|
|
|
if (hFontOld) {
|
|
SelectFont(hdc, hFontOld);
|
|
}
|
|
NtUserReleaseDC(NULL, hdc);
|
|
|
|
// Find the window position
|
|
cntMBox = GetClientInfo()->pDeskInfo->cntMBox;
|
|
|
|
xMB = (rcWork.left + rcWork.right - cxBox) / 2 + (cntMBox * SYSMET(CXSIZE));
|
|
xMB = max(xMB, rcWork.left);
|
|
yMB = (rcWork.top + rcWork.bottom - cyBox) / 2 + (cntMBox * SYSMET(CYSIZE));
|
|
yMB = max(yMB, rcWork.top);
|
|
|
|
//
|
|
// Bottom, right justify if we're going off the screen--but leave a
|
|
// little gap.
|
|
//
|
|
if (xMB + cxBox > rcWork.right) {
|
|
xMB = rcWork.right - SYSMET(CXEDGE) - cxBox;
|
|
}
|
|
|
|
//
|
|
// Pin to the working area. If it won't fit, then pin to the screen
|
|
// height. Bottom justify it at least if too big even for that, so
|
|
// that the buttons are visible.
|
|
//
|
|
if (yMB + cyBox > rcWork.bottom) {
|
|
yMB = rcWork.bottom - SYSMET(CYEDGE) - cyBox;
|
|
if (yMB < rcWork.top) {
|
|
yMB = pMonitor->rcMonitor.bottom - SYSMET(CYEDGE) - cyBox;
|
|
}
|
|
}
|
|
|
|
wTextLen = wcslen(lpmb->lpszText);
|
|
|
|
// Find out the memory required for the Dlg template and try to alloc it
|
|
hTemplate = UserLocalAlloc(HEAP_ZERO_MEMORY, MB_FindDlgTemplateSize(lpmb));
|
|
if (!hTemplate) {
|
|
goto SMB_Exit;
|
|
}
|
|
|
|
lpDlgTmp = (LPBYTE)hTemplate;
|
|
|
|
//
|
|
// Setup the dialog style for the message box
|
|
//
|
|
dwStyleDlg = WS_POPUPWINDOW | WS_CAPTION | DS_ABSALIGN | DS_NOIDLEMSG |
|
|
DS_SETFONT | DS_3DLOOK;
|
|
|
|
if ((dwStyleMsg & MB_MODEMASK) == MB_SYSTEMMODAL) {
|
|
dwStyleDlg |= DS_SYSMODAL | DS_SETFOREGROUND;
|
|
} else {
|
|
dwStyleDlg |= DS_MODALFRAME | WS_SYSMENU;
|
|
}
|
|
|
|
if (dwStyleMsg & MB_SETFOREGROUND) {
|
|
dwStyleDlg |= DS_SETFOREGROUND;
|
|
}
|
|
|
|
// Add the Header of the Dlg Template
|
|
// BOGUS !!! don't ADD bools
|
|
lpDlgTmp = MB_UpdateDlgHdr((LPDLGTEMPLATE) lpDlgTmp, dwStyleDlg, dwExStyleMsg,
|
|
(BYTE) (lpmb->cButtons + (wIconOrdNum != 0) + (lpmb->lpszText != NULL)),
|
|
xMB, yMB, cxBox, cyBox, (LPWSTR)lpmb->lpszCaption, wCaptionLen);
|
|
|
|
//
|
|
// Center the buttons
|
|
//
|
|
|
|
cxFoo = (cxBox - 2*SYSMET(CXFIXEDFRAME) - cxButtons) / 2;
|
|
|
|
lpDlgTmp = MB_AddPushButtons((LPDLGITEMTEMPLATE)lpDlgTmp, lpmb, cxFoo,
|
|
cyBox - SYSMET(CYCAPTION) - (2 * SYSMET(CYFIXEDFRAME)) -
|
|
YPixFromYDU(DU_OUTERMARGIN, gpsi->cyMsgFontChar));
|
|
|
|
// Add Icon, if any, to the Dlg template
|
|
//
|
|
// The icon is always top justified. If the text is shorter than the
|
|
// height of the icon, we center it. Otherwise the text will start at
|
|
// the top.
|
|
//
|
|
if (wIconOrdNum) {
|
|
OrdNum[0] = 0xFFFF; // To indicate that an Ordinal number follows
|
|
OrdNum[1] = (WORD) wIconOrdNum;
|
|
|
|
lpDlgTmp = MB_UpdateDlgItem((LPDLGITEMTEMPLATE)lpDlgTmp, IDUSERICON, // Control Id
|
|
SS_ICON | WS_GROUP | WS_CHILD | WS_VISIBLE, 0,
|
|
XPixFromXDU(DU_OUTERMARGIN, gpsi->cxMsgFontChar), // X co-ordinate
|
|
YPixFromYDU(DU_OUTERMARGIN, gpsi->cyMsgFontChar), // Y co-ordinate
|
|
0, 0, // For Icons, CX and CY are ignored, can be zero
|
|
OrdNum, // Ordinal number of Icon
|
|
ARRAY_SIZE(OrdNum), // Length of OrdNum
|
|
STATICCODE);
|
|
}
|
|
|
|
// Add the Text of the Message to the Dlg Template
|
|
if (lpmb->lpszText) {
|
|
//
|
|
// Center the text if shorter than the icon.
|
|
//
|
|
if (cyText >= cyIcon)
|
|
cxFoo = 0;
|
|
else
|
|
cxFoo = (cyIcon - cyText) / 2;
|
|
|
|
dwStyleText = SS_NOPREFIX | WS_GROUP | WS_CHILD | WS_VISIBLE | SS_EDITCONTROL;
|
|
if (dwStyleMsg & MB_RIGHT) {
|
|
dwStyleText |= SS_RIGHT;
|
|
xText = cxBox - (SYSMET(CXSIZE) + cxText);
|
|
} else {
|
|
dwStyleText |= SS_LEFT;
|
|
xText = cxIcon + XPixFromXDU(DU_INNERMARGIN, gpsi->cxMsgFontChar);
|
|
}
|
|
|
|
MB_UpdateDlgItem((LPDLGITEMTEMPLATE)lpDlgTmp, -1, dwStyleText, dwExStyleMsg, xText,
|
|
YPixFromYDU(DU_OUTERMARGIN, gpsi->cyMsgFontChar) + cxFoo,
|
|
cxText, cyText,
|
|
(LPWSTR)lpmb->lpszText, wTextLen, STATICCODE);
|
|
}
|
|
|
|
// The dialog template is ready
|
|
|
|
//
|
|
// Set the normal cursor
|
|
//
|
|
hcurOld = NtUserSetCursor(LoadCursor(NULL, IDC_ARROW));
|
|
|
|
lpmb->lpszIcon = (LPWSTR) hIcon;
|
|
|
|
if (!(lpmb->dwStyle & MB_USERICON))
|
|
{
|
|
int wBeep = (LOWORD(lpmb->dwStyle & MB_ICONMASK)) >> MB_MASKSHIFT;
|
|
if (wBeep < USER_SOUND_MAX) {
|
|
NtUserCallOneParam(wBeep, SFI_PLAYEVENTSOUND);
|
|
}
|
|
}
|
|
|
|
iRetVal = (int)InternalDialogBox(hmodUser, hTemplate, hwndOwner,
|
|
MB_DlgProc, (LPARAM) lpmb, FALSE);
|
|
|
|
//
|
|
// Fix up return value
|
|
if (iRetVal == -1)
|
|
iRetVal = 0; /* Messagebox should also return error */
|
|
|
|
//
|
|
// If the messagebox contains only OK button, then its ID is changed as
|
|
// IDCANCEL in MB_DlgProc; So, we must change it back to IDOK irrespective
|
|
// of whether ESC is pressed or Carriage return is pressed;
|
|
//
|
|
if (((dwStyleMsg & MB_TYPEMASK) == MB_OK) && iRetVal)
|
|
iRetVal = IDOK;
|
|
|
|
|
|
//
|
|
// Restore the previous cursor
|
|
//
|
|
if (hcurOld)
|
|
NtUserSetCursor(hcurOld);
|
|
|
|
SMB_Exit:
|
|
if (hTemplate) {
|
|
UserLocalFree(hTemplate);
|
|
}
|
|
|
|
if (hCaption) {
|
|
UserLocalFree(hCaption);
|
|
}
|
|
|
|
if (hText) {
|
|
UserLocalFree(hText);
|
|
}
|
|
|
|
return iRetVal;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MB_CopyToClipboard
|
|
*
|
|
* Called in response to WM_COPY, it will save the title, message and button's
|
|
* texts to the clipboard in CF_UNICODETEXT format.
|
|
*
|
|
* ---------------------------
|
|
* Caption
|
|
* ---------------------------
|
|
* Text
|
|
* ---------------------------
|
|
* Button1 ... ButtonN
|
|
* ---------------------------
|
|
*
|
|
*
|
|
* History:
|
|
* 08-03-97 MCostea Created
|
|
\***************************************************************************/
|
|
VOID
|
|
MB_CopyToClipboard(
|
|
HWND hwndDlg)
|
|
{
|
|
LPCWSTR lpszRead;
|
|
LPWSTR lpszAll, lpszWrite;
|
|
HANDLE hData;
|
|
static CONST WCHAR szLine[] = L"---------------------------\r\n";
|
|
UINT cBufSize, i, cWrote;
|
|
LPMSGBOXDATA lpmb;
|
|
|
|
if (!(lpmb = (LPMSGBOXDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA))) {
|
|
return;
|
|
}
|
|
|
|
if (!OpenClipboard(hwndDlg)) {
|
|
return;
|
|
}
|
|
|
|
/*
|
|
* Calculate the buffer size:
|
|
* - the message text can be all \n, that will become \r\n
|
|
* - there are a few extra \r\n (that's why 8)
|
|
*/
|
|
cBufSize = (lpmb->lpszCaption ? wcslen(lpmb->lpszCaption) : 0) +
|
|
(lpmb->lpszText ? 2*wcslen(lpmb->lpszText) : 0) +
|
|
4*sizeof(szLine) +
|
|
lpmb->cButtons * gpsi->wMaxBtnSize +
|
|
8;
|
|
|
|
cBufSize *= sizeof(WCHAR);
|
|
|
|
if (!(hData = UserGlobalAlloc(LHND, (LONG)(cBufSize))) ) {
|
|
goto CloseClip;
|
|
}
|
|
|
|
USERGLOBALLOCK(hData, lpszAll);
|
|
UserAssert(lpszAll);
|
|
|
|
cWrote = wsprintf(lpszAll, L"%s%s\r\n%s",
|
|
szLine,
|
|
lpmb->lpszCaption ? lpmb->lpszCaption : L"",
|
|
szLine);
|
|
|
|
lpszWrite = lpszAll + cWrote;
|
|
lpszRead = lpmb->lpszText;
|
|
/*
|
|
* Change \n to \r\n in the text
|
|
*/
|
|
for (i = 0; *lpszRead; i++) {
|
|
|
|
if (*lpszRead == L'\n')
|
|
*lpszWrite++ = L'\r';
|
|
|
|
*lpszWrite++ = *lpszRead++;
|
|
}
|
|
|
|
cWrote = wsprintf(lpszWrite, L"\r\n%s", szLine);
|
|
lpszWrite += cWrote;
|
|
|
|
/*
|
|
* Remove & from the button texts
|
|
*/
|
|
for (i = 0; i<lpmb->cButtons; i++) {
|
|
|
|
lpszRead = lpmb->ppszButtonText[i];
|
|
while (*lpszRead) {
|
|
if (*lpszRead != L'&') {
|
|
*lpszWrite++ = *lpszRead;
|
|
}
|
|
lpszRead++;
|
|
}
|
|
*lpszWrite++ = L' ';
|
|
*lpszWrite++ = L' ';
|
|
*lpszWrite++ = L' ';
|
|
}
|
|
wsprintf(lpszWrite, L"\r\n%s\0", szLine);
|
|
|
|
USERGLOBALUNLOCK(hData);
|
|
|
|
NtUserEmptyClipboard();
|
|
/*
|
|
* If we just called EmptyClipboard in the context of a 16 bit
|
|
* app then we also have to tell WOW to nix its 16 handle copy of
|
|
* clipboard data. WOW does its own clipboard caching because
|
|
* some 16 bit apps use clipboard data even after the clipboard
|
|
* has been emptied. See the note in the server code.
|
|
*
|
|
* Note: this is another place (besides client\editec.c) where
|
|
* EmptyClipboard is called* for a 16 bit app not going through WOW.
|
|
* If we added others we might want to move this into EmptyClipboard
|
|
* and have two versions.
|
|
*/
|
|
if (GetClientInfo()->CI_flags & CI_16BIT) {
|
|
pfnWowEmptyClipBoard();
|
|
}
|
|
|
|
SetClipboardData(CF_UNICODETEXT, hData);
|
|
|
|
CloseClip:
|
|
NtUserCloseClipboard();
|
|
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MB_UpdateDlgHdr
|
|
*
|
|
* History:
|
|
* 11-20-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
LPBYTE MB_UpdateDlgHdr(
|
|
LPDLGTEMPLATE lpDlgTmp,
|
|
long lStyle,
|
|
long lExtendedStyle,
|
|
BYTE bItemCount,
|
|
int iX,
|
|
int iY,
|
|
int iCX,
|
|
int iCY,
|
|
LPWSTR lpszCaption,
|
|
int cchCaptionLen)
|
|
{
|
|
LPTSTR lpStr;
|
|
RECT rc;
|
|
|
|
/*
|
|
* Adjust the rectangle dimensions.
|
|
*/
|
|
rc.left = iX + SYSMET(CXFIXEDFRAME);
|
|
rc.top = iY + SYSMET(CYFIXEDFRAME);
|
|
rc.right = iX + iCX - SYSMET(CXFIXEDFRAME);
|
|
rc.bottom = iY + iCY - SYSMET(CYFIXEDFRAME);
|
|
|
|
|
|
/*
|
|
* Adjust for the caption.
|
|
*/
|
|
rc.top += SYSMET(CYCAPTION);
|
|
|
|
lpDlgTmp->style = lStyle;
|
|
lpDlgTmp->dwExtendedStyle = lExtendedStyle;
|
|
lpDlgTmp->cdit = bItemCount;
|
|
lpDlgTmp->x = XDUFromXPix(rc.left, gpsi->cxMsgFontChar);
|
|
lpDlgTmp->y = YDUFromYPix(rc.top, gpsi->cyMsgFontChar);
|
|
lpDlgTmp->cx = XDUFromXPix(rc.right - rc.left, gpsi->cxMsgFontChar);
|
|
lpDlgTmp->cy = YDUFromYPix(rc.bottom - rc.top, gpsi->cyMsgFontChar);
|
|
|
|
/*
|
|
* Move pointer to variable length fields. No menu resource for
|
|
* message box, a zero window class (means dialog box class).
|
|
*/
|
|
lpStr = (LPWSTR)(lpDlgTmp + 1);
|
|
*lpStr++ = 0; // Menu
|
|
lpStr = (LPWSTR)NextWordBoundary(lpStr);
|
|
*lpStr++ = 0; // Class
|
|
lpStr = (LPWSTR)NextWordBoundary(lpStr);
|
|
|
|
/*
|
|
* NOTE: iCaptionLen may be less than the length of the Caption string;
|
|
* So, DO NOT USE lstrcpy();
|
|
*/
|
|
RtlCopyMemory(lpStr, lpszCaption, cchCaptionLen*sizeof(WCHAR));
|
|
lpStr += cchCaptionLen;
|
|
*lpStr++ = TEXT('\0'); // Null terminate the caption str
|
|
|
|
/*
|
|
* Font height of 0x7FFF means use the message box font
|
|
*/
|
|
*lpStr++ = 0x7FFF;
|
|
|
|
return NextDWordBoundary(lpStr);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MB_AddPushButtons
|
|
*
|
|
* History:
|
|
* 11-20-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
LPBYTE MB_AddPushButtons(
|
|
LPDLGITEMTEMPLATE lpDlgTmp,
|
|
LPMSGBOXDATA lpmb,
|
|
UINT wLEdge,
|
|
UINT wBEdge)
|
|
{
|
|
UINT wYValue;
|
|
UINT i;
|
|
UINT wHeight;
|
|
UINT wCount = lpmb->cButtons;
|
|
|
|
wHeight = YPixFromYDU(DU_BTNHEIGHT, gpsi->cyMsgFontChar);
|
|
|
|
wYValue = wBEdge - wHeight; // Y co-ordinate for push buttons
|
|
|
|
for (i = 0; i < wCount; i++) {
|
|
|
|
lpDlgTmp = (LPDLGITEMTEMPLATE)MB_UpdateDlgItem(
|
|
lpDlgTmp, /* Ptr to template */
|
|
lpmb->pidButton[i], /* Control Id */
|
|
WS_TABSTOP | WS_CHILD | WS_VISIBLE | (i == 0 ? WS_GROUP : 0) |
|
|
((UINT)i == lpmb->DefButton ? BS_DEFPUSHBUTTON : BS_PUSHBUTTON),
|
|
0,
|
|
wLEdge, /* X co-ordinate */
|
|
wYValue, /* Y co-ordinate */
|
|
gpsi->wMaxBtnSize, /* CX */
|
|
wHeight, /* CY */
|
|
lpmb->ppszButtonText[i], /* String for button */
|
|
(UINT)wcslen(lpmb->ppszButtonText[i]),/* Length */
|
|
BUTTONCODE);
|
|
|
|
/*
|
|
* Get the X co-ordinate for the next Push button
|
|
*/
|
|
wLEdge += gpsi->wMaxBtnSize + XPixFromXDU(DU_BTNGAP, gpsi->cxMsgFontChar);
|
|
}
|
|
|
|
return (LPBYTE)lpDlgTmp;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MB_UpdateDlgItem
|
|
*
|
|
* History:
|
|
* 11-20-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
LPBYTE MB_UpdateDlgItem(
|
|
LPDLGITEMTEMPLATE lpDlgItem,
|
|
int iCtrlId,
|
|
long lStyle,
|
|
long lExtendedStyle,
|
|
int iX,
|
|
int iY,
|
|
int iCX,
|
|
int iCY,
|
|
LPWSTR lpszText,
|
|
UINT cchTextLen,
|
|
int iControlClass)
|
|
{
|
|
LPWSTR lpStr;
|
|
BOOL fIsOrdNum;
|
|
|
|
|
|
lpDlgItem->x = XDUFromXPix(iX, gpsi->cxMsgFontChar);
|
|
lpDlgItem->y = YDUFromYPix(iY, gpsi->cyMsgFontChar);
|
|
lpDlgItem->cx = XDUFromXPix(iCX,gpsi->cxMsgFontChar);
|
|
lpDlgItem->cy = YDUFromYPix(iCY,gpsi->cyMsgFontChar);
|
|
lpDlgItem->id = (WORD)iCtrlId;
|
|
lpDlgItem->style = lStyle;
|
|
lpDlgItem->dwExtendedStyle = lExtendedStyle;
|
|
|
|
/*
|
|
* We have to avoid the following nasty rounding off problem:
|
|
* (e.g) If iCX=192 and cxSysFontChar=9, then cx becomes 85; When the
|
|
* static text is drawn, from 85 dlg units we get 191 pixels; So, the text
|
|
* is truncated;
|
|
* So, to avoid this, check if this is a static text and if so,
|
|
* add one more dialog unit to cx and cy;
|
|
* --Fix for Bug #4481 --SANKAR-- 09-29-89--
|
|
*/
|
|
|
|
/*
|
|
* Also, make sure we only do this to static text items. davidds
|
|
*/
|
|
|
|
/*
|
|
* Now static text uses SS_NOPREFIX = 0x80;
|
|
* So, test the lStyle field only with 0x0F instead of 0xFF;
|
|
* Fix for Bugs #5933 and 5935 --SANKAR-- 11-28-89
|
|
*/
|
|
if (iControlClass == STATICCODE &&
|
|
(((lStyle & 0x0F) == SS_LEFT) || ((lStyle & 0x0F) == SS_RIGHT))) {
|
|
|
|
/*
|
|
* This is static text
|
|
*/
|
|
lpDlgItem->cx++;
|
|
lpDlgItem->cy++;
|
|
}
|
|
|
|
/*
|
|
* Move ptr to the variable fields
|
|
*/
|
|
lpStr = (LPWSTR)(lpDlgItem + 1);
|
|
|
|
/*
|
|
* Store the Control Class value
|
|
*/
|
|
*lpStr++ = 0xFFFF;
|
|
*lpStr++ = (BYTE)iControlClass;
|
|
lpStr = (LPWSTR)NextWordBoundary(lpStr); // WORD-align lpszText
|
|
|
|
/*
|
|
* Check if the String contains Ordinal number or not
|
|
*/
|
|
fIsOrdNum = ((*lpszText == 0xFFFF) && (cchTextLen == sizeof(DWORD)/sizeof(WCHAR)));
|
|
|
|
/*
|
|
* NOTE: cchTextLen may be less than the length of lpszText. So,
|
|
* DO NOT USE lstrcpy() for the copy.
|
|
*/
|
|
RtlCopyMemory(lpStr, lpszText, cchTextLen*sizeof(WCHAR));
|
|
lpStr = lpStr + cchTextLen;
|
|
if (!fIsOrdNum) {
|
|
*lpStr = TEXT('\0'); // NULL terminate the string
|
|
lpStr = (LPWSTR)NextWordBoundary(lpStr + 1);
|
|
}
|
|
|
|
*lpStr++ = 0; // sizeof control data (there is none)
|
|
|
|
return NextDWordBoundary(lpStr);
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* MB_FindDlgTemplateSize
|
|
*
|
|
* This routine computes the amount of memory that will be needed for the
|
|
* messagebox's dialog template structure. The dialog template has several
|
|
* required and optional records. The dialog manager expects each record to
|
|
* be DWORD aligned so any necessary padding is also accounted for.
|
|
*
|
|
* (header - required)
|
|
* DLGTEMPLATE (header) + 1 menu byte + 1 pad + 1 class byte + 1 pad
|
|
* szCaption + 0 term + DWORD alignment
|
|
*
|
|
* (static icon control - optional)
|
|
* DLGITEMTEMPLATE + 1 class byte + 1 pad + (0xFF00 + icon ordinal # [szText]) +
|
|
* UINT alignment + 1 control data length byte (0) + DWORD alignment
|
|
*
|
|
* (pushbutton controls - variable, but at least one required)
|
|
* DLGITEMTEMPLATE + 1 class byte + 1 pad + length of button text +
|
|
* UINT alignment + 1 control data length byte (0) + DWORD alignment
|
|
*
|
|
* (static text control - optional)
|
|
* DLGITEMTEMPLATE + 1 class byte + 1 pad + length of text +
|
|
* UINT alignment + 1 control data length byte (0) + DWORD alignment
|
|
*
|
|
* History:
|
|
* 11-20-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
UINT
|
|
MB_FindDlgTemplateSize(
|
|
LPMSGBOXDATA lpmb)
|
|
{
|
|
ULONG_PTR cbLen;
|
|
UINT cbT;
|
|
UINT i;
|
|
UINT wCount;
|
|
|
|
wCount = lpmb->cButtons;
|
|
|
|
/*
|
|
* Start with dialog header's size.
|
|
*/
|
|
cbLen = (ULONG_PTR)NextWordBoundary(sizeof(DLGTEMPLATE) + sizeof(WCHAR));
|
|
cbLen = (ULONG_PTR)NextWordBoundary(cbLen + sizeof(WCHAR));
|
|
cbLen += wcslen(lpmb->lpszCaption) * sizeof(WCHAR) + sizeof(WCHAR);
|
|
cbLen += sizeof(WORD); // Font height
|
|
cbLen = (ULONG_PTR)NextDWordBoundary(cbLen);
|
|
|
|
/*
|
|
* Check if an Icon is present.
|
|
*/
|
|
if (lpmb->dwStyle & MB_ICONMASK)
|
|
cbLen += (ULONG_PTR)NextDWordBoundary(sizeof(DLGITEMTEMPLATE) + 7 * sizeof(WCHAR));
|
|
|
|
/*
|
|
* Find the number of buttons in the msg box.
|
|
*/
|
|
for (i = 0; i < wCount; i++) {
|
|
cbLen = (ULONG_PTR)NextWordBoundary(cbLen + sizeof(DLGITEMTEMPLATE) +
|
|
(2 * sizeof(WCHAR)));
|
|
cbT = (wcslen(lpmb->ppszButtonText[i]) + 1) * sizeof(WCHAR);
|
|
cbLen = (ULONG_PTR)NextWordBoundary(cbLen + cbT);
|
|
cbLen += sizeof(WCHAR);
|
|
cbLen = (ULONG_PTR)NextDWordBoundary(cbLen);
|
|
}
|
|
|
|
/*
|
|
* Add in the space required for the text message (if there is one).
|
|
*/
|
|
if (lpmb->lpszText != NULL) {
|
|
cbLen = (ULONG_PTR)NextWordBoundary(cbLen + sizeof(DLGITEMTEMPLATE) +
|
|
(2 * sizeof(WCHAR)));
|
|
cbT = (wcslen(lpmb->lpszText) + 1) * sizeof(WCHAR);
|
|
cbLen = (ULONG_PTR)NextWordBoundary(cbLen + cbT);
|
|
cbLen += sizeof(WCHAR);
|
|
cbLen = (ULONG_PTR)NextDWordBoundary(cbLen);
|
|
}
|
|
|
|
return (UINT)cbLen;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MB_GetIconOrdNum
|
|
*
|
|
* History:
|
|
* 11-20-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
|
|
UINT MB_GetIconOrdNum(
|
|
UINT rgBits)
|
|
{
|
|
switch (rgBits & MB_ICONMASK) {
|
|
case MB_USERICON:
|
|
case MB_ICONHAND:
|
|
return PtrToUlong(IDI_HAND);
|
|
|
|
case MB_ICONQUESTION:
|
|
return PtrToUlong(IDI_QUESTION);
|
|
|
|
case MB_ICONEXCLAMATION:
|
|
return PtrToUlong(IDI_EXCLAMATION);
|
|
|
|
case MB_ICONASTERISK:
|
|
return PtrToUlong(IDI_ASTERISK);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MB_GetString
|
|
*
|
|
* History:
|
|
* 1-24-95 JerrySh Created.
|
|
\***************************************************************************/
|
|
LPWSTR MB_GetString(
|
|
UINT wBtn)
|
|
{
|
|
if (wBtn < MAX_SEB_STYLES)
|
|
return GETGPSIMBPSTR(wBtn);
|
|
|
|
RIPMSG1(RIP_ERROR, "Invalid wBtn: %d", wBtn);
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* MB_DlgProc
|
|
*
|
|
* Returns: TRUE - message processed
|
|
* FALSE - message not processed
|
|
*
|
|
* History:
|
|
* 11-20-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
INT_PTR MB_DlgProc(
|
|
HWND hwndDlg,
|
|
UINT wMsg,
|
|
WPARAM wParam,
|
|
LPARAM lParam)
|
|
{
|
|
HWND hwndT;
|
|
int iCount;
|
|
LPMSGBOXDATA lpmb;
|
|
HWND hwndOwner;
|
|
PVOID lpfnCallback;
|
|
PWND pwnd;
|
|
BOOL bTimedOut = FALSE;
|
|
|
|
switch (wMsg) {
|
|
case WM_CTLCOLORDLG:
|
|
case WM_CTLCOLORSTATIC:
|
|
if ((pwnd = ValidateHwnd(hwndDlg)) == NULL)
|
|
return 0L;
|
|
return DefWindowProcWorker(pwnd, WM_CTLCOLORMSGBOX,
|
|
wParam, lParam, FALSE);
|
|
|
|
case WM_TIMER:
|
|
if (!bTimedOut) {
|
|
bTimedOut = TRUE;
|
|
EndTaskModalDialog(hwndDlg);
|
|
EndDialog(hwndDlg, IDTIMEOUT);
|
|
}
|
|
break;
|
|
|
|
case WM_NCDESTROY:
|
|
if ((lpmb = (LPMSGBOXDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA))) {
|
|
if (lpmb->dwTimeout != INFINITE) {
|
|
NtUserKillTimer(hwndDlg, 0);
|
|
lpmb->dwTimeout = INFINITE;
|
|
}
|
|
}
|
|
if ((pwnd = ValidateHwnd(hwndDlg)) == NULL) {
|
|
return 0L;
|
|
}
|
|
return DefWindowProcWorker(pwnd, wMsg,
|
|
wParam, lParam, FALSE);
|
|
|
|
|
|
case WM_INITDIALOG:
|
|
|
|
lpmb = (LPMSGBOXDATA)lParam;
|
|
SetWindowLongPtr(hwndDlg, GWLP_USERDATA, (ULONG_PTR)lParam);
|
|
|
|
NtUserCallHwnd(hwndDlg, SFI_SETMSGBOX);
|
|
|
|
if (lpmb->dwStyle & MB_HELP) {
|
|
NtUserSetWindowContextHelpId(hwndDlg, lpmb->dwContextHelpId);
|
|
}
|
|
|
|
if (lpmb->dwStyle & MB_TOPMOST) {
|
|
NtUserSetWindowPos(hwndDlg,
|
|
HWND_TOPMOST,
|
|
0, 0, 0, 0,
|
|
SWP_NOMOVE | SWP_NOSIZE);
|
|
}
|
|
|
|
if (lpmb->dwStyle & MB_USERICON) {
|
|
SendDlgItemMessage(hwndDlg, IDUSERICON, STM_SETICON, (WPARAM)(lpmb->lpszIcon), 0);
|
|
iCount = ALERT_SYSTEM_WARNING;
|
|
} else {
|
|
/*
|
|
* Generate an alert notification
|
|
*/
|
|
switch (lpmb->dwStyle & MB_ICONMASK) {
|
|
case MB_ICONWARNING:
|
|
iCount = ALERT_SYSTEM_WARNING;
|
|
break;
|
|
|
|
case MB_ICONQUESTION:
|
|
iCount = ALERT_SYSTEM_QUERY;
|
|
break;
|
|
|
|
case MB_ICONERROR:
|
|
iCount = ALERT_SYSTEM_ERROR;
|
|
break;
|
|
|
|
case MB_ICONINFORMATION:
|
|
default:
|
|
iCount = ALERT_SYSTEM_INFORMATIONAL;
|
|
break;
|
|
}
|
|
}
|
|
|
|
NotifyWinEvent(EVENT_SYSTEM_ALERT, hwndDlg, OBJID_ALERT, iCount);
|
|
|
|
if (lpmb->hwndOwner == NULL &&
|
|
(lpmb->dwStyle & MB_MODEMASK) == MB_TASKMODAL) {
|
|
StartTaskModalDialog(hwndDlg);
|
|
}
|
|
|
|
/*
|
|
* Set focus on the default button
|
|
*/
|
|
hwndT = GetWindow(hwndDlg, GW_CHILD);
|
|
iCount = lpmb->DefButton;
|
|
while (iCount--)
|
|
hwndT = GetWindow(hwndT, GW_HWNDNEXT);
|
|
|
|
NtUserSetFocus(hwndT);
|
|
|
|
//
|
|
// Need the dialog's HWND later, but we reuse hwndDlg.
|
|
//
|
|
hwndT = hwndDlg;
|
|
|
|
//
|
|
// If this dialogbox does not contain a IDCANCEL button, then
|
|
// remove the CLOSE command from the system menu.
|
|
// Bug #4445, --SANKAR-- 09-13-89 --
|
|
//
|
|
if (lpmb->CancelId == 0) {
|
|
HMENU hMenu;
|
|
|
|
if (hMenu = NtUserGetSystemMenu(hwndDlg, FALSE)) {
|
|
NtUserDeleteMenu(hMenu, SC_CLOSE, (UINT)MF_BYCOMMAND);
|
|
}
|
|
}
|
|
|
|
if ((lpmb->dwStyle & MB_TYPEMASK) == MB_OK) {
|
|
//
|
|
// Make the ID of OK button to be CANCEL, because we want
|
|
// the ESC to terminate the dialogbox; GetDlgItem32() will
|
|
// not fail, because this is MB_OK messagebox!
|
|
//
|
|
|
|
hwndDlg = GetDlgItem(hwndDlg, IDOK);
|
|
|
|
if (hwndDlg != NULL) {
|
|
// hwndDlg->hMenu = (HMENU)IDCANCEL;
|
|
SetWindowLongPtr(hwndDlg, GWLP_ID, IDCANCEL);
|
|
} else {
|
|
RIPMSG0(RIP_WARNING, "MB_DlgProc- IDOK control not found");
|
|
}
|
|
}
|
|
|
|
if (lpmb->dwTimeout != INFINITE) {
|
|
if (NtUserSetTimer(hwndT, 0, lpmb->dwTimeout, NULL) == 0) {
|
|
/*
|
|
* Couldn't create the timer, so "clear" out the timeout value
|
|
* for future reference.
|
|
*/
|
|
lpmb->dwTimeout = INFINITE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* We have changed the input focus
|
|
*/
|
|
return FALSE;
|
|
|
|
case WM_HELP:
|
|
// When user hits an F1 key, it results in this message.
|
|
// It is possible that this MsgBox has a callback instead of a
|
|
// parent. So, we must behave as if the user hit the HELP button.
|
|
|
|
goto MB_GenerateHelp;
|
|
|
|
case WM_COMMAND:
|
|
switch (LOWORD(wParam)) {
|
|
case IDOK:
|
|
case IDCANCEL:
|
|
//
|
|
// Check if a control exists with the given ID; This
|
|
// check is needed because DlgManager returns IDCANCEL
|
|
// blindly when ESC is pressed even if a button with
|
|
// IDCANCEL is not present.
|
|
// Bug #4445 --SANKAR--09-13-1989--
|
|
//
|
|
if (!GetDlgItem(hwndDlg, LOWORD(wParam)))
|
|
return FALSE;
|
|
|
|
|
|
// else FALL THRO....This is intentional.
|
|
case IDABORT:
|
|
case IDIGNORE:
|
|
case IDNO:
|
|
case IDRETRY:
|
|
case IDYES:
|
|
case IDTRYAGAIN:
|
|
case IDCONTINUE:
|
|
EndTaskModalDialog(hwndDlg);
|
|
EndDialog(hwndDlg, LOWORD(wParam));
|
|
break;
|
|
case IDHELP:
|
|
MB_GenerateHelp:
|
|
// Generate the WM_HELP message and send it to owner or callback
|
|
hwndOwner = NULL;
|
|
|
|
// Check if there is an app supplied callback for this MsgBox
|
|
lpmb = (LPMSGBOXDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
|
if ((lpfnCallback = lpmb->lpfnMsgBoxCallback) == NULL) {
|
|
// If not, see if we need to inform the parent.
|
|
hwndOwner = GetWindow(hwndDlg, GW_OWNER);
|
|
}
|
|
|
|
/*
|
|
* See if we need to generate the Help message or call back.
|
|
*/
|
|
if (hwndOwner || lpfnCallback) {
|
|
SendHelpMessage(hwndOwner,
|
|
HELPINFO_WINDOW,
|
|
IDHELP,
|
|
hwndDlg,
|
|
NtUserGetWindowContextHelpId(hwndDlg),
|
|
lpfnCallback);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case WM_COPY:
|
|
MB_CopyToClipboard(hwndDlg);
|
|
break;
|
|
|
|
default:
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* StartTaskModalDialog
|
|
*
|
|
* History:
|
|
* 11-20-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
VOID
|
|
StartTaskModalDialog(
|
|
HWND hwndDlg)
|
|
{
|
|
int cHwnd;
|
|
HWND *phwnd;
|
|
HWND *phwndList, *phwndEnd;
|
|
HWND hwnd;
|
|
PWND pwnd;
|
|
LPMSGBOXDATA lpmb;
|
|
|
|
/*
|
|
* Get the hwnd list. It is returned in a block of memory allocated with
|
|
* UserLocalAlloc.
|
|
*/
|
|
if ((cHwnd = BuildHwndList(NULL, NULL, FALSE, GetCurrentThreadId(), &phwndList)) == 0) {
|
|
return;
|
|
}
|
|
|
|
lpmb = (LPMSGBOXDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
|
lpmb->phwndList = phwndList;
|
|
|
|
phwndEnd = phwndList + cHwnd;
|
|
for (phwnd = phwndList; phwnd < phwndEnd; phwnd++) {
|
|
if ((hwnd = *phwnd) == NULL || (pwnd = RevalidateHwnd(hwnd)) == NULL)
|
|
continue;
|
|
|
|
/*
|
|
* if the window belongs to the current task and is enabled, disable
|
|
* it. All other windows are NULL'd out, to prevent their being
|
|
* enabled later
|
|
*/
|
|
if (!TestWF(pwnd, WFDISABLED) && DIFFWOWHANDLE(hwnd, hwndDlg)) {
|
|
NtUserEnableWindow(hwnd, FALSE);
|
|
} else {
|
|
*phwnd = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/***************************************************************************\
|
|
* EndTaskModalDialog
|
|
*
|
|
* History:
|
|
* 11-20-90 DarrinM Ported from Win 3.0 sources.
|
|
\***************************************************************************/
|
|
VOID
|
|
EndTaskModalDialog(
|
|
HWND hwndDlg)
|
|
{
|
|
HWND *phwnd;
|
|
HWND *phwndList;
|
|
HWND hwnd;
|
|
LPMSGBOXDATA lpmb;
|
|
|
|
lpmb = (LPMSGBOXDATA)GetWindowLongPtr(hwndDlg, GWLP_USERDATA);
|
|
phwndList = lpmb->phwndList;
|
|
if (phwndList == NULL) {
|
|
return;
|
|
}
|
|
|
|
lpmb->phwndList = NULL;
|
|
|
|
for (phwnd = phwndList; *phwnd != (HWND)1; phwnd++) {
|
|
if ((hwnd = *phwnd) != NULL) {
|
|
NtUserEnableWindow(hwnd, TRUE);
|
|
}
|
|
}
|
|
|
|
UserLocalFree(phwndList);
|
|
}
|
|
|
|
#ifdef _JANUS_
|
|
/***************************************************************************\
|
|
* ErrorMessageInst
|
|
*
|
|
* Instrument routine for recording error msg
|
|
*
|
|
* Returns: TRUE - Instrument error msg Success
|
|
* FALSE - Fail
|
|
*
|
|
* History:
|
|
* 8-5-98 Chienho Created
|
|
\***************************************************************************/
|
|
|
|
BOOL ErrorMessageInst(
|
|
LPMSGBOXDATA pMsgBoxParams)
|
|
{
|
|
ERROR_ELEMENT ErrEle;
|
|
WCHAR *pwcs;
|
|
PVOID ImageBase;
|
|
PIMAGE_NT_HEADERS NtHeaders;
|
|
BOOL rc;
|
|
WCHAR szUnknown[32];
|
|
|
|
/*
|
|
* Check if the MessageBox style is within the logged severity level
|
|
*/
|
|
switch (pMsgBoxParams->dwStyle & MB_ICONMASK) {
|
|
case MB_ICONHAND:
|
|
/*
|
|
* when EMI is enabled, we at least log error messages
|
|
*/
|
|
break;
|
|
case MB_ICONEXCLAMATION:
|
|
if (gdwEMIControl > EMI_SEVERITY_WARNING) {
|
|
rc = TRUE;
|
|
goto End;
|
|
}
|
|
break;
|
|
case MB_ICONQUESTION:
|
|
if (gdwEMIControl > EMI_SEVERITY_QUESTION) {
|
|
rc = TRUE;
|
|
goto End;
|
|
}
|
|
break;
|
|
case MB_ICONASTERISK:
|
|
if (gdwEMIControl > EMI_SEVERITY_INFORMATION) {
|
|
rc = TRUE;
|
|
goto End;
|
|
}
|
|
break;
|
|
case MB_USERICON:
|
|
if (gdwEMIControl > EMI_SEVERITY_USER) {
|
|
rc = TRUE;
|
|
goto End;
|
|
}
|
|
break;
|
|
default:
|
|
if (gdwEMIControl > EMI_SEVERITY_ALL) {
|
|
rc = TRUE;
|
|
goto End;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (gdwEMIThreadID != GETTHREADID()) {
|
|
rc = FALSE;
|
|
goto End;
|
|
}
|
|
RtlZeroMemory(&ErrEle, sizeof(ErrEle));
|
|
|
|
/*
|
|
* get last error first, check with FormatMessage???
|
|
*/
|
|
ErrEle.dwErrorCode = GetLastError();
|
|
|
|
/*
|
|
* get return address
|
|
*/
|
|
|
|
ErrEle.ReturnAddr = gpReturnAddr;
|
|
|
|
/*
|
|
* get the process image name
|
|
*/
|
|
GetCurrentProcessName(ErrEle.ProcessName, ARRAY_SIZE(ErrEle.ProcessName));
|
|
|
|
/*
|
|
* Load the "unknown" string
|
|
*/
|
|
LoadString(hmodUser, STR_UNKNOWN, szUnknown, ARRAYSIZE(szUnknown));
|
|
|
|
/*
|
|
* get the window title
|
|
*/
|
|
GetWindowTextW(pMsgBoxParams->hwndOwner, ErrEle.WindowTitle, TITLE_SIZE);
|
|
if (!(*(ErrEle.WindowTitle))) {
|
|
lstrcpy(ErrEle.WindowTitle, szUnknown);
|
|
}
|
|
|
|
/*
|
|
* get messagebox data
|
|
*/
|
|
ErrEle.lpszText = (LPWSTR)pMsgBoxParams->lpszText;
|
|
ErrEle.lpszCaption = (LPWSTR)pMsgBoxParams->lpszCaption;
|
|
ErrEle.dwStyle = pMsgBoxParams->dwStyle;
|
|
|
|
/*
|
|
* resolve the module name of caller
|
|
*/
|
|
if (!RtlPcToFileHeader((PVOID)ErrEle.ReturnAddr, &ImageBase)) {
|
|
RIPMSG0(RIP_WARNING, "ErrorMessageInst: Can't find Caller");
|
|
ErrEle.BaseAddr = (PVOID)-1;
|
|
ErrEle.dwImageSize = -1;
|
|
lstrcpy(ErrEle.CallerModuleName, szUnknown);
|
|
} else {
|
|
ErrEle.BaseAddr = ImageBase;
|
|
if (GetModuleFileName((HMODULE)ImageBase, ErrEle.CallerModuleName, MAX_PATH)) {
|
|
pwcs = wcsrchr(ErrEle.CallerModuleName, TEXT('\\'));
|
|
if (pwcs) {
|
|
pwcs++;
|
|
lstrcpy(ErrEle.CallerModuleName, pwcs);
|
|
}
|
|
} else {
|
|
lstrcpy(ErrEle.CallerModuleName, szUnknown);
|
|
}
|
|
NtHeaders = RtlImageNtHeader(ImageBase);
|
|
if (NtHeaders == NULL) {
|
|
ErrEle.dwImageSize = -1;
|
|
} else {
|
|
ErrEle.dwImageSize = NtHeaders->OptionalHeader.SizeOfImage;
|
|
}
|
|
}
|
|
/*
|
|
* Register the event if we haven't done so already.
|
|
* Since RegisterEventSource is supported by a service, we must not hold
|
|
* any locks while making this call. Hence we might have several threads
|
|
* registering the event simultaneously.
|
|
*/
|
|
|
|
if (!gEventSource) {
|
|
gEventSource = RegisterEventSourceW(NULL, L"Error Instrument");
|
|
if (!gEventSource) {
|
|
ErrEle.dwErrorCode = GetLastError();
|
|
rc = FALSE;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* report event
|
|
*/
|
|
if (gEventSource) {
|
|
rc = LogMessageBox(&ErrEle);
|
|
}
|
|
|
|
/*
|
|
* allow to process another event log again
|
|
*/
|
|
|
|
InterlockedExchangePointer(&gdwEMIThreadID, 0);
|
|
|
|
End:
|
|
return rc;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* InitInstrument
|
|
*
|
|
* Returns: TRUE - Initialization Success
|
|
* FALSE - Initialization Fail
|
|
*
|
|
\***************************************************************************/
|
|
BOOL InitInstrument(
|
|
LPDWORD lpEMIControl)
|
|
{
|
|
NTSTATUS Status;
|
|
HKEY hKeyEMI = NULL;
|
|
UNICODE_STRING UnicodeStringEMIKey;
|
|
UNICODE_STRING UnicodeStringEnable;
|
|
UNICODE_STRING UnicodeStringStyle;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
DWORD EMIEnable = 0; //means disable
|
|
DWORD EMISeverity;
|
|
struct {
|
|
KEY_VALUE_PARTIAL_INFORMATION;
|
|
LARGE_INTEGER;
|
|
} EMIValueInfo;
|
|
DWORD dwDisposition;
|
|
|
|
RtlInitUnicodeString(&UnicodeStringEMIKey, szEMIKey);
|
|
InitializeObjectAttributes(&ObjA, &UnicodeStringEMIKey, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
Status = NtOpenKey(&hKeyEMI, KEY_READ, &ObjA);
|
|
if (!NT_SUCCESS(Status)) {
|
|
/*
|
|
* Key doesn't exist, assume disable
|
|
*/
|
|
return FALSE;
|
|
}
|
|
|
|
/*
|
|
* read the logging enable and setting
|
|
*/
|
|
RtlInitUnicodeString(&UnicodeStringEnable, szEMIEnable);
|
|
Status = NtQueryValueKey(hKeyEMI,
|
|
&UnicodeStringEnable,
|
|
KeyValuePartialInformation,
|
|
&EMIValueInfo,
|
|
sizeof(EMIValueInfo),
|
|
&dwDisposition);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
|
|
RtlCopyMemory(&EMIEnable, &EMIValueInfo.Data, sizeof(EMIEnable));
|
|
|
|
RtlInitUnicodeString(&UnicodeStringStyle, szEMISeverity);
|
|
Status = NtQueryValueKey(hKeyEMI,
|
|
&UnicodeStringStyle,
|
|
KeyValuePartialInformation,
|
|
&EMIValueInfo,
|
|
sizeof(EMIValueInfo),
|
|
&dwDisposition);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlCopyMemory(&EMISeverity, &EMIValueInfo.Data, sizeof(EMISeverity));
|
|
/*
|
|
* Validate data
|
|
*/
|
|
if (EMISeverity > EMI_SEVERITY_MAX_VALUE) {
|
|
EMISeverity = EMI_SEVERITY_MAX_VALUE;
|
|
}
|
|
} else {
|
|
/*
|
|
* default severity for instrument
|
|
*/
|
|
EMISeverity = EMI_SEVERITY_WARNING;
|
|
}
|
|
*lpEMIControl = EMISeverity;
|
|
}
|
|
|
|
/*
|
|
* read default message reply enable
|
|
*/
|
|
RtlInitUnicodeString(&UnicodeStringEnable, szDMREnable);
|
|
Status = NtQueryValueKey(hKeyEMI,
|
|
&UnicodeStringEnable,
|
|
KeyValuePartialInformation,
|
|
&EMIValueInfo,
|
|
sizeof(EMIValueInfo),
|
|
&dwDisposition);
|
|
|
|
if (NT_SUCCESS(Status)) {
|
|
RtlCopyMemory(&gfDMREnable, &EMIValueInfo.Data, sizeof(gfDMREnable));
|
|
}
|
|
|
|
NtClose(hKeyEMI);
|
|
|
|
if (EMIEnable) {
|
|
|
|
/*
|
|
* add eventlog file
|
|
*/
|
|
if (NT_SUCCESS(CreateLogSource())) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* CreateLogSource
|
|
*
|
|
* Create the event source for eventlog
|
|
* Return : NTSTATUS
|
|
*
|
|
\***************************************************************************/
|
|
NTSTATUS CreateLogSource()
|
|
{
|
|
NTSTATUS Status;
|
|
UNICODE_STRING UnicodeStringEventKey;
|
|
OBJECT_ATTRIBUTES ObjA;
|
|
HKEY hKeyEvent = NULL;
|
|
UNICODE_STRING UnicodeString;
|
|
DWORD dwDisposition;
|
|
|
|
|
|
RtlInitUnicodeString(&UnicodeStringEventKey, szEventKey);
|
|
InitializeObjectAttributes(&ObjA, &UnicodeStringEventKey, OBJ_CASE_INSENSITIVE, NULL, NULL);
|
|
|
|
if (NT_SUCCESS(Status = NtOpenKey(&hKeyEvent, KEY_READ, &ObjA))) {
|
|
|
|
struct {
|
|
KEY_VALUE_PARTIAL_INFORMATION KeyInfo;
|
|
WCHAR awchMsgFileName[256];
|
|
} MsgFile;
|
|
|
|
RtlInitUnicodeString(&UnicodeString, szEventMsgFile);
|
|
|
|
Status = NtQueryValueKey(hKeyEvent,
|
|
&UnicodeString,
|
|
KeyValuePartialInformation,
|
|
&MsgFile,
|
|
sizeof MsgFile,
|
|
&dwDisposition);
|
|
if (NT_SUCCESS(Status)) {
|
|
Status = lstrcmpi((LPWSTR)MsgFile.KeyInfo.Data, L"%SystemRoot%\\System32\\user32.dll");
|
|
}
|
|
NtClose(hKeyEvent);
|
|
}
|
|
|
|
return Status;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* LogMessageBox
|
|
*
|
|
* Output error message record into eventlog
|
|
*
|
|
\***************************************************************************/
|
|
BOOL LogMessageBox(
|
|
LPERROR_ELEMENT lpErrEle)
|
|
{
|
|
LPWSTR lps[8];
|
|
DWORD dwData[2];
|
|
WCHAR BaseAddress[19];
|
|
WCHAR ImageSize[19];
|
|
WCHAR ReturnAddress[19];
|
|
PTOKEN_USER pTokenUser = NULL;
|
|
PSID pSid = NULL;
|
|
BOOL rc;
|
|
|
|
lps[0] = lpErrEle->ProcessName;
|
|
lps[1] = lpErrEle->WindowTitle;
|
|
lps[2] = lpErrEle->lpszCaption;
|
|
lps[3] = lpErrEle->lpszText;
|
|
lps[4] = lpErrEle->CallerModuleName;
|
|
wsprintf(BaseAddress, L"%-#16p", lpErrEle->BaseAddr);
|
|
lps[5] = BaseAddress;
|
|
wsprintf(ImageSize, L"%-#16lX", lpErrEle->dwImageSize);
|
|
lps[6] = ImageSize;
|
|
wsprintf(ReturnAddress, L"%-#16p", lpErrEle->ReturnAddr);
|
|
lps[7] = ReturnAddress;
|
|
|
|
dwData[0] = lpErrEle->dwStyle;
|
|
dwData[1] = lpErrEle->dwErrorCode;
|
|
|
|
if (GetUserSid(&pTokenUser)) {
|
|
pSid = pTokenUser->User.Sid;
|
|
}
|
|
|
|
UserAssert(gEventSource != NULL);
|
|
rc = ReportEventW(gEventSource,
|
|
EVENTLOG_INFORMATION_TYPE,
|
|
0,
|
|
STATUS_LOG_ERROR_MSG,
|
|
pSid,
|
|
ARRAY_SIZE(lps),
|
|
sizeof(dwData),
|
|
lps,
|
|
dwData);
|
|
|
|
if (pTokenUser) {
|
|
VirtualFree(pTokenUser, 0, MEM_RELEASE);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
#endif
|