602 lines
14 KiB
C
602 lines
14 KiB
C
/*++
|
|
|
|
Copyright (c) 1997 Microsoft Corporation
|
|
All rights reserved
|
|
|
|
Module Name:
|
|
|
|
devmode.c
|
|
|
|
Abstract:
|
|
|
|
Handles per-user devmode implementation.
|
|
|
|
Author:
|
|
|
|
Environment:
|
|
|
|
User Mode -Win32
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
|
|
#include "local.h"
|
|
#include <offsets.h>
|
|
|
|
/********************************************************************
|
|
|
|
Forward prototypes
|
|
|
|
********************************************************************/
|
|
|
|
BOOL
|
|
bGetDevModeLocation(
|
|
IN HKEY hKeyUser, OPTIONAL
|
|
IN LPCWSTR pszPrinter,
|
|
OUT PHKEY phKey,
|
|
OUT LPCWSTR *ppszValue
|
|
);
|
|
|
|
|
|
const WCHAR gszPrinterConnections[] = L"Printers\\Connections\\";
|
|
const WCHAR gszDevMode[] = L"DevMode";
|
|
const WCHAR gszDevModePerUserLocal[] = L"Printers\\DevModePerUser";
|
|
|
|
/********************************************************************
|
|
|
|
Private Functions
|
|
|
|
********************************************************************/
|
|
|
|
|
|
DWORD
|
|
RegOpenConnectionKey(
|
|
HKEY hKeyUser,
|
|
LPWSTR pszPrinter,
|
|
PHKEY phKey
|
|
)
|
|
{
|
|
PWCHAR pszPrinterScratch = NULL;
|
|
DWORD dwRetValue = ERROR_SUCCESS;
|
|
DWORD cchSize = MAX_UNC_PRINTER_NAME + PRINTER_NAME_SUFFIX_MAX + COUNTOF( gszPrinterConnections );
|
|
|
|
if (pszPrinter &&
|
|
wcslen(pszPrinter) < MAX_UNC_PRINTER_NAME + PRINTER_NAME_SUFFIX_MAX) {
|
|
|
|
if (pszPrinterScratch = AllocSplMem(cchSize * sizeof(WCHAR))) {
|
|
|
|
wcscpy(pszPrinterScratch, gszPrinterConnections);
|
|
|
|
FormatPrinterForRegistryKey(pszPrinter,
|
|
&pszPrinterScratch[ COUNTOF( gszPrinterConnections )-1] );
|
|
|
|
dwRetValue = RegOpenKeyEx(hKeyUser,
|
|
pszPrinterScratch,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
phKey );
|
|
|
|
FreeSplMem(pszPrinterScratch);
|
|
|
|
} else {
|
|
|
|
dwRetValue = GetLastError();
|
|
}
|
|
|
|
} else {
|
|
|
|
dwRetValue = ERROR_INVALID_PARAMETER;
|
|
}
|
|
|
|
return dwRetValue;
|
|
}
|
|
|
|
|
|
/********************************************************************
|
|
|
|
Public functions
|
|
|
|
********************************************************************/
|
|
|
|
BOOL
|
|
bSetDevModePerUser(
|
|
HANDLE hKeyUser,
|
|
LPCWSTR pszPrinter,
|
|
PDEVMODE pDevMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Sets the per-user DevMode in HKCU.
|
|
|
|
Arguments:
|
|
|
|
hKeyUser - HKEY_CURRENT_USER handle. OPTIONAL
|
|
|
|
pszPrinter - Printer to set.
|
|
|
|
pDevMode - DevMode to save. If NULL, deletes value.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
FALSE - Failure
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY hKey = NULL;
|
|
LPWSTR pszValue = NULL;
|
|
DWORD Status;
|
|
|
|
if( !pszPrinter ){
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Retrieve the location of the DevMode.
|
|
//
|
|
if( !bGetDevModeLocation( hKeyUser,
|
|
pszPrinter,
|
|
&hKey,
|
|
&pszValue )){
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
if( !pDevMode ){
|
|
|
|
//
|
|
// NULL, so delete the value.
|
|
//
|
|
Status = RegDeleteValue( hKey, pszValue );
|
|
|
|
//
|
|
// If value not found, don't fail.
|
|
//
|
|
if( Status == ERROR_FILE_NOT_FOUND ){
|
|
Status = ERROR_SUCCESS;
|
|
}
|
|
|
|
} else {
|
|
|
|
Status = RegSetValueEx( hKey,
|
|
pszValue,
|
|
0,
|
|
REG_BINARY,
|
|
(PBYTE)pDevMode,
|
|
pDevMode->dmSize +
|
|
pDevMode->dmDriverExtra );
|
|
|
|
if( Status == ERROR_SUCCESS ){
|
|
|
|
//
|
|
// Notify everyone that the DevMode has changed.
|
|
//
|
|
SendNotifyMessage( HWND_BROADCAST,
|
|
WM_DEVMODECHANGE,
|
|
0,
|
|
(LPARAM)pszPrinter );
|
|
}
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
|
|
if( Status != ERROR_SUCCESS ){
|
|
SetLastError( Status );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL
|
|
bGetDevModePerUser(
|
|
HKEY hKeyUser,
|
|
LPCWSTR pszPrinter,
|
|
PDEVMODE *ppDevMode
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves the per-user DevMode based on the current user.
|
|
|
|
Arguments:
|
|
|
|
hKeyUser - HKEY_CURRENT_USER handle. OPTIONAL
|
|
|
|
pszPrinter - Printer to get.
|
|
|
|
ppDevMode - Receives pointer to devmode. Must be freed by callee.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success: able to check if per-user DevMode exists. *ppDevMode
|
|
is NULL if no per-user DevMode is there. (TRUE does not indicate
|
|
that a per-user DevMode was found, only that we successfully checked.)
|
|
|
|
FALSE - Failure.
|
|
|
|
--*/
|
|
|
|
{
|
|
HKEY hKey = NULL;
|
|
LPWSTR pszValue = NULL;
|
|
LONG Status;
|
|
|
|
*ppDevMode = NULL;
|
|
|
|
if( !pszPrinter ){
|
|
SetLastError( ERROR_INVALID_HANDLE );
|
|
return FALSE;
|
|
}
|
|
//
|
|
// Retrieve the location of the DevMode.
|
|
//
|
|
if( !bGetDevModeLocation( hKeyUser,
|
|
pszPrinter,
|
|
&hKey,
|
|
&pszValue )){
|
|
|
|
Status = GetLastError();
|
|
|
|
} else {
|
|
|
|
DWORD cbDevModePerUser;
|
|
|
|
//
|
|
// Key exists. See if we can read it and get the per-user DevMode.
|
|
//
|
|
Status = RegQueryInfoKey( hKey,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&cbDevModePerUser,
|
|
NULL,
|
|
NULL );
|
|
|
|
if( Status == ERROR_SUCCESS ){
|
|
|
|
if( cbDevModePerUser >= MIN_DEVMODE_SIZEW ){
|
|
|
|
*ppDevMode = AllocSplMem( cbDevModePerUser );
|
|
|
|
if( !*ppDevMode ){
|
|
|
|
Status = GetLastError();
|
|
|
|
} else {
|
|
|
|
Status = RegQueryValueEx( hKey,
|
|
pszValue,
|
|
NULL,
|
|
NULL,
|
|
(PBYTE)*ppDevMode,
|
|
&cbDevModePerUser );
|
|
|
|
if (ERROR_SUCCESS == Status) {
|
|
|
|
Status = IsValidDevmode(*ppDevMode, cbDevModePerUser);
|
|
|
|
//
|
|
// If the devmode isn't valid, delete it and treat it
|
|
// as if the devmode cannot be found.
|
|
//
|
|
if (ERROR_SUCCESS != Status) {
|
|
|
|
//
|
|
// If we can delete the key, just consider it as if
|
|
// it does not exist.
|
|
//
|
|
if (ERROR_SUCCESS == RegDeleteValue(hKey, pszValue)) {
|
|
|
|
Status = ERROR_FILE_NOT_FOUND;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( Status != ERROR_SUCCESS ){
|
|
FreeSplMem( *ppDevMode );
|
|
*ppDevMode = NULL;
|
|
}
|
|
|
|
//
|
|
// Allow ERROR_FILE_NOT_FOUND to return success. *ppDevMode
|
|
// is still NULL, but we return TRUE to indicate that we
|
|
// successfully checked the registry--we just didn't find one.
|
|
//
|
|
if( Status == ERROR_FILE_NOT_FOUND ){
|
|
Status = ERROR_SUCCESS;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
RegCloseKey( hKey );
|
|
}
|
|
|
|
if( Status != ERROR_SUCCESS ){
|
|
SetLastError( Status );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
BOOL
|
|
bCompatibleDevMode(
|
|
PPRINTHANDLE pPrintHandle,
|
|
PDEVMODE pDevModeBase,
|
|
PDEVMODE pDevModeNew
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Check if two DevModes are compatible (e.g., they can be used
|
|
interchangably).
|
|
|
|
This is done by checking size and version information. Not
|
|
foolproof, but the best we can do since we can't look at private
|
|
information.
|
|
|
|
Arguments:
|
|
|
|
pPrintHandle - Printer to check.
|
|
|
|
pDevModeBase - Known good DevMode.
|
|
|
|
pDevModeNew - DevMode to check.
|
|
|
|
Return Value:
|
|
|
|
TRUE - Appears compatible.
|
|
FALSE - Not compatible.
|
|
|
|
--*/
|
|
{
|
|
if( !pDevModeBase || ! pDevModeNew ){
|
|
return FALSE;
|
|
}
|
|
|
|
return pDevModeBase->dmSize == pDevModeNew->dmSize &&
|
|
pDevModeBase->dmDriverExtra == pDevModeNew->dmDriverExtra &&
|
|
pDevModeBase->dmSpecVersion == pDevModeNew->dmSpecVersion &&
|
|
pDevModeBase->dmDriverVersion == pDevModeNew->dmDriverVersion;
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
Support Functions
|
|
|
|
********************************************************************/
|
|
|
|
BOOL
|
|
bGetDevModeLocation(
|
|
IN HKEY hKeyUser, OPTIONAL
|
|
IN LPCWSTR pszPrinter,
|
|
OUT PHKEY phKey,
|
|
OUT LPCWSTR *ppszValue
|
|
)
|
|
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Retrieves the location of the per-user DevMode.
|
|
|
|
On success, caller is responsible for closing phKey. ppszValue's
|
|
life is dependent on pszPrinter.
|
|
|
|
Arguments:
|
|
|
|
hKeyUser - HKEY_CURRENT_USER key--optional. If not specified, current
|
|
impersonation used.
|
|
|
|
pszPrinter - Printer to use.
|
|
|
|
phKey - Receives R/W key of per-user DevMode. On success, this
|
|
must be closed by caller.
|
|
|
|
ppszValue - Receives value of per-user DevMode (where to read/write).
|
|
|
|
Return Value:
|
|
|
|
TRUE - Success
|
|
|
|
FALSE - Failure, LastError set.
|
|
|
|
--*/
|
|
|
|
{
|
|
HANDLE hKeyClose = NULL;
|
|
DWORD Status;
|
|
|
|
*phKey = NULL;
|
|
*ppszValue = NULL;
|
|
|
|
if( !hKeyUser ){
|
|
|
|
hKeyUser = GetClientUserHandle( KEY_READ|KEY_WRITE );
|
|
hKeyClose = hKeyUser;
|
|
}
|
|
|
|
if( !hKeyUser ){
|
|
|
|
//
|
|
// Failed to get impersonation information. Probably because
|
|
// we're not impersonating, so there's no per-user information.
|
|
//
|
|
Status = GetLastError();
|
|
|
|
} else {
|
|
|
|
//
|
|
// If it starts with two backslashes, it may be either a connection
|
|
// or a masq printer.
|
|
//
|
|
if( pszPrinter[0] == L'\\' && pszPrinter[1] == L'\\' )
|
|
{
|
|
|
|
//
|
|
// Query the registry for pszPrinter and look for DevMode.
|
|
// First look at the HKCU:Printer\Connections.
|
|
//
|
|
|
|
if((Status = RegOpenConnectionKey(hKeyUser,(LPWSTR)pszPrinter,phKey)) == ERROR_SUCCESS)
|
|
{
|
|
*ppszValue = gszDevMode;
|
|
}
|
|
}
|
|
|
|
//
|
|
// If we didn't find it in Printer\Connection, then it
|
|
// must be a local or masq printer.
|
|
//
|
|
if( !*ppszValue ){
|
|
|
|
DWORD dwIgnore;
|
|
|
|
//
|
|
// Not a connection or didn't exist in the connections key.
|
|
// Look in the Printers\DevModePerUser key.
|
|
//
|
|
Status = RegCreateKeyEx( hKeyUser,
|
|
gszDevModePerUserLocal,
|
|
0,
|
|
NULL,
|
|
0,
|
|
KEY_READ | KEY_WRITE,
|
|
NULL,
|
|
phKey,
|
|
&dwIgnore );
|
|
|
|
if( Status == ERROR_SUCCESS ){
|
|
*ppszValue = pszPrinter;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( hKeyClose ){
|
|
RegCloseKey( hKeyClose );
|
|
}
|
|
|
|
if( Status != ERROR_SUCCESS ){
|
|
SetLastError( Status );
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL bGetDevModePerUserEvenForShares(
|
|
HKEY hKeyUser,
|
|
LPCWSTR pszPrinter,
|
|
PDEVMODE *ppDevMode
|
|
)
|
|
{
|
|
BOOL RetVal = FALSE;
|
|
HANDLE hPrinter;
|
|
|
|
if(OpenPrinter((LPWSTR)pszPrinter,&hPrinter,NULL))
|
|
{
|
|
DWORD PrntrInfoSize=0,PrntrInfoSizeReq=0;
|
|
PPRINTER_INFO_2 pPrinterInfo2 = NULL;
|
|
|
|
if(!GetPrinter(hPrinter,
|
|
2,
|
|
(LPBYTE)pPrinterInfo2,
|
|
PrntrInfoSize,
|
|
&PrntrInfoSizeReq) &&
|
|
(GetLastError() == ERROR_INSUFFICIENT_BUFFER) &&
|
|
(pPrinterInfo2 = (PPRINTER_INFO_2)AllocSplMem((PrntrInfoSize = PrntrInfoSizeReq))) &&
|
|
GetPrinter(hPrinter,
|
|
2,
|
|
(LPBYTE)pPrinterInfo2,
|
|
PrntrInfoSize,
|
|
&PrntrInfoSizeReq))
|
|
{
|
|
RetVal = bGetDevModePerUser( hKeyUser,
|
|
pPrinterInfo2->pPrinterName,
|
|
ppDevMode );
|
|
}
|
|
|
|
if(hPrinter)
|
|
ClosePrinter(hPrinter);
|
|
|
|
if(pPrinterInfo2)
|
|
FreeSplMem(pPrinterInfo2);
|
|
}
|
|
else
|
|
{
|
|
RetVal = bGetDevModePerUser( hKeyUser,
|
|
pszPrinter,
|
|
ppDevMode );
|
|
}
|
|
|
|
return(RetVal);
|
|
}
|
|
|
|
|
|
/*++
|
|
|
|
Routine Name:
|
|
|
|
IsValidDevmode
|
|
|
|
Description:
|
|
|
|
Check to see whether the devmode passed in is at least as advertised in its
|
|
buffer.
|
|
|
|
Arguments:
|
|
|
|
pDevmode - The devmode
|
|
DevmodeSize - The size of the devmode as advertised in the registry.
|
|
|
|
Return Value:
|
|
|
|
An HRESULT
|
|
|
|
--*/
|
|
DWORD
|
|
IsValidDevmode(
|
|
IN PDEVMODE pDevmode,
|
|
IN size_t DevmodeSize
|
|
)
|
|
{
|
|
DWORD Status = ERROR_SUCCESS;
|
|
|
|
//
|
|
// This guarantees that the devmode is at least big enough to get the dmSize
|
|
// and dmDriverExtra.
|
|
//
|
|
Status = DevmodeSize >= MIN_DEVMODE_SIZEW ? ERROR_SUCCESS : ERROR_INVALID_DATA;
|
|
|
|
//
|
|
// The advertised devmode size must be contained inside
|
|
//
|
|
if (ERROR_SUCCESS == Status)
|
|
{
|
|
Status = (size_t)(pDevmode->dmSize + pDevmode->dmDriverExtra) <= DevmodeSize ? ERROR_SUCCESS : ERROR_INVALID_DATA;
|
|
}
|
|
|
|
return Status;
|
|
}
|