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.
3142 lines
77 KiB
3142 lines
77 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1995 - 1999
|
|
All rights reserved.
|
|
|
|
Module Name:
|
|
|
|
Util.cxx
|
|
|
|
Abstract:
|
|
|
|
Misc util functions.
|
|
|
|
Author:
|
|
|
|
Albert Ting (AlbertT) 27-Jan-1995
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.hxx"
|
|
#pragma hdrstop
|
|
|
|
#include <shgina.h>
|
|
#include "result.hxx"
|
|
#include "msgbox.hxx"
|
|
#include "spllibex.hxx"
|
|
#include "prndata.hxx"
|
|
|
|
//
|
|
// Global functions
|
|
//
|
|
|
|
static MSG_HLPMAP gMsgHelpTable [] =
|
|
{
|
|
{ ERROR_INVALID_PRINTER_NAME, IDS_ERR_WITH_HELP1, gszHelpTroubleShooterURL},
|
|
{ ERROR_KM_DRIVER_BLOCKED, IDS_COULDNOTCONNECTTOPRINTER_BLOCKED_HELP, gszHelpTroubleShooterURL},
|
|
{ 0, 0, 0},
|
|
};
|
|
|
|
enum
|
|
{
|
|
//
|
|
// Not mapped error code.
|
|
// Show up the text from the GetLastError()
|
|
//
|
|
IDS_NOT_MAPPED = (UINT )(-1)
|
|
};
|
|
|
|
#define MAKERESOURCEINT(psz) PTR2UINT(psz)
|
|
|
|
//
|
|
// This global error mapping table is proposed to filter out
|
|
// the errors that are showed up to the end user to a certain
|
|
// well known set, which we have a good explanation for. All the
|
|
// rest of the errors will be mapped to the generic error text.
|
|
// if the message mapping is IDS_NOT_MAPPED it means the text
|
|
// coming from the GetLastError/FormatMessage APIs is good enough
|
|
// so we are going to use it. Otherwise the mapping ID is a
|
|
// resource id of the remapped text.
|
|
//
|
|
static MSG_ERRMAP gGlobalErrorMapTable[] =
|
|
{
|
|
|
|
//
|
|
// Category 1 - Print Spooler Error Codes
|
|
//
|
|
ERROR_UNKNOWN_PRINT_MONITOR, IDS_NOT_MAPPED,
|
|
ERROR_PRINTER_DRIVER_IN_USE, IDS_NOT_MAPPED,
|
|
ERROR_SPOOL_FILE_NOT_FOUND, IDS_ERRMAP_SPOOL_FILE_NOT_FOUND,
|
|
ERROR_SPL_NO_STARTDOC, IDS_ERRMAP_SPL_NO_STARTDOC,
|
|
ERROR_SPL_NO_ADDJOB, IDS_ERRMAP_SPL_NO_STARTDOC,
|
|
ERROR_PRINT_PROCESSOR_ALREADY_INSTALLED, IDS_NOT_MAPPED,
|
|
ERROR_PRINT_MONITOR_ALREADY_INSTALLED, IDS_NOT_MAPPED,
|
|
ERROR_INVALID_PRINT_MONITOR, IDS_NOT_MAPPED,
|
|
ERROR_PRINT_MONITOR_IN_USE, IDS_NOT_MAPPED,
|
|
ERROR_PRINTER_HAS_JOBS_QUEUED, IDS_NOT_MAPPED,
|
|
ERROR_SUCCESS_REBOOT_REQUIRED, IDS_NOT_MAPPED,
|
|
ERROR_SUCCESS_RESTART_REQUIRED, IDS_NOT_MAPPED,
|
|
ERROR_PRINTER_NOT_FOUND, IDS_NOT_MAPPED,
|
|
ERROR_REMOTE_PRINT_CONNECTIONS_BLOCKED, IDS_NOT_MAPPED,
|
|
|
|
//
|
|
// Category 2 - Print Subsystem Related Error Codes
|
|
//
|
|
ERROR_OUT_OF_PAPER, IDS_NOT_MAPPED,
|
|
ERROR_PRINTQ_FULL, IDS_NOT_MAPPED,
|
|
ERROR_NO_SPOOL_SPACE, IDS_ERRMAP_NO_SPOOL_SPACE,
|
|
ERROR_PRINT_CANCELLED, IDS_NOT_MAPPED,
|
|
ERROR_REDIR_PAUSED, IDS_NOT_MAPPED,
|
|
ERROR_PRINTER_DRIVER_ALREADY_INSTALLED, IDS_NOT_MAPPED,
|
|
ERROR_UNKNOWN_PRINTER_DRIVER, IDS_ERROR_UNKNOWN_DRIVER,
|
|
ERROR_UNKNOWN_PRINTPROCESSOR, IDS_ERRMAP_UNKNOWN_PRINTPROCESSOR,
|
|
ERROR_INVALID_PRINTER_NAME, IDS_NOT_MAPPED,
|
|
ERROR_PRINTER_ALREADY_EXISTS, IDS_ERRMAP_PRINTER_ALREADY_EXISTS,
|
|
ERROR_INVALID_PRINTER_COMMAND, IDS_NOT_MAPPED,
|
|
ERROR_ALREADY_WAITING, IDS_NOT_MAPPED,
|
|
ERROR_PRINTER_DELETED, IDS_NOT_MAPPED,
|
|
ERROR_INVALID_PRINTER_STATE, IDS_NOT_MAPPED,
|
|
ERROR_BAD_DRIVER, IDS_NOT_MAPPED,
|
|
|
|
//
|
|
// Category 3 - Common Win32/RPC
|
|
//
|
|
RPC_S_SERVER_UNAVAILABLE, IDS_ERRMAP_RPC_S_SERVER_UNAVAILABLE,
|
|
RPC_S_INVALID_NET_ADDR, IDS_NOT_MAPPED,
|
|
ERROR_ACCESS_DENIED, IDS_NOT_MAPPED,
|
|
ERROR_DISK_FULL, IDS_ERRMAP_DISK_FULL,
|
|
ERROR_NOT_READY, IDS_NOT_MAPPED,
|
|
ERROR_DEVICE_NOT_AVAILABLE, IDS_NOT_MAPPED,
|
|
ERROR_WRITE_PROTECT, IDS_NOT_MAPPED,
|
|
ERROR_SHARING_VIOLATION, IDS_NOT_MAPPED,
|
|
ERROR_LOCK_VIOLATION, IDS_NOT_MAPPED,
|
|
ERROR_HANDLE_DISK_FULL, IDS_ERRMAP_DISK_FULL,
|
|
ERROR_NETWORK_BUSY, IDS_NOT_MAPPED,
|
|
ERROR_NETWORK_ACCESS_DENIED, IDS_ERRMAP_NETWORK_ACCESS_DENIED,
|
|
ERROR_NETNAME_DELETED, IDS_NOT_MAPPED,
|
|
ERROR_CANNOT_MAKE, IDS_NOT_MAPPED,
|
|
ERROR_NET_WRITE_FAULT, IDS_ERRMAP_NET_WRITE_FAULT,
|
|
ERROR_INVALID_PASSWORD, IDS_NOT_MAPPED,
|
|
ERROR_NOT_SUPPORTED, IDS_ERRMAP_NOT_SUPPORTED,
|
|
ERROR_OUTOFMEMORY, IDS_ERRMAP_OUTOFMEMORY,
|
|
ERROR_NOT_ENOUGH_MEMORY, IDS_ERRMAP_OUTOFMEMORY,
|
|
ERROR_CANCELLED, IDS_NOT_MAPPED,
|
|
ERROR_BAD_DRIVER, IDS_NOT_MAPPED,
|
|
ERROR_DRIVE_LOCKED, IDS_NOT_MAPPED,
|
|
ERROR_DEV_NOT_EXIST, IDS_NOT_MAPPED,
|
|
ERROR_OPEN_FAILED, IDS_NOT_MAPPED,
|
|
ERROR_DEVICE_DOOR_OPEN, IDS_NOT_MAPPED,
|
|
ERROR_BAD_DEVICE, IDS_NOT_MAPPED,
|
|
ERROR_INVALID_PROFILE, IDS_NOT_MAPPED,
|
|
ERROR_PROFILE_NOT_ASSOCIATED_WITH_DEVICE, IDS_NOT_MAPPED,
|
|
ERROR_PROFILE_NOT_FOUND, IDS_NOT_MAPPED,
|
|
ERROR_DEVICE_NOT_AVAILABLE, IDS_NOT_MAPPED,
|
|
ERROR_NO_MEDIA_IN_DRIVE, IDS_NOT_MAPPED,
|
|
ERROR_UNRECOGNIZED_MEDIA, IDS_NOT_MAPPED,
|
|
ERROR_MEDIA_NOT_AVAILABLE, IDS_NOT_MAPPED,
|
|
ERROR_UNKNOWN_PORT, IDS_NOT_MAPPED,
|
|
ERROR_FILE_CORRUPT, IDS_ERRMAP_FILE_CORRUPT,
|
|
ERROR_DISK_CORRUPT, IDS_NOT_MAPPED,
|
|
ERROR_ALREADY_EXISTS, IDS_NOT_MAPPED,
|
|
ERROR_KM_DRIVER_BLOCKED, IDS_NOT_MAPPED,
|
|
ERROR_PRINTER_DRIVER_BLOCKED, IDS_PRINTER_DRIVER_BLOCKED,
|
|
ERROR_REQ_NOT_ACCEP, IDS_NOT_MAPPED,
|
|
|
|
// end of the table
|
|
0, 0
|
|
|
|
}; // gGlobalErrorMapTable
|
|
|
|
inline static HRESULT CreateError()
|
|
{
|
|
DWORD dw = GetLastError();
|
|
if (ERROR_SUCCESS == dw) return E_FAIL;
|
|
return HRESULT_FROM_WIN32(dw);
|
|
}
|
|
|
|
LPTSTR
|
|
pszStrCat(
|
|
IN OUT LPTSTR pszDest,
|
|
OUT LPCTSTR pszSource, OPTIONAL
|
|
IN OUT UINT& cchDest
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Copies pszSource to pszDest iff there is enough space in cchDest.
|
|
|
|
Arguments:
|
|
|
|
pszDest - Destination buffer
|
|
|
|
pszSource - Source string, may be NULL (no action taken)
|
|
|
|
cchDest - char count size of pszDest, on return, space remaining
|
|
|
|
Return Value:
|
|
|
|
LPTSTR - pointer to remaining space
|
|
NULL - out of space.
|
|
|
|
--*/
|
|
|
|
{
|
|
if( !pszSource ){
|
|
return pszDest;
|
|
}
|
|
|
|
UINT cchSource = lstrlen( pszSource );
|
|
|
|
//
|
|
// Fail if dest can't hold source (equal case doesn't hold NULL term)
|
|
// OR dest string is NULL.
|
|
//
|
|
if( !pszDest || cchDest <= cchSource + 1 ){
|
|
return pszDest;
|
|
}
|
|
|
|
StringCchCopy( pszDest, cchDest, pszSource );
|
|
cchDest -= cchSource;
|
|
|
|
return pszDest + cchSource;
|
|
}
|
|
|
|
|
|
INT
|
|
iMessage(
|
|
IN HWND hwnd,
|
|
IN UINT idsTitle,
|
|
IN UINT idsMessage,
|
|
IN UINT uType,
|
|
IN DWORD dwLastError,
|
|
IN const PMSG_ERRMAP pMsgErrMap,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Formats error message.
|
|
|
|
Arguments:
|
|
|
|
See Internal_Message for details
|
|
|
|
Return Value:
|
|
|
|
returns value from MessageBox on sucess
|
|
and zero on failure
|
|
|
|
--*/
|
|
{
|
|
va_list valist;
|
|
va_start(valist, pMsgErrMap);
|
|
|
|
INT iRetval = 0;
|
|
if( FAILED(Internal_Message(&iRetval,
|
|
ghInst,
|
|
hwnd,
|
|
MAKEINTRESOURCE(idsTitle),
|
|
MAKEINTRESOURCE(idsMessage),
|
|
uType,
|
|
dwLastError,
|
|
pMsgErrMap,
|
|
NULL,
|
|
0,
|
|
valist)) )
|
|
{
|
|
// make sure we return zero in the failure case
|
|
iRetval = 0;
|
|
}
|
|
|
|
va_end(valist);
|
|
return iRetval;
|
|
}
|
|
|
|
INT
|
|
iMessage2(
|
|
IN HWND hwnd,
|
|
IN LPCTSTR pszTitle,
|
|
IN LPCTSTR pszMessage,
|
|
IN UINT uType,
|
|
IN DWORD dwLastError,
|
|
IN const PMSG_ERRMAP pMsgErrMap
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Formats error message.
|
|
|
|
Arguments:
|
|
|
|
See Internal_Message for details
|
|
pszTitle and pszMessage can be resource IDs i.e. MAKEINTRESOURCE(id)
|
|
|
|
Return Value:
|
|
|
|
returns value from MessageBox on sucess
|
|
and zero on failure
|
|
|
|
--*/
|
|
{
|
|
va_list valist;
|
|
va_start(valist, pMsgErrMap);
|
|
|
|
INT iRetval = 0;
|
|
if( FAILED(Internal_Message(&iRetval,
|
|
ghInst,
|
|
hwnd,
|
|
pszTitle,
|
|
pszMessage,
|
|
uType,
|
|
dwLastError,
|
|
pMsgErrMap,
|
|
NULL,
|
|
0,
|
|
valist)) )
|
|
{
|
|
// make sure we return zero in the failure case
|
|
iRetval = 0;
|
|
}
|
|
|
|
va_end(valist);
|
|
return iRetval;
|
|
}
|
|
|
|
INT
|
|
iMessageEx(
|
|
IN HWND hwnd,
|
|
IN UINT idsTitle,
|
|
IN UINT idsMessage,
|
|
IN UINT uType,
|
|
IN DWORD dwLastError,
|
|
IN const PMSG_ERRMAP pMsgErrMap,
|
|
IN UINT idMessage,
|
|
IN const PMSG_HLPMAP pHlpErrMap,
|
|
...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Formats error message.
|
|
|
|
Arguments:
|
|
|
|
See Internal_Message for details
|
|
|
|
Return Value:
|
|
|
|
returns value from MessageBox on sucess
|
|
and zero on failure
|
|
|
|
--*/
|
|
{
|
|
va_list valist;
|
|
va_start(valist, pHlpErrMap);
|
|
|
|
INT iRetval = 0;
|
|
if( FAILED(Internal_Message(&iRetval,
|
|
ghInst,
|
|
hwnd,
|
|
MAKEINTRESOURCE(idsTitle),
|
|
MAKEINTRESOURCE(idsMessage),
|
|
uType,
|
|
dwLastError,
|
|
pMsgErrMap,
|
|
idMessage,
|
|
pHlpErrMap,
|
|
valist)) )
|
|
{
|
|
// make sure we return zero in the failure case
|
|
iRetval = 0;
|
|
}
|
|
|
|
va_end(valist);
|
|
return iRetval;
|
|
}
|
|
|
|
HRESULT
|
|
Internal_Message(
|
|
OUT INT *piResult,
|
|
IN HINSTANCE hModule,
|
|
IN HWND hwnd,
|
|
IN LPCTSTR pszTitle,
|
|
IN LPCTSTR pszMessage,
|
|
IN UINT uType,
|
|
IN DWORD dwLastError,
|
|
IN const PMSG_ERRMAP pMsgErrMap,
|
|
IN UINT idHlpMessage,
|
|
IN const PMSG_HLPMAP pHlpErrMap,
|
|
IN va_list valist
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Formats error message.
|
|
|
|
Arguments:
|
|
|
|
piResult - the message box return value (on success)
|
|
|
|
hModule - module where to load the resources from
|
|
|
|
hwnd - Parent windows. This only be NULL if there is a synchronous
|
|
error (an error that occurs immediately after a user action).
|
|
If it is NULL, then we set it to foreground.
|
|
|
|
pszTitle - Title of message box. Can be a resource ID i.e. MAKEINTRESOURCE(id)
|
|
|
|
pszMessage - Message text. May have inserts. Can be a resource ID too.
|
|
|
|
uType - Type of message box. See MessageBox api.
|
|
|
|
dwLastError - 0 indicates don't cat error string onto end of message text
|
|
kMsgGetLastError: Use GetLastError(). Other: format to LastError
|
|
text.
|
|
|
|
pMsgErrMap - Translate some error messages into friendlier text.
|
|
May be NULL.
|
|
|
|
idMessage - Help message ids to look for in the help message map.
|
|
|
|
pHlpErrMap - Pointer to error message map where look, If a match
|
|
is found in this map then the help button will be
|
|
displayed on the message box. May be MULL
|
|
|
|
... extra parameters to idsMessage follow.
|
|
|
|
Return Value:
|
|
|
|
S_OK on success and COM error on failure
|
|
|
|
--*/
|
|
|
|
{
|
|
TStatusB bStatus;
|
|
TString strMappedMessage;
|
|
TString strMsgTemplate;
|
|
TString strMessage;
|
|
TString strTitle;
|
|
INT iResult = 0;
|
|
BOOL bFormatNeeded = FALSE;
|
|
MSG_HLPMAP *pHelpMapEntry = NULL;
|
|
MSG_ERRMAP *pErrMapEntry = NULL;
|
|
UINT idsTitle = IDS_NOT_MAPPED;
|
|
UINT idsMessage = IDS_NOT_MAPPED;
|
|
HRESULT hr = S_OK;
|
|
|
|
if( NULL == hModule )
|
|
{
|
|
hModule = ghInst;
|
|
}
|
|
|
|
HINSTANCE hInstTitle = hModule,
|
|
hInstMessage = hModule;
|
|
|
|
if( NULL == pszTitle )
|
|
{
|
|
pszTitle = MAKEINTRESOURCE(IDS_PRINTERS_TITLE);
|
|
hInstTitle = ghInst;
|
|
}
|
|
|
|
if( NULL == pszMessage )
|
|
{
|
|
pszMessage = MAKEINTRESOURCE(IDS_ERR_GENERIC);
|
|
hInstMessage = ghInst;
|
|
}
|
|
|
|
// first load the title and format strings and
|
|
// then format the string with any following aguments.
|
|
|
|
if( SUCCEEDED(hr) && IS_INTRESOURCE(pszTitle) )
|
|
{
|
|
idsTitle = MAKERESOURCEINT(pszTitle);
|
|
bStatus DBGCHK = strTitle.bLoadString(hInstTitle, idsTitle);
|
|
pszTitle = strTitle;
|
|
|
|
if( !bStatus )
|
|
{
|
|
hr = CreateError();
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hr) && IS_INTRESOURCE(pszMessage) )
|
|
{
|
|
idsMessage = MAKERESOURCEINT(pszMessage);
|
|
bStatus DBGCHK = strMsgTemplate.bLoadString(hInstMessage, idsMessage);
|
|
pszMessage = strMsgTemplate;
|
|
|
|
if( !bStatus )
|
|
{
|
|
hr = CreateError();
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
bStatus DBGCHK = strMessage.bvFormat(pszMessage, valist);
|
|
|
|
if( !bStatus )
|
|
{
|
|
hr = CreateError();
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
// check to see if last error format is needed
|
|
switch( dwLastError )
|
|
{
|
|
case kMsgGetLastError:
|
|
|
|
//
|
|
// Get the last error.
|
|
//
|
|
dwLastError = GetLastError();
|
|
|
|
//
|
|
// If for some reason there wasn't an error code, don't
|
|
// append the error string.
|
|
//
|
|
bFormatNeeded = !!dwLastError;
|
|
break;
|
|
|
|
case kMsgNone:
|
|
|
|
//
|
|
// No format message needed.
|
|
//
|
|
bFormatNeeded = FALSE;
|
|
break;
|
|
|
|
default:
|
|
|
|
//
|
|
// Format needed, use provided error value.
|
|
//
|
|
bFormatNeeded = TRUE;
|
|
break;
|
|
}
|
|
|
|
if( bFormatNeeded )
|
|
{
|
|
//
|
|
// If there was a help message map passed then look up the id in this
|
|
// help message map, note this call does not use the last error for finding a match
|
|
//
|
|
if( bLookupHelpMessageMap(pHlpErrMap, idHlpMessage, &pHelpMapEntry) )
|
|
{
|
|
//
|
|
// Extract the message string from the resource file.
|
|
//
|
|
bStatus DBGCHK = strMappedMessage.bLoadString(ghInst, pHelpMapEntry->uIdMessage);
|
|
|
|
//
|
|
// Indicate the message box should have the help button on it.
|
|
//
|
|
uType |= MB_HELP;
|
|
}
|
|
|
|
//
|
|
// If there was not a remapped message then lookup this error value
|
|
// to see if this message has help remapped to it.
|
|
//
|
|
else if( bLookupHelpMessageMap( gMsgHelpTable, dwLastError, &pHelpMapEntry ) )
|
|
{
|
|
//
|
|
// Extract the message string from the resource file.
|
|
//
|
|
bStatus DBGCHK = strMappedMessage.bLoadString(ghInst, pHelpMapEntry->uIdMessage);
|
|
|
|
//
|
|
// Indicate the message box should have the help button on it.
|
|
//
|
|
uType |= MB_HELP;
|
|
}
|
|
|
|
//
|
|
// Check if this last error has a remapped message in the provided message map.
|
|
//
|
|
else if( bLookupErrorMessageMap( pMsgErrMap, dwLastError, &pErrMapEntry ) )
|
|
{
|
|
//
|
|
// Extract the message string from the resource file.
|
|
//
|
|
bStatus DBGCHK = strMappedMessage.bLoadString(ghInst, pErrMapEntry->idsString);
|
|
}
|
|
|
|
//
|
|
// Check if this last error is a known error or the error code is pretty
|
|
// useless for the end users (like "Invalid Handle"), so we need to re-map
|
|
// the text to a generic error message.
|
|
//
|
|
else if( bLookupErrorMessageMap( gGlobalErrorMapTable, dwLastError, &pErrMapEntry ) )
|
|
{
|
|
if( CMsgBoxCounter::GetMsg() )
|
|
{
|
|
// we have a message ID set based on the context where command
|
|
// gets executed - use this message ID instead the defaults
|
|
bStatus DBGCHK = strMappedMessage.bLoadString(ghInst, CMsgBoxCounter::GetMsg());
|
|
}
|
|
else if( IDS_NOT_MAPPED == pErrMapEntry->idsString )
|
|
{
|
|
//
|
|
// The error text, which is coming from the GetLastError/FormatMessage
|
|
// APIs is good enough for this error, so we are going to use it. Just get
|
|
// the error string from the FormatMessage API.
|
|
//
|
|
TResult result(dwLastError);
|
|
if( VALID_OBJ(result) )
|
|
{
|
|
bStatus DBGCHK = result.bGetErrorString(strMappedMessage);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// This error message is re-mapped to a better text. - Just
|
|
// load the re-mapped message string from the resource file.
|
|
//
|
|
bStatus DBGCHK = strMappedMessage.bLoadString(ghInst, pErrMapEntry->idsString);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( IDS_ERR_GENERIC != idsMessage )
|
|
{
|
|
//
|
|
// This error code is unknown or useless to be displayed
|
|
// to the end user. Just load the generic error text string
|
|
// in this case.
|
|
//
|
|
bStatus DBGCHK = strMappedMessage.bLoadString(ghInst, IDS_ERR_GENERIC);
|
|
}
|
|
}
|
|
|
|
if( !bStatus )
|
|
{
|
|
hr = CreateError();
|
|
}
|
|
|
|
if( SUCCEEDED(hr) && !strMappedMessage.bEmpty() )
|
|
{
|
|
//
|
|
// Tack on the mapped, help or error string, with a two space separator.
|
|
//
|
|
bStatus DBGCHK = strMessage.bCat(TEXT(" ")) &&
|
|
strMessage.bCat(strMappedMessage);
|
|
|
|
if( !bStatus )
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if succeeded - show up the message box
|
|
if( SUCCEEDED(hr) )
|
|
{
|
|
if( FAILED(GetCurrentThreadLastPopup(&hwnd)) )
|
|
{
|
|
// if the parent is going to be NULL then push in foreground
|
|
uType |= MB_SETFOREGROUND;
|
|
}
|
|
|
|
// display the message box.
|
|
iResult = PrintUIMessageBox(hwnd, strMessage, pszTitle, uType, UtilHelpCallback, pHelpMapEntry);
|
|
|
|
// log this messsage into the message box counter
|
|
CMsgBoxCounter::LogMessage(uType);
|
|
}
|
|
}
|
|
|
|
if( SUCCEEDED(hr) && piResult )
|
|
{
|
|
*piResult = iResult;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
BOOL
|
|
WINAPI
|
|
UtilHelpCallback(
|
|
IN HWND hwnd,
|
|
IN PVOID pVoid
|
|
)
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
UtilHelpCallback
|
|
|
|
Routine Description:
|
|
|
|
This routine is the called back that is called when a
|
|
message box is displayed with a help button and the user
|
|
clicks on the help button.
|
|
|
|
Arguments:
|
|
|
|
hwnd - handle to windows that recieved the WM_HELP message.
|
|
pVoid - pointer to the user data passsed in the call to
|
|
PrintUIMessageBox. The client can store any value
|
|
in this paramter.
|
|
|
|
Return Value:
|
|
|
|
TRUE callback was successful, FALSE some error occurred.
|
|
|
|
--*/
|
|
|
|
{
|
|
DBGMSG( DBG_TRACE, ( "UtilHelpCallback\n") );
|
|
|
|
//
|
|
// Get a usable pointer to the help map entry.
|
|
//
|
|
MSG_HLPMAP *pHelpMapEntry = reinterpret_cast<MSG_HLPMAP *>( pVoid );
|
|
|
|
|
|
if (pHelpMapEntry)
|
|
{
|
|
//
|
|
// Invoke troubleshooter for this topic
|
|
//
|
|
DWORD_PTR dwRet = (DWORD_PTR)ShellExecute(hwnd, gszOpen,
|
|
TEXT("helpctr.exe"),
|
|
pHelpMapEntry->pszHelpFile,
|
|
NULL,
|
|
SW_SHOWNORMAL);
|
|
return dwRet > 32;
|
|
}
|
|
else
|
|
{
|
|
DBGMSG( DBG_ERROR, ( "UtilHelpCallback (pVoid == NULL)\n") );
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
vShowResourceError(
|
|
HWND hwnd
|
|
)
|
|
{
|
|
//
|
|
// Show up the generic error message here.
|
|
//
|
|
vShowUnexpectedError(hwnd, IDS_PRINTERS_TITLE);
|
|
}
|
|
|
|
VOID
|
|
vShowUnexpectedError(
|
|
HWND hwnd,
|
|
UINT idsTitle
|
|
)
|
|
{
|
|
iMessage( hwnd,
|
|
idsTitle,
|
|
IDS_ERR_GENERIC,
|
|
MB_OK|MB_ICONSTOP|MB_SETFOREGROUND,
|
|
kMsgGetLastError,
|
|
NULL );
|
|
}
|
|
|
|
VOID
|
|
vPrinterSplitFullName(
|
|
IN LPTSTR pszScratch,
|
|
IN DWORD cchScratchSize,
|
|
IN LPCTSTR pszFullName,
|
|
IN LPCTSTR *ppszServer,
|
|
IN LPCTSTR *ppszPrinter
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Splits a fully qualified printer connection name into server and
|
|
printer name parts.
|
|
|
|
Arguments:
|
|
|
|
pszScratch - Scratch buffer used to store output strings.
|
|
|
|
cchScratchSize - Maximum character size of the scratch buffer.
|
|
|
|
pszFullName - Input name of a printer. If it is a printer
|
|
connection (\\server\printer), then we will split it. If
|
|
it is a true local printer (not a masq) then the server is
|
|
szNULL.
|
|
|
|
ppszServer - Receives pointer to the server string. If it is a
|
|
local printer, szNULL is returned.
|
|
|
|
ppszPrinter - Receives a pointer to the printer string. OPTIONAL
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
|
|
{
|
|
LPTSTR pszPrinter;
|
|
|
|
StringCchCopy(pszScratch, cchScratchSize, pszFullName);
|
|
|
|
if (pszFullName[0] != TEXT('\\') || pszFullName[1] != TEXT('\\'))
|
|
{
|
|
//
|
|
// Set *ppszServer to szNULL since it's the local machine.
|
|
//
|
|
*ppszServer = gszNULL;
|
|
pszPrinter = pszScratch;
|
|
}
|
|
else
|
|
{
|
|
*ppszServer = pszScratch;
|
|
pszPrinter = _tcschr(*ppszServer + 2, TEXT('\\'));
|
|
|
|
if (!pszPrinter)
|
|
{
|
|
//
|
|
// We've encountered a printer called "\\server"
|
|
// (only two backslashes in the string). We'll treat
|
|
// it as a local printer. We should never hit this,
|
|
// but the spooler doesn't enforce this. We won't
|
|
// format the string. Server is local, so set to szNULL.
|
|
//
|
|
pszPrinter = pszScratch;
|
|
*ppszServer = gszNULL;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// We found the third backslash; null terminate our
|
|
// copy and set bRemote TRUE to format the string.
|
|
//
|
|
*pszPrinter++ = 0;
|
|
}
|
|
}
|
|
|
|
if (ppszPrinter)
|
|
{
|
|
*ppszPrinter = pszPrinter;
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
bBuildFullPrinterName(
|
|
IN LPCTSTR pszServer,
|
|
IN LPCTSTR pszPrinterName,
|
|
IN TString &strFullName
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Builds a fully qualified printer name from a server name
|
|
and a printer name.
|
|
|
|
Arguments:
|
|
|
|
pszServer - Pointer to server name, if this parameter is null
|
|
the machine name is used. If this parameter is non
|
|
null it is assumed it is a server name of the form
|
|
\\server including the leading wacks.
|
|
|
|
pszPrinterName - Pointer to printer name. This parameter must be a
|
|
a valid string.
|
|
|
|
strFullName - Reference where to return full printer name.
|
|
|
|
Return Value:
|
|
|
|
TRUE strFullName contains a full printer name, FALSE error.
|
|
|
|
--*/
|
|
{
|
|
SPLASSERT( pszPrinterName );
|
|
|
|
TStatusB bStatus;
|
|
bStatus DBGNOCHK = TRUE;
|
|
|
|
bStatus DBGCHK = strFullName.bUpdate( NULL );
|
|
|
|
//
|
|
// If the printer name is already fully qualified then do
|
|
// not prepend the server name.
|
|
//
|
|
if( bStatus && *(pszPrinterName+0) != TEXT('\\') && *(pszPrinterName+1) != TEXT('\\') )
|
|
{
|
|
//
|
|
// If the server name is null, get the current machine name.
|
|
//
|
|
if( !pszServer || !*pszServer )
|
|
{
|
|
bStatus DBGCHK = bGetMachineName( strFullName, FALSE );
|
|
}
|
|
else
|
|
{
|
|
bStatus DBGCHK = strFullName.bUpdate( pszServer );
|
|
}
|
|
|
|
//
|
|
// Append the wack separator.
|
|
//
|
|
if( bStatus )
|
|
{
|
|
bStatus DBGCHK = strFullName.bCat( gszWack );
|
|
}
|
|
}
|
|
|
|
//
|
|
// Append the printer name or copy the already fully qualified printer name.
|
|
//
|
|
if( bStatus )
|
|
{
|
|
bStatus DBGCHK = strFullName.bCat( pszPrinterName );
|
|
}
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
/*++
|
|
|
|
bGetMachineName
|
|
|
|
Routine Description:
|
|
|
|
Get the machine name if the local machine.
|
|
|
|
Arguments:
|
|
|
|
String to return machine name.
|
|
Flag TRUE do not add leading slashes, FALSE add leading slashes.
|
|
|
|
Return Value:
|
|
|
|
TRUE machine name returned.
|
|
|
|
--*/
|
|
BOOL
|
|
bGetMachineName(
|
|
IN OUT TString &strMachineName,
|
|
IN BOOL bNoLeadingSlashes
|
|
)
|
|
{
|
|
TCHAR szBuffer[2 + MAX_COMPUTERNAME_LENGTH + 1];
|
|
DWORD dwBufferSize;
|
|
BOOL bStatus = FALSE;
|
|
DWORD dwSizeAdjust = 0;
|
|
|
|
//
|
|
// Prepend with the wack wack.
|
|
//
|
|
if( !bNoLeadingSlashes ){
|
|
szBuffer[0] = TEXT( '\\' );
|
|
szBuffer[1] = TEXT( '\\' );
|
|
dwSizeAdjust = 2;
|
|
}
|
|
|
|
//
|
|
// Get the computer name.
|
|
//
|
|
dwBufferSize = COUNTOF( szBuffer ) - dwSizeAdjust;
|
|
if( GetComputerName( szBuffer+dwSizeAdjust, &dwBufferSize ) ){
|
|
bStatus = strMachineName.bUpdate( szBuffer );
|
|
}
|
|
|
|
return bStatus;
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
NewFriendlyName
|
|
|
|
Routine Description:
|
|
|
|
Create a new (and unique) friendly name
|
|
|
|
Arguments:
|
|
|
|
pszServerName - print server name
|
|
lpBaseName - base printer name
|
|
lpNewName - new printer name is base name is not unique
|
|
cchNewNameMax - the maximum character (including '\0') count
|
|
of the buffer lpNewName points to.
|
|
pwInstance - [in,out],
|
|
|
|
if NULL (this is by default) then:
|
|
[in] - assume instance equals to zero
|
|
[out] - <NA> (don't care)
|
|
|
|
if not NULL then:
|
|
[in] - instance to start from
|
|
[out] - instance choosen
|
|
|
|
Return Value:
|
|
|
|
TRUE if lpFriendlyName recevies new unique name, FALSE if not
|
|
|
|
--*/
|
|
BOOL
|
|
NewFriendlyName(
|
|
IN LPCTSTR pszServerName,
|
|
IN LPCTSTR lpBaseName,
|
|
OUT LPTSTR lpNewName,
|
|
IN size_t cchNewNameMax,
|
|
IN OUT WORD *pwInstance
|
|
)
|
|
{
|
|
TCHAR szTestName[kPrinterBufMax];
|
|
WORD wCount = 0;
|
|
DWORD cPrinterInfo2 = 0;
|
|
DWORD cbPrinterInfo2 = 0;
|
|
PRINTER_INFO_2 *pPrinterInfo2 = NULL;
|
|
BOOL bStatus = FALSE;
|
|
TStatusB bEnumStatus;
|
|
|
|
if( pwInstance ){
|
|
wCount = *pwInstance;
|
|
}
|
|
|
|
//
|
|
// Enumerate the current printers.
|
|
//
|
|
bEnumStatus DBGCHK = VDataRefresh::bEnumPrinters(
|
|
PRINTER_ENUM_NAME,
|
|
(LPTSTR)pszServerName,
|
|
2,
|
|
(PVOID *)&pPrinterInfo2,
|
|
&cbPrinterInfo2,
|
|
&cPrinterInfo2 );
|
|
//
|
|
// Failure enumerating printers.
|
|
//
|
|
if( !bEnumStatus ){
|
|
DBGMSG( DBG_WARN, ( "Error enumerating printers.\n" ) );
|
|
bStatus = FALSE;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Set upper limit of 1000 tries, just to avoid hanging forever
|
|
//
|
|
bStatus = FALSE;
|
|
for( ; wCount < 1000; wCount++ ){
|
|
|
|
if( CreateUniqueName( szTestName, COUNTOF(szTestName), lpBaseName, wCount )){
|
|
|
|
LPCTSTR pszName;
|
|
BOOL bFound = FALSE;
|
|
|
|
for ( UINT i = 0; i < cPrinterInfo2; i++ ){
|
|
|
|
pszName = pPrinterInfo2[i].pPrinterName;
|
|
|
|
//
|
|
// Strip the server name if not the local machine.
|
|
//
|
|
if( pszServerName ){
|
|
|
|
if( pszName[0] == TEXT( '\\' ) &&
|
|
pszName[1] == TEXT( '\\' ) ){
|
|
|
|
pszName = _tcschr( &pszName[2], TEXT( '\\' ) );
|
|
|
|
if( !pszName ){
|
|
pszName = pPrinterInfo2[i].pPrinterName;
|
|
} else {
|
|
pszName += 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// If name matches indicate found and continue trying to
|
|
// create a unique name.
|
|
//
|
|
if( !lstrcmpi( szTestName, pszName ) ){
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
|
|
//
|
|
// Check if there is a printer share name that matches and
|
|
// if so continue trying to create a unique name.
|
|
//
|
|
if( pPrinterInfo2[i].pShareName &&
|
|
pPrinterInfo2[i].pShareName[0] &&
|
|
!lstrcmpi( szTestName, pPrinterInfo2[i].pShareName ) ){
|
|
bFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If a unique name was found and this was not the
|
|
// first time trough the loop copy the new unique name
|
|
// to the provided buffer.
|
|
//
|
|
if( bFound == FALSE ) {
|
|
if( wCount != 0 ){
|
|
lstrcpyn( lpNewName, szTestName, cchNewNameMax );
|
|
bStatus = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Insure we clean up.
|
|
//
|
|
Cleanup:
|
|
|
|
if( pPrinterInfo2 ){
|
|
|
|
DBGMSG( DBG_TRACE, ( "Releaseing printer info 2 memory.\n" ) );
|
|
FreeMem( pPrinterInfo2 );
|
|
}
|
|
|
|
if( pwInstance ){
|
|
*pwInstance = wCount;
|
|
}
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
CreateUniqueName
|
|
|
|
Routine Description:
|
|
|
|
Create a unique friendly name for this printer. If wInstance
|
|
is 0, just copy the name over. Otherwise, play some games
|
|
with truncating the name so it will fit.
|
|
|
|
Arguments:
|
|
|
|
lpDest - destination buffer
|
|
cchMaxChars - max number of chars in lpDest
|
|
lpBaseName - base printer name
|
|
wInstance - the instance number
|
|
|
|
Return Value:
|
|
|
|
TRUE if we created a name, FALSE if something went wrong
|
|
|
|
Note:
|
|
|
|
This code is from msprint2.dll! we should match their naming scheme.
|
|
|
|
--*/
|
|
BOOL
|
|
CreateUniqueName(
|
|
IN LPTSTR lpDest,
|
|
IN UINT cchMaxChars,
|
|
IN LPCTSTR lpBaseName,
|
|
IN WORD wInstance
|
|
)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
if (wInstance)
|
|
{
|
|
// We want to provide a fully localizable way to create a
|
|
// unique friendly name for each instance. We start with
|
|
// a single string from the resource, and call wsprintf
|
|
// twice, getting something like this:
|
|
//
|
|
// "%%s (Copy %u)" From resource
|
|
// "%s (Copy 2)" After first wsprintf
|
|
// "Foobar Laser (Copy 2)" After second wsprintf
|
|
//
|
|
// We can't make a single wsprintf call, since it has no
|
|
// concept of limiting the string size. We truncate the
|
|
// model name (in a DBCS-aware fashion) to the appropriate
|
|
// size, so the whole string fits in kPrinterBufMax bytes. This
|
|
// may cause some name truncation, but only in cases where
|
|
// the model name is extremely long.
|
|
|
|
CAutoPtrArray<TCHAR> spszFormat1 = new TCHAR[kPrinterBufMax];
|
|
CAutoPtrArray<TCHAR> spszFormat2 = new TCHAR[kPrinterBufMax];
|
|
|
|
if (spszFormat1 && spszFormat2)
|
|
{
|
|
if (LoadString(ghInst, IDS_PRTPROP_UNIQUE_FORMAT, spszFormat1, kPrinterBufMax))
|
|
{
|
|
// wFormatLength is length of format string before inserting
|
|
// the model name. Subtract 2 to remove the "%s", and add
|
|
// 1 to compensate for the terminating NULL, which is
|
|
// counted in the total buffer length, but not the string length
|
|
|
|
wnsprintf(spszFormat2, kPrinterBufMax, spszFormat1, wInstance);
|
|
wnsprintf(lpDest, cchMaxChars, spszFormat2, lpBaseName);
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// some of the allocations failed
|
|
SetLastError(ERROR_OUTOFMEMORY);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if wInstance is zero then just copy lpBaseName to lpDest
|
|
lstrcpyn(lpDest, lpBaseName, cchMaxChars);
|
|
bRet = TRUE;
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
bSplitPath
|
|
|
|
Routine Description:
|
|
|
|
splits a file path in to the path and file component.
|
|
|
|
Arguments:
|
|
|
|
pszScratch - pointer to sratch buffer, must be minimum max path.
|
|
ppszFile - pointer where to return file pointer, may be null.
|
|
ppszPath - pointer where to return path pointer, may be null.
|
|
ppszExt - pointer where to return ext pointer, may be null.
|
|
pszFull - pointer to fully qualified path\file
|
|
|
|
Return Value:
|
|
|
|
TRUE file and path part split.
|
|
|
|
--*/
|
|
BOOL
|
|
bSplitPath(
|
|
IN LPTSTR pszScratch,
|
|
IN size_t cchScratch,
|
|
IN LPCTSTR *ppszFile,
|
|
IN LPCTSTR *ppszPath,
|
|
IN LPCTSTR *ppszExt,
|
|
IN LPCTSTR pszFull
|
|
)
|
|
{
|
|
SPLASSERT( pszScratch );
|
|
SPLASSERT( pszFull );
|
|
|
|
BOOL bStatus = FALSE;
|
|
|
|
if( ppszFile )
|
|
*ppszFile = NULL;
|
|
|
|
if( ppszPath )
|
|
*ppszPath = NULL;
|
|
|
|
if( ppszExt )
|
|
*ppszExt = NULL;
|
|
|
|
if( pszScratch && pszFull )
|
|
{
|
|
StringCchCopy( pszScratch, cchScratch, pszFull );
|
|
|
|
//
|
|
// Full is of the form d:\test
|
|
//
|
|
LPTSTR pszFile = _tcsrchr( pszScratch, TEXT('\\') );
|
|
|
|
//
|
|
// Full is of the form d:test
|
|
//
|
|
if( !pszFile )
|
|
{
|
|
pszFile = _tcsrchr( pszScratch, TEXT(':') );
|
|
}
|
|
|
|
if( pszFile )
|
|
{
|
|
*pszFile++ = NULL;
|
|
|
|
//
|
|
// Locate the file extension.
|
|
//
|
|
if( ppszExt )
|
|
{
|
|
LPTSTR pszExt = _tcsrchr( pszFile, TEXT('.') );
|
|
|
|
if( pszExt )
|
|
{
|
|
*ppszExt = pszExt + 1;
|
|
}
|
|
}
|
|
|
|
if( ppszFile )
|
|
*ppszFile = pszFile;
|
|
|
|
if( ppszPath )
|
|
*ppszPath = pszScratch;
|
|
}
|
|
|
|
//
|
|
// Success.
|
|
//
|
|
bStatus = TRUE;
|
|
}
|
|
|
|
return bStatus;
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
bCopyMultizString
|
|
|
|
Routine Description:
|
|
|
|
Copies a multiz string.
|
|
|
|
Arguments:
|
|
|
|
ppszCopy - pointer where to return multiz string copy.
|
|
pszMultizString - pointer to multiz string.
|
|
|
|
Return Value:
|
|
|
|
TRUE success, FALSE copy multiz string failed, out of memory etc.
|
|
|
|
--*/
|
|
|
|
BOOL
|
|
bCopyMultizString(
|
|
IN LPTSTR *ppszMultizCopy,
|
|
IN LPCTSTR pszMultizString
|
|
)
|
|
{
|
|
SPLASSERT( ppszMultizCopy );
|
|
|
|
BOOL bStatus = TRUE;
|
|
|
|
if( pszMultizString )
|
|
{
|
|
//
|
|
// Calculate the length of the multiz string.
|
|
//
|
|
for( LPCTSTR psz = pszMultizString; psz && *psz; psz += _tcslen( psz ) + 1 );
|
|
|
|
UINT uSize = (UINT)(psz - pszMultizString) + 1;
|
|
|
|
if( uSize )
|
|
{
|
|
//
|
|
// Alocate the new multiz string.
|
|
//
|
|
*ppszMultizCopy = new TCHAR[uSize];
|
|
|
|
if( *ppszMultizCopy )
|
|
{
|
|
CopyMemory( *ppszMultizCopy, pszMultizString, uSize * sizeof( TCHAR ) );
|
|
}
|
|
else
|
|
{
|
|
bStatus = FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
*ppszMultizCopy = NULL;
|
|
}
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
/*++
|
|
|
|
Name:
|
|
|
|
vStripTrailWhiteSpace
|
|
|
|
Routine Description:
|
|
|
|
Strips trailing white space in a single forward scan.
|
|
|
|
Arguments:
|
|
|
|
pszString - Pointer to a null terminated string.
|
|
|
|
Return Value:
|
|
|
|
Nothing.
|
|
|
|
Notes:
|
|
|
|
The string is modified in place.
|
|
|
|
--*/
|
|
VOID
|
|
vStripTrailWhiteSpace(
|
|
IN LPTSTR pszString
|
|
)
|
|
{
|
|
for( LPTSTR pBlank = NULL; pszString && *pszString; ++pszString )
|
|
{
|
|
if( *pszString == TEXT(' ') )
|
|
pBlank = pBlank == NULL ? pszString : pBlank;
|
|
else
|
|
pBlank = NULL;
|
|
}
|
|
if( pBlank )
|
|
{
|
|
*pBlank = 0;
|
|
}
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
bTrimString
|
|
|
|
Routine Description:
|
|
|
|
Trim the string pszTrimMe of any leading or trailing
|
|
characters that are in pszTrimChars.
|
|
|
|
Return Value:
|
|
|
|
TRUE if anything was stripped
|
|
|
|
--*/
|
|
BOOL
|
|
bTrimString(
|
|
IN OUT LPTSTR pszTrimMe,
|
|
IN LPCTSTR pszTrimChars
|
|
)
|
|
{
|
|
BOOL bRet = FALSE;
|
|
LPTSTR psz;
|
|
LPTSTR pszStartMeat;
|
|
LPTSTR pszMark = NULL;
|
|
|
|
SPLASSERT(pszTrimMe);
|
|
SPLASSERT(pszTrimChars);
|
|
|
|
if (pszTrimMe)
|
|
{
|
|
//
|
|
// Trim leading characters.
|
|
//
|
|
psz = pszTrimMe;
|
|
|
|
while (*psz && _tcschr(pszTrimChars, *psz))
|
|
psz++;
|
|
|
|
pszStartMeat = psz;
|
|
|
|
//
|
|
// Trim trailing characters.
|
|
//
|
|
while (*psz)
|
|
{
|
|
if (_tcschr(pszTrimChars, *psz))
|
|
{
|
|
if (!pszMark)
|
|
{
|
|
pszMark = psz;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pszMark = NULL;
|
|
}
|
|
|
|
psz++;
|
|
}
|
|
|
|
//
|
|
// Any trailing characters to clip?
|
|
//
|
|
if (pszMark)
|
|
{
|
|
*pszMark = '\0';
|
|
bRet = TRUE;
|
|
}
|
|
|
|
//
|
|
// Relocate stripped string.
|
|
//
|
|
if (pszStartMeat > pszTrimMe)
|
|
{
|
|
//
|
|
// (+ 1) for null terminator.
|
|
//
|
|
MoveMemory(pszTrimMe, pszStartMeat, (_tcslen(pszStartMeat) + 1) * sizeof(TCHAR));
|
|
bRet = TRUE;
|
|
}
|
|
else
|
|
{
|
|
SPLASSERT(pszStartMeat == pszTrimMe);
|
|
}
|
|
|
|
SPLASSERT(pszTrimMe);
|
|
}
|
|
|
|
return bRet;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
bIsRemote
|
|
|
|
Routine Description:
|
|
|
|
Determines if the specified name is a
|
|
remote machine or local machine name.
|
|
|
|
Arguments:
|
|
|
|
pszString - Pointer to string which contains the machine name.
|
|
|
|
Return Value:
|
|
|
|
TRUE machine name is a remote machine name, FALSE local machine name.
|
|
|
|
--*/
|
|
BOOL
|
|
bIsRemote(
|
|
IN LPCTSTR pszName
|
|
)
|
|
{
|
|
BOOL bRetval = FALSE;
|
|
|
|
//
|
|
// Null pointer or null string is assumed to be
|
|
// the local machine. (Spooler's definition)
|
|
//
|
|
if( !pszName || !*pszName )
|
|
{
|
|
return bRetval;
|
|
}
|
|
|
|
//
|
|
// Get the local machines name.
|
|
//
|
|
TString strLocalName;
|
|
if( !bGetMachineName( strLocalName, TRUE ) )
|
|
{
|
|
DBGMSG( DBG_ERROR, ("bIsRemote::bGetMachineName Failed!") );
|
|
return bRetval;
|
|
}
|
|
|
|
//
|
|
// If the provided name has leading '\\' then compare with
|
|
// '\\' else point to just after the '\\' and compare.
|
|
//
|
|
if( *(pszName+0) == TEXT('\\') && *(pszName+1) == TEXT('\\') )
|
|
{
|
|
pszName += 2;
|
|
}
|
|
|
|
//
|
|
// If the machine names are different then the provided
|
|
// name is assumed to be a remote machine.
|
|
//
|
|
if( _tcsicmp( pszName, strLocalName ) )
|
|
{
|
|
bRetval = TRUE;
|
|
}
|
|
|
|
return bRetval;
|
|
}
|
|
|
|
BOOL
|
|
bLookupErrorMessageMap(
|
|
IN const PMSG_ERRMAP pMsgErrMap,
|
|
IN DWORD dwLastError,
|
|
IN OUT MSG_ERRMAP **ppErrMapEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
bLookupErrorMessageMap
|
|
|
|
Routine Description:
|
|
|
|
Scanns the error message map for a matching
|
|
last error. If the message is found in the map
|
|
the string is returned in the specified string
|
|
mapped message object.
|
|
|
|
Arguments:
|
|
|
|
pMsgErrMap - pointer to error message map
|
|
dwLastError - last error to look for in error message map
|
|
ppErrMapEntry - pointer to error map entry where match was found
|
|
|
|
Return Value:
|
|
|
|
TRUE success, FALSE error occurred.
|
|
|
|
--*/
|
|
{
|
|
BOOL bStatus = FALSE;
|
|
|
|
//
|
|
// If there is a message map, use it.
|
|
//
|
|
if( pMsgErrMap )
|
|
{
|
|
for( UINT i = 0; pMsgErrMap[i].dwError; i++ )
|
|
{
|
|
//
|
|
// If we have a matching error, then return a pointer to this entry.
|
|
//
|
|
if( dwLastError == pMsgErrMap[i].dwError )
|
|
{
|
|
*ppErrMapEntry = &pMsgErrMap[i];
|
|
bStatus = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
BOOL
|
|
bLookupHelpMessageMap(
|
|
IN const PMSG_HLPMAP pMsgHlpMap,
|
|
IN DWORD dwLastError,
|
|
IN OUT MSG_HLPMAP **ppHelpMapEntry
|
|
)
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
bLookupHelpMessageMap
|
|
|
|
Routine Description:
|
|
|
|
Scans the error message map for a matching
|
|
last error. If the message is found in the map
|
|
the string is returned in the specified string
|
|
object.
|
|
|
|
Arguments:
|
|
|
|
pMsgHlpMap - pointer to help error message map
|
|
dwLastError - last error to look for in error message map
|
|
ppHelpMapEntry - pointer to help map entry where match was found
|
|
|
|
Return Value:
|
|
|
|
TRUE success match found, FALSE error occurred.
|
|
|
|
--*/
|
|
{
|
|
BOOL bStatus = FALSE;
|
|
|
|
//
|
|
// If there is a message map, use it.
|
|
//
|
|
if( pMsgHlpMap )
|
|
{
|
|
for( UINT i = 0; pMsgHlpMap[i].dwError; i++ )
|
|
{
|
|
//
|
|
// If we have a matching error, then return a pointer to this entry.
|
|
//
|
|
if( dwLastError == pMsgHlpMap[i].dwError )
|
|
{
|
|
*ppHelpMapEntry = &pMsgHlpMap[i];
|
|
bStatus = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
BOOL
|
|
bGoodLastError(
|
|
IN DWORD dwLastError
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks if this is a good last error - i.e. has
|
|
a good real message associated with it.
|
|
|
|
Arguments:
|
|
|
|
dwLastError - the last error to check for
|
|
|
|
Return Value:
|
|
|
|
TRUE if good, FALSE if not good
|
|
|
|
--*/
|
|
{
|
|
MSG_ERRMAP *pErrMapEntry = NULL;
|
|
return bLookupErrorMessageMap( gGlobalErrorMapTable, dwLastError, &pErrMapEntry );
|
|
}
|
|
|
|
BOOL
|
|
StringA2W(
|
|
IN OUT LPWSTR *ppResult,
|
|
IN LPCSTR pString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert an ansi string to a wide string returning
|
|
a pointer to a newly allocated string.
|
|
|
|
Arguments:
|
|
|
|
ppResult - pointer to where to return pointer to new wide string.
|
|
pString - pointer to ansi string.
|
|
|
|
Return Value:
|
|
|
|
TRUE success, FALSE error occurred.
|
|
|
|
--*/
|
|
{
|
|
SPLASSERT( ppResult || pString );
|
|
|
|
BOOL bReturn = FALSE;
|
|
|
|
INT iLen = ( strlen( pString ) + 1 ) * sizeof( WCHAR );
|
|
|
|
*ppResult = reinterpret_cast<LPWSTR>( AllocMem( iLen ) );
|
|
|
|
if( *ppResult )
|
|
{
|
|
if( MultiByteToWideChar( CP_ACP, 0, pString, -1, *ppResult, iLen ) )
|
|
{
|
|
bReturn = TRUE;
|
|
}
|
|
else
|
|
{
|
|
FreeMem( *ppResult );
|
|
*ppResult = NULL;
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
BOOL
|
|
StringW2A(
|
|
IN OUT LPSTR *ppResult,
|
|
IN LPCWSTR pString
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert a wide string to and ansi string returning
|
|
a pointer to a newly allocated string.
|
|
|
|
Arguments:
|
|
|
|
ppResult - pointer to where to return pointer to new ansi string.
|
|
pString - pointer to wide string.
|
|
|
|
Return Value:
|
|
|
|
TRUE success, FALSE error occurred.
|
|
|
|
--*/
|
|
{
|
|
SPLASSERT( ppResult || pString );
|
|
|
|
BOOL bReturn = FALSE;
|
|
|
|
INT iLen = ( wcslen( pString ) + 1 ) * sizeof( CHAR );
|
|
|
|
*ppResult = reinterpret_cast<LPSTR>( AllocMem( iLen ) );
|
|
|
|
if( *ppResult )
|
|
{
|
|
if( WideCharToMultiByte( CP_ACP, 0, pString, -1, *ppResult, iLen, NULL, NULL ) )
|
|
{
|
|
bReturn = TRUE;
|
|
}
|
|
else
|
|
{
|
|
FreeMem( *ppResult );
|
|
*ppResult = NULL;
|
|
}
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Centers a dialog relative to hwndContext
|
|
|
|
Arguments:
|
|
|
|
hwndToCenter - window handle of dialog to center
|
|
hwndContext - window handle of window to center with respect to
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
|
|
VOID
|
|
CenterDialog(
|
|
IN HWND hwndToCenter,
|
|
IN HWND hwndContext
|
|
)
|
|
{
|
|
POINT point;
|
|
RECT rcContext, rcWindow;
|
|
LONG x, y, w, h;
|
|
LONG sx = GetSystemMetrics(SM_CXSCREEN);
|
|
LONG sy = GetSystemMetrics(SM_CYSCREEN);
|
|
|
|
point.x = point.y = 0;
|
|
if (hwndContext)
|
|
{
|
|
ClientToScreen(hwndContext, &point);
|
|
GetClientRect (hwndContext, &rcContext);
|
|
}
|
|
else
|
|
{
|
|
rcContext.top = rcContext.left = 0;
|
|
rcContext.right = sx;
|
|
rcContext.bottom = sy;
|
|
}
|
|
GetWindowRect (hwndToCenter, &rcWindow);
|
|
|
|
w = rcWindow.right - rcWindow.left + 1;
|
|
h = rcWindow.bottom - rcWindow.top + 1;
|
|
x = point.x + ((rcContext.right - rcContext.left + 1 - w) / 2);
|
|
y = point.y + ((rcContext.bottom - rcContext.top + 1 - h) / 2);
|
|
|
|
if (x + w > sx)
|
|
{
|
|
x = sx - w;
|
|
}
|
|
else if (x < 0)
|
|
{
|
|
x = 0;
|
|
}
|
|
|
|
if (y + h > sy)
|
|
{
|
|
y = sy - h;
|
|
}
|
|
else if (y < 0)
|
|
{
|
|
y = 0;
|
|
}
|
|
|
|
MoveWindow(hwndToCenter, x, y, w, h, FALSE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Name:
|
|
|
|
GetDomainName
|
|
|
|
Routine Description:
|
|
|
|
Returns the DNS domain name where the current computer is located.
|
|
|
|
Arguments:
|
|
|
|
pstrDomainName - pointer to a string refrence where to return the domain name.
|
|
|
|
Return Value:
|
|
|
|
TRUE success, FALSE error occurred.
|
|
|
|
--*/
|
|
BOOL
|
|
GetDomainName(
|
|
OUT TString &strDomainName
|
|
)
|
|
{
|
|
static TString *g_pstrDomainName = NULL;
|
|
|
|
TStatusB bStatus;
|
|
|
|
bStatus DBGNOCHK = FALSE;
|
|
|
|
CCSLock::Locker CSL( *gpCritSec );
|
|
|
|
if( !g_pstrDomainName )
|
|
{
|
|
g_pstrDomainName = new TString;
|
|
|
|
if( VALID_PTR( g_pstrDomainName ) )
|
|
{
|
|
typedef DWORD (WINAPI *pfDsRoleGetPrimaryDomainInformation)( LPCTSTR, DSROLE_PRIMARY_DOMAIN_INFO_LEVEL, PBYTE * );
|
|
typedef VOID (WINAPI *pfDsRoleFreeMemory)( PVOID );
|
|
|
|
TLibrary Lib( gszNetApiLibrary );
|
|
|
|
//
|
|
// Check if the netapi library was loaded.
|
|
//
|
|
bStatus DBGNOCHK = VALID_OBJ( Lib );
|
|
|
|
if (bStatus)
|
|
{
|
|
pfDsRoleGetPrimaryDomainInformation pfGetPrimrayDomainInformation = reinterpret_cast<pfDsRoleGetPrimaryDomainInformation>( Lib.pfnGetProc( "DsRoleGetPrimaryDomainInformation" ) );
|
|
pfDsRoleFreeMemory pfFreeMemory = reinterpret_cast<pfDsRoleFreeMemory>( Lib.pfnGetProc( "DsRoleFreeMemory" ) );
|
|
|
|
if( pfGetPrimrayDomainInformation && pfFreeMemory )
|
|
{
|
|
DWORD dwStatus = ERROR_SUCCESS;
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL;
|
|
|
|
dwStatus = pfGetPrimrayDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *)&pDsRole);
|
|
|
|
if (dwStatus == ERROR_SUCCESS)
|
|
{
|
|
bStatus DBGCHK = g_pstrDomainName->bUpdate( pDsRole->DomainNameDns );
|
|
|
|
if( bStatus )
|
|
{
|
|
bStatus DBGCHK = strDomainName.bUpdate( *g_pstrDomainName );
|
|
}
|
|
}
|
|
|
|
if (pDsRole)
|
|
{
|
|
pfFreeMemory((PVOID) pDsRole);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bStatus DBGCHK = strDomainName.bUpdate( *g_pstrDomainName );
|
|
}
|
|
|
|
DBGMSG( DBG_TRACE, ( "GetDomainName " TSTR "\n", DBGSTR((LPCTSTR)*g_pstrDomainName) ) );
|
|
|
|
return bStatus;
|
|
|
|
}
|
|
|
|
/*++
|
|
|
|
Name:
|
|
|
|
AreWeOnADomain
|
|
|
|
Routine Description:
|
|
|
|
Returns whether we are on a domain.
|
|
|
|
Arguments:
|
|
|
|
pbOnDomain - If TRUE, we are on a domain.
|
|
|
|
Return Value:
|
|
|
|
TRUE success, FALSE error occurred.
|
|
|
|
--*/
|
|
BOOL
|
|
AreWeOnADomain(
|
|
OUT BOOL *pbOnDomain
|
|
)
|
|
{
|
|
typedef DWORD (WINAPI *pfDsRoleGetPrimaryDomainInformation)( LPCTSTR, DSROLE_PRIMARY_DOMAIN_INFO_LEVEL, PBYTE * );
|
|
typedef VOID (WINAPI *pfDsRoleFreeMemory)( PVOID );
|
|
|
|
BOOL bOnDomain = FALSE;
|
|
TStatusB bStatus;
|
|
|
|
TLibrary Lib(gszNetApiLibrary);
|
|
|
|
//
|
|
// Check if the netapi library was loaded.
|
|
//
|
|
bStatus DBGNOCHK = VALID_OBJ( Lib );
|
|
|
|
if (bStatus)
|
|
{
|
|
pfDsRoleGetPrimaryDomainInformation pfGetPrimrayDomainInformation = NULL;
|
|
pfDsRoleFreeMemory pfFreeMemory = NULL;
|
|
PDSROLE_PRIMARY_DOMAIN_INFO_BASIC pDsRole = NULL;
|
|
|
|
pfGetPrimrayDomainInformation = reinterpret_cast<pfDsRoleGetPrimaryDomainInformation>(Lib.pfnGetProc("DsRoleGetPrimaryDomainInformation"));
|
|
pfFreeMemory = reinterpret_cast<pfDsRoleFreeMemory>(Lib.pfnGetProc("DsRoleFreeMemory"));
|
|
|
|
bStatus DBGCHK = pfGetPrimrayDomainInformation && pfFreeMemory;
|
|
|
|
if (bStatus)
|
|
{
|
|
DWORD dwStatus = pfGetPrimrayDomainInformation(NULL, DsRolePrimaryDomainInfoBasic, (PBYTE *)&pDsRole);
|
|
|
|
if (dwStatus)
|
|
{
|
|
SetLastError(dwStatus);
|
|
bStatus DBGCHK = FALSE;
|
|
}
|
|
}
|
|
|
|
if (bStatus)
|
|
{
|
|
bOnDomain = pDsRole->MachineRole == DsRole_RoleMemberWorkstation ||
|
|
pDsRole->MachineRole == DsRole_RoleMemberServer ||
|
|
pDsRole->MachineRole == DsRole_RoleBackupDomainController ||
|
|
pDsRole->MachineRole == DsRole_RolePrimaryDomainController;
|
|
}
|
|
|
|
if (pDsRole)
|
|
{
|
|
pfFreeMemory(pDsRole);
|
|
}
|
|
}
|
|
|
|
*pbOnDomain = bOnDomain;
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
BOOL
|
|
ConstructPrinterFriendlyName(
|
|
IN LPCTSTR pszFullPrinter,
|
|
IN OUT LPTSTR pszPrinterBuffer,
|
|
IN OUT UINT *pcchSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Returns printer name formatted like "printer on server." if the provided
|
|
printer name is fully qualified, else it just returns the passed in
|
|
printer name.
|
|
|
|
Arguments:
|
|
|
|
pszPrinter - pointer to printer name, if fully qualified the
|
|
name is formatted to printer on server, if not a
|
|
fully qualified name then the printer name is not
|
|
altered.
|
|
pszPrinterBuffer - Output buffer to receive the printer name. May be
|
|
NULL of the caller want only the size of the buffer
|
|
returned.
|
|
pcchSize - pointer to a variable that specifies the size
|
|
in characters of pszPrinterBuffer includeing the
|
|
null terminator.
|
|
|
|
Return Value:
|
|
|
|
TRUE success, FALSE error occurred.
|
|
|
|
--*/
|
|
{
|
|
LPCTSTR pszServer;
|
|
LPCTSTR pszPrinter;
|
|
LPCTSTR pszFriendly;
|
|
TString strPrinter;
|
|
TStatusB bStatus;
|
|
TCHAR szScratch[kPrinterBufMax];
|
|
|
|
//
|
|
// Validate the printer name and buffer pointer size.
|
|
//
|
|
if( pszFullPrinter && pcchSize )
|
|
{
|
|
//
|
|
// Split the printer name into its components.
|
|
//
|
|
vPrinterSplitFullName( szScratch, ARRAYSIZE(szScratch), pszFullPrinter, &pszServer, &pszPrinter );
|
|
|
|
//
|
|
// If this printer has a server name component then construct a
|
|
// 'printer on server' friendly name.
|
|
//
|
|
if( pszServer && *pszServer )
|
|
{
|
|
//
|
|
// Strip the leading slashes.
|
|
//
|
|
if( *(pszServer+0) == _T('\\') && *(pszServer+1) == _T('\\') )
|
|
{
|
|
pszServer = pszServer + 2;
|
|
}
|
|
|
|
bStatus DBGCHK = bConstructMessageString( ghInst, strPrinter, IDS_DSPTEMPLATE_WITH_ON, pszPrinter, pszServer );
|
|
|
|
if( bStatus )
|
|
{
|
|
pszFriendly = strPrinter;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Just use the current printer name if it is not fully qualified.
|
|
//
|
|
bStatus DBGNOCHK = TRUE;
|
|
pszFriendly = pszFullPrinter;
|
|
}
|
|
|
|
if( bStatus )
|
|
{
|
|
UINT uFriendlySize = _tcslen( pszFriendly );
|
|
|
|
//
|
|
// If a buffer was provided then check if it is large enough to
|
|
// hold the friendly name. If it is large enough then copy the
|
|
// friendly name into it.
|
|
//
|
|
if( pszPrinterBuffer && ( *pcchSize > uFriendlySize ) )
|
|
{
|
|
StringCchCopy( pszPrinterBuffer, *pcchSize, pszFriendly );
|
|
}
|
|
else
|
|
{
|
|
bStatus DBGNOCHK = FALSE;
|
|
SetLastError( ERROR_INSUFFICIENT_BUFFER );
|
|
}
|
|
|
|
//
|
|
// Return the friendly name size.
|
|
//
|
|
*pcchSize = uFriendlySize + 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bStatus DBGNOCHK = FALSE;
|
|
SetLastError( ERROR_INVALID_PARAMETER );
|
|
}
|
|
|
|
return bStatus;
|
|
}
|
|
|
|
BOOL
|
|
WINAPIV
|
|
bConstructMessageString(
|
|
IN HINSTANCE hInst,
|
|
IN TString &strString,
|
|
IN INT iResId,
|
|
IN ...
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine formats the specified string using a format string.
|
|
The format string is extracted from the resouce file using the
|
|
passed in resouce id. The format is of the form required by
|
|
FormatMessage API. See FormatMessage for more details.
|
|
|
|
Arguments:
|
|
|
|
hInst - Instance handle where resource is loaded.
|
|
strString - Place to return resultant formatted string,
|
|
iResId - Format string resource id.
|
|
.. - Variable number of arguments
|
|
|
|
Return Value:
|
|
|
|
TRUE success, FALSE if error occurred.
|
|
|
|
--*/
|
|
{
|
|
LPTSTR pszRet = NULL;
|
|
DWORD dwBytes = 0;
|
|
TString strRes;
|
|
TStatusB bStatus;
|
|
va_list pArgs;
|
|
|
|
//
|
|
// Load the resource string.
|
|
//
|
|
bStatus DBGCHK = strRes.bLoadString( hInst, iResId );
|
|
|
|
if( bStatus )
|
|
{
|
|
va_start( pArgs, iResId );
|
|
|
|
//
|
|
// Format the message.
|
|
//
|
|
dwBytes = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_STRING,
|
|
strRes,
|
|
0,
|
|
0,
|
|
(LPTSTR)&pszRet,
|
|
0,
|
|
&pArgs );
|
|
va_end( pArgs );
|
|
|
|
//
|
|
// If the number of bytes is non zero the API formatted the
|
|
// string.
|
|
//
|
|
if( dwBytes )
|
|
{
|
|
//
|
|
// Update the return string object.
|
|
//
|
|
bStatus DBGCHK = strString.bUpdate( pszRet );
|
|
|
|
//
|
|
// Release the formated string.
|
|
//
|
|
if( pszRet )
|
|
{
|
|
LocalFree(pszRet);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bStatus DBGNOCHK = FALSE;
|
|
}
|
|
}
|
|
return bStatus;
|
|
}
|
|
|
|
BOOL
|
|
bIsLocalPrinterNameValid(
|
|
IN LPCTSTR pszPrinter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine checks if the specified local printer name contains
|
|
any illegal characters. Note the spooler also inforces the illegal
|
|
characters but doing the check in the UI code is more efficent and
|
|
nicer to the user, rather than having to call add printer with a
|
|
printer name that is invalid.
|
|
|
|
Arguments:
|
|
|
|
pszPrinter - pointer to local printer name
|
|
|
|
Return Value:
|
|
|
|
TRUE printer name is valid, FALSE name contains illegal character
|
|
|
|
--*/
|
|
{
|
|
BOOL bRetval = TRUE;
|
|
|
|
//
|
|
// Check if the name has any illegal characters.
|
|
//
|
|
for( LPCTSTR p = pszPrinter; p && *p; p++ )
|
|
{
|
|
if( *p == TEXT( ',' ) || *p == TEXT( '!' ) || *p == TEXT( '\\' ) )
|
|
{
|
|
bRetval = FALSE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return bRetval;
|
|
}
|
|
|
|
BOOL
|
|
CheckRestrictions(
|
|
IN HWND hwnd,
|
|
IN RESTRICTIONS rest
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Verify the resrtictions of the explorer policies
|
|
|
|
Arguments:
|
|
|
|
hwnd - Handle to the parent window
|
|
rest - Restriction to check.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Restriction apply
|
|
FALSE - Not restrcited
|
|
|
|
--*/
|
|
{
|
|
if (SHRestricted(rest))
|
|
{
|
|
iMessage( hwnd,
|
|
IDS_RESTRICTIONSTITLE,
|
|
IDS_RESTRICTIONS,
|
|
MB_OK|MB_ICONSTOP,
|
|
kMsgNone,
|
|
NULL );
|
|
return TRUE;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
VOID
|
|
vAdjustHeaderColumns(
|
|
IN HWND hwndLV,
|
|
IN UINT uColumnsCount,
|
|
IN UINT uPercentages[]
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adjusts the columns to a certan percentages
|
|
in a list view
|
|
|
|
Arguments:
|
|
|
|
hwndLV - Handle to a list view control
|
|
uColumnsCount - Number of columns
|
|
uPercentages - The percentage of the total width
|
|
each column will take. 100 == SUM(uPercentages)
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// Calculate the header column width.
|
|
//
|
|
RECT rc;
|
|
DWORD Interval = 20; // why not
|
|
|
|
if( GetClientRect( hwndLV, &rc ))
|
|
{
|
|
Interval = rc.right;
|
|
}
|
|
|
|
for( UINT iCol = 0; iCol < uColumnsCount; ++iCol )
|
|
{
|
|
ListView_SetColumnWidth( hwndLV, iCol, (Interval * uPercentages[iCol]) / 100 );
|
|
}
|
|
}
|
|
|
|
VOID
|
|
LoadPrinterIcons(
|
|
IN LPCTSTR pszPrinterName,
|
|
OUT HICON *phLargeIcon,
|
|
OUT HICON *phSmallIcon
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Loads a printer icon with the requested size.
|
|
|
|
Arguments:
|
|
|
|
IN pszPrinterName - the printer name of the printer we want icon for
|
|
OUT phLargeIcon - where to put the large icon
|
|
OUT phSmallIcon - where to put the small icon
|
|
|
|
Return Value:
|
|
|
|
--*/
|
|
{
|
|
// invoke shell to do the right thing
|
|
Printer_LoadIcons(pszPrinterName, phLargeIcon, phSmallIcon);
|
|
}
|
|
|
|
BOOL
|
|
CommandConfirmationPurge(
|
|
IN HWND hwnd,
|
|
IN LPCTSTR pszPrinterName
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dispalys confirmation message box for the purge command.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE user wants to execute command, FALSE cancel.
|
|
|
|
--*/
|
|
{
|
|
TCHAR szText[kStrMax+kPrinterBufMax] = {0};
|
|
UINT nSize = COUNTOF( szText );
|
|
|
|
//
|
|
// Build the printer status string.
|
|
//
|
|
ConstructPrinterFriendlyName(pszPrinterName, szText, &nSize);
|
|
|
|
return iMessage( hwnd,
|
|
IDS_PRINTERS_TITLE,
|
|
IDS_PRINTER_SUREPURGE,
|
|
MB_YESNO|MB_ICONQUESTION,
|
|
kMsgNone,
|
|
NULL,
|
|
szText ) == IDYES;
|
|
}
|
|
|
|
BOOL
|
|
bMatchTemplate(
|
|
IN LPCTSTR pszTemplate,
|
|
IN LPCTSTR pszText
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Simple template matching routine
|
|
|
|
Arguments:
|
|
|
|
pszTemplate - pointer to a simple template string
|
|
pszText - pointer to a text to match against the template
|
|
|
|
Return Value:
|
|
|
|
TRUE matched, FALSE not matched
|
|
|
|
--*/
|
|
{
|
|
int iLen = lstrlen(pszTemplate);
|
|
if( iLen != lstrlen(pszText) )
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
TString strText, strTemplate;
|
|
if( strText.bUpdate(pszText) &&
|
|
strTemplate.bUpdate(pszTemplate) )
|
|
{
|
|
// convert to uppercase prior the matching
|
|
strText.vToUpper();
|
|
strTemplate.vToUpper();
|
|
|
|
for( int i = 0; i < iLen; i++ )
|
|
{
|
|
if( TEXT('?') == strTemplate[i] )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( strTemplate[i] != strText[i] )
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// failed to allocate the strings
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
bIsFaxPort(
|
|
IN LPCTSTR pszName,
|
|
IN LPCTSTR pszMonitor
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Determins if this port is a fax port. A fax
|
|
port is a port managed by the fax port monitor.
|
|
|
|
Arguments:
|
|
|
|
pszName - pointer to port name.
|
|
pszMonitor - pointer to port monitor.
|
|
|
|
Return Value:
|
|
|
|
TRUE port is a fax port, FALSE port is not a fax port.
|
|
|
|
--*/
|
|
{
|
|
// the monitor name can be null on downlevel servers.
|
|
return ((pszName && 0 == lstrcmp(pszName, FAX_MONITOR_PORT_NAME)) ||
|
|
(pszMonitor && 0 == lstrcmp(pszMonitor, FAX_MONITOR_NAME)));
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
shows up an appropriate error message based on the last error code
|
|
(if spcified) this function is exported from printui to shell, so
|
|
shell error messages can be consistent with us.
|
|
|
|
Arguments:
|
|
|
|
piResult - the result from MessageBox
|
|
hModule - module who contains the test resources
|
|
hwnd - parent window
|
|
pszTitle - msg title (uID or text)
|
|
pszMessage - msg itself or msg context (uID or text)
|
|
uType - msg type
|
|
iLastError - the last error (-1 means don't append the last error text)
|
|
|
|
Return Value:
|
|
|
|
S_OK on success or COM error otherwise
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
ShowErrorMessageSC(
|
|
OUT INT *piResult,
|
|
IN HINSTANCE hModule,
|
|
IN HWND hwnd,
|
|
IN LPCTSTR pszTitle,
|
|
IN LPCTSTR pszMessage,
|
|
IN UINT uType,
|
|
IN DWORD dwCode
|
|
)
|
|
{
|
|
return Internal_Message(piResult, hModule, hwnd, pszTitle, pszMessage,
|
|
uType, dwCode, NULL, NULL, 0, NULL);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
shows up an appropriate error message based on the last error code
|
|
(if spcified) this function is exported from printui to shell, so
|
|
shell error messages can be consistent with us.
|
|
|
|
Arguments:
|
|
|
|
piResult - the result from MessageBox
|
|
hModule - module who contains the test resources
|
|
hwnd - parent window
|
|
pszTitle - msg title (uID or text)
|
|
pszMessage - msg itself or msg context (uID or text)
|
|
uType - msg type
|
|
iLastError - the last error (-1 means don't append the last error text)
|
|
|
|
Return Value:
|
|
|
|
S_OK on success or COM error otherwise
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
ShowErrorMessageHR(
|
|
OUT INT *piResult,
|
|
IN HINSTANCE hModule,
|
|
IN HWND hwnd,
|
|
IN LPCTSTR pszTitle,
|
|
IN LPCTSTR pszMessage,
|
|
IN UINT uType,
|
|
IN HRESULT hr
|
|
)
|
|
{
|
|
return Internal_Message(piResult, hModule, hwnd, pszTitle, pszMessage,
|
|
uType, SCODE_CODE(GetScode(hr)), NULL, NULL, 0, NULL);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
abbreviates text by adding ellipses if text is longer than cchMaxChars
|
|
|
|
Arguments:
|
|
|
|
pszText - [in] text to abbreviate
|
|
cchMaxChars - [in] max characters of the output (abbreviated) text
|
|
pstrAbbreviatedText - [out] the abbreviated text
|
|
|
|
Return Value:
|
|
|
|
S_OK on success or COM error otherwise
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
AbbreviateText(
|
|
IN LPCTSTR pszText,
|
|
IN UINT cchMaxChars,
|
|
OUT TString *pstrAbbreviatedText
|
|
)
|
|
{
|
|
HRESULT hr = E_INVALIDARG;
|
|
if( pszText && cchMaxChars && pstrAbbreviatedText )
|
|
{
|
|
TStatusB bStatus;
|
|
if( lstrlen(pszText) <= cchMaxChars )
|
|
{
|
|
// the text fits in the buffer, just copy it
|
|
bStatus DBGCHK = pstrAbbreviatedText->bUpdate(pszText);
|
|
hr = bStatus ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
// the text doesn't fit in the buffer, add ellipses
|
|
TString strEllipses;
|
|
bStatus DBGCHK = strEllipses.bLoadString(ghInst, IDS_TEXT_ELLIPSES);
|
|
|
|
if( bStatus && strEllipses.uLen() < cchMaxChars )
|
|
{
|
|
TCHAR szBuffer[255];
|
|
|
|
lstrcpyn(szBuffer, pszText, min(cchMaxChars - strEllipses.uLen(), ARRAYSIZE(szBuffer)));
|
|
|
|
bStatus DBGCHK = pstrAbbreviatedText->bFormat(TEXT("%s%s"), szBuffer, static_cast<LPCTSTR>(strEllipses));
|
|
|
|
hr = bStatus ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
// generate proper HRESULT from Win32 last error
|
|
hr = CreateHRFromWin32();
|
|
}
|
|
}
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
MoveWindowWrap
|
|
|
|
Routine Description:
|
|
|
|
Move specific window by given offset.
|
|
|
|
Arguments:
|
|
|
|
hWnd -- window handle
|
|
deltaX -- horizonal offset
|
|
deltaY -- vertical offset
|
|
|
|
Return Value:
|
|
|
|
Return value by MoveWindow().
|
|
|
|
--*/
|
|
BOOL
|
|
MoveWindowWrap(
|
|
HWND hwnd,
|
|
int deltaX,
|
|
int deltaY
|
|
)
|
|
{
|
|
RECT rect;
|
|
|
|
GetWindowRect(hwnd, &rect);
|
|
MapWindowPoints(HWND_DESKTOP, GetParent(hwnd), (LPPOINT)&rect, 2);
|
|
return MoveWindow(hwnd, rect.left + deltaX, rect.top + deltaY,
|
|
rect.right - rect.left, rect.bottom - rect.top, TRUE);
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
IsColorPrinter
|
|
|
|
Routine Description:
|
|
|
|
Checks if a printer supports color
|
|
|
|
Arguments:
|
|
|
|
pszPrinter - printer name
|
|
pbColor - pointer to bool
|
|
|
|
Return Value:
|
|
|
|
S_OK - the function succeded and pbColor can be used
|
|
anything else - the function failed
|
|
|
|
--*/
|
|
HRESULT
|
|
IsColorPrinter(
|
|
IN LPCWSTR pszPrinter,
|
|
IN OUT LPBOOL pbColor
|
|
)
|
|
{
|
|
TStatusH hStatus;
|
|
|
|
hStatus DBGNOCHK = E_INVALIDARG;
|
|
|
|
if (pszPrinter && pbColor)
|
|
{
|
|
hStatus DBGNOCHK = E_FAIL;
|
|
|
|
//
|
|
// Create the printer data access class.
|
|
//
|
|
TPrinterDataAccess Data(pszPrinter,
|
|
TPrinterDataAccess::kResourcePrinter,
|
|
TPrinterDataAccess::kAccessRead);
|
|
|
|
//
|
|
// Relax the return type checking, BOOL are not REG_DWORD but REG_BINARY
|
|
//
|
|
Data.RelaxReturnTypeCheck(TRUE);
|
|
|
|
//
|
|
// Initialize the data class.
|
|
//
|
|
if (Data.Init())
|
|
{
|
|
hStatus DBGCHK = Data.Get(SPLDS_DRIVER_KEY, SPLDS_PRINT_COLOR, *pbColor);
|
|
}
|
|
}
|
|
|
|
return hStatus;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
IsGuestAccessMode
|
|
|
|
Routine Description:
|
|
|
|
Checks if guest access mode is enabled for the local machine.
|
|
|
|
Arguments:
|
|
|
|
pbGuestAccessMode - [out] TRUE if guest access mode is enabled
|
|
for the local machine and FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
S_OK if succeded and OLE error otherwise.
|
|
|
|
History:
|
|
|
|
Lazar Ivanov (LazarI), Mar-19-2001 - created.
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
IsGuestAccessMode(
|
|
OUT BOOL *pbGuestAccessMode
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pbGuestAccessMode)
|
|
{
|
|
if (GetCurrentPlatform() == kPlatform_IA64)
|
|
{
|
|
//
|
|
// Guest mode is always off for IA64 machine since we don't have
|
|
// homenetworking wizard for IA64 machine
|
|
//
|
|
*pbGuestAccessMode = FALSE;
|
|
}
|
|
else if (IsOS(OS_PERSONAL))
|
|
{
|
|
//
|
|
// Guest mode is always on for Personal.
|
|
//
|
|
*pbGuestAccessMode = TRUE;
|
|
}
|
|
else if (IsOS(OS_PROFESSIONAL) && !IsOS(OS_DOMAINMEMBER))
|
|
{
|
|
//
|
|
// Professional, not in a domain. Check the "ForceGuest" value
|
|
// in the registry.
|
|
//
|
|
LONG errCode;
|
|
CAutoHandleHKEY shKey;
|
|
DWORD dwValue = 0;
|
|
DWORD dwValueSize = sizeof(dwValue);
|
|
|
|
errCode = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
|
|
TEXT("SYSTEM\\CurrentControlSet\\Control\\LSA"),
|
|
0, KEY_QUERY_VALUE, &shKey);
|
|
|
|
if (errCode == ERROR_SUCCESS)
|
|
{
|
|
errCode = RegQueryValueEx(shKey, TEXT("ForceGuest"), NULL, NULL,
|
|
(LPBYTE)&dwValue, &dwValueSize);
|
|
|
|
if (errCode == ERROR_SUCCESS)
|
|
{
|
|
//
|
|
// Declare success here.
|
|
//
|
|
*pbGuestAccessMode = (1 == dwValue);
|
|
hr = S_OK;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(errCode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(errCode);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Not in guest mode.
|
|
//
|
|
*pbGuestAccessMode = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
IsGuestEnabledForNetworkAccess
|
|
|
|
Routine Description:
|
|
|
|
Checks if the Guest account is enabled for network access.
|
|
|
|
Arguments:
|
|
|
|
pbGuestEnabledForNetworkAccess - [out] TRUE if the Guest
|
|
account is enabled for network access and FALSE otherwise.
|
|
|
|
Return Value:
|
|
|
|
S_OK if succeded and OLE error otherwise.
|
|
|
|
History:
|
|
|
|
Lazar Ivanov (LazarI), Mar-19-2001 - created.
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
IsGuestEnabledForNetworkAccess(
|
|
OUT BOOL *pbGuestEnabledForNetworkAccess
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pbGuestEnabledForNetworkAccess)
|
|
{
|
|
CRefPtrCOM<ILocalMachine> spLM;
|
|
VARIANT_BOOL vbEnabled = VARIANT_FALSE;
|
|
|
|
//
|
|
// Get pointer to ILocalMachine to check ILM_GUEST_NETWORK_LOGON
|
|
//
|
|
if (SUCCEEDED(hr = CoCreateInstance(CLSID_ShellLocalMachine, NULL,
|
|
CLSCTX_INPROC_SERVER, IID_ILocalMachine, (void**)&spLM)) &&
|
|
SUCCEEDED(hr = spLM->get_isGuestEnabled(ILM_GUEST_NETWORK_LOGON, &vbEnabled)))
|
|
{
|
|
//
|
|
// Declare success here.
|
|
//
|
|
*pbGuestEnabledForNetworkAccess = (VARIANT_TRUE == vbEnabled);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
IsSharingEnabled
|
|
|
|
Routine Description:
|
|
|
|
Checks if sharing is enabled or we should launch the home
|
|
networking wizard to enable sharing before allowing the
|
|
users to share out printers.
|
|
|
|
Arguments:
|
|
|
|
pbSharingEnabled - [out] TRUE if sharing is enabled and
|
|
FALSE otherwise (in which case we should launch HNW)
|
|
|
|
Return Value:
|
|
|
|
S_OK if succeded and OLE error otherwise.
|
|
|
|
History:
|
|
|
|
Lazar Ivanov (LazarI), Mar-19-2001 - created.
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
IsSharingEnabled(
|
|
OUT BOOL *pbSharingEnabled
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (pbSharingEnabled)
|
|
{
|
|
BOOL bGuestAccessMode = FALSE;
|
|
BOOL bGuestEnabledForNetworkAccess = FALSE;
|
|
|
|
//
|
|
// First get the values of bGuestAccessMode & bGuestEnabledForNetworkAccess
|
|
//
|
|
if (SUCCEEDED(hr = IsGuestAccessMode(&bGuestAccessMode)) &&
|
|
SUCCEEDED(hr = IsGuestEnabledForNetworkAccess(&bGuestEnabledForNetworkAccess)))
|
|
{
|
|
//
|
|
// Sharing is enabled *only* if not in guest mode OR the guest account is
|
|
// enabled for network access.
|
|
//
|
|
*pbSharingEnabled = (!bGuestAccessMode || bGuestEnabledForNetworkAccess);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
LaunchHomeNetworkingWizard
|
|
|
|
Routine Description:
|
|
|
|
Launches the home networking wizard
|
|
|
|
Arguments:
|
|
|
|
hwnd - [in] window handle to show the wizard modally against
|
|
pbRebootRequired - [out] TRUE if the user made a change which
|
|
requires to reboot the system.
|
|
|
|
Return Value:
|
|
|
|
S_OK if succeded and OLE error otherwise.
|
|
|
|
History:
|
|
|
|
Lazar Ivanov (LazarI), Mar-19-2001 - created.
|
|
|
|
--*/
|
|
|
|
HRESULT
|
|
LaunchHomeNetworkingWizard(
|
|
IN HWND hwnd,
|
|
OUT BOOL *pbRebootRequired
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (hwnd && pbRebootRequired)
|
|
{
|
|
CRefPtrCOM<IHomeNetworkWizard> spHNW;
|
|
|
|
//
|
|
// Get pointer to IHomeNetworkWizard, get the top level owner of hwnd
|
|
// and then show the wizard.
|
|
//
|
|
if (SUCCEEDED(hr = CoCreateInstance(CLSID_HomeNetworkWizard, NULL,
|
|
CLSCTX_INPROC_SERVER, IID_IHomeNetworkWizard, (void**)&spHNW)) &&
|
|
SUCCEEDED(hr = GetCurrentThreadLastPopup(&hwnd)) &&
|
|
SUCCEEDED(hr = spHNW->ShowWizard(hwnd, pbRebootRequired)))
|
|
{
|
|
//
|
|
// Declare success here.
|
|
//
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = E_INVALIDARG;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
IsRedirectedPort
|
|
|
|
Routine Description:
|
|
|
|
Determines if the specified port name is a redirected port.
|
|
|
|
Arguments:
|
|
|
|
pszPortName - Port name.
|
|
|
|
Return Value:
|
|
|
|
Standard HRESULT value.
|
|
|
|
--*/
|
|
HRESULT
|
|
IsRedirectedPort(
|
|
IN LPCTSTR pszPortName,
|
|
OUT BOOL *pbIsRedirected
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!pszPortName || lstrlen(pszPortName) < 2)
|
|
{
|
|
*pbIsRedirected = FALSE;
|
|
}
|
|
else
|
|
{
|
|
*pbIsRedirected = (*(pszPortName+0) == TEXT('\\')) && (*(pszPortName+1) == TEXT('\\'));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
MsgInfinitelyWaitForSingleObject
|
|
|
|
Routine Description:
|
|
|
|
Waits infinitely on an object
|
|
while pumping UI messages
|
|
|
|
Arguments:
|
|
|
|
hWaitableObject - object to wait on.
|
|
|
|
Return Value:
|
|
|
|
Standard COM error.
|
|
|
|
--*/
|
|
HRESULT
|
|
MsgInfinitelyWaitForSingleObject(
|
|
IN HANDLE hWaitableObject
|
|
)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwWait = 0;
|
|
MSG msg = {0};
|
|
|
|
ASSERT(hWaitableObject);
|
|
|
|
//
|
|
// Wait on the object while keep processing window messages.
|
|
//
|
|
|
|
for (;;)
|
|
{
|
|
dwWait = MsgWaitForMultipleObjects(1, &hWaitableObject, FALSE, INFINITE, QS_ALLINPUT);
|
|
|
|
if ((WAIT_OBJECT_0 + 1) == dwWait)
|
|
{
|
|
//
|
|
// An input message was posted in the message queue.
|
|
// Process the message (if any) and continue waiting.
|
|
//
|
|
|
|
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Either the object got signaled, the wait timed
|
|
// out OR it failed for some reason. In any case
|
|
// we should break the loop and exit.
|
|
//
|
|
|
|
if (WAIT_OBJECT_0 == dwWait)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
else if (WAIT_TIMEOUT == dwWait)
|
|
{
|
|
hr = S_FALSE;
|
|
}
|
|
else if (WAIT_FAILED == dwWait)
|
|
{
|
|
hr = CreateHRFromWin32();
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|