Windows2000/private/shell/shell32/printer1.c
2020-09-30 17:12:32 +02:00

1188 lines
32 KiB
C

#include "shellprv.h"
#pragma hdrstop
#include "printer.h"
typedef struct tagPRINTERS_RUNDLL_INFO
{
UINT uAction;
LPTSTR lpBuf1;
LPTSTR lpBuf2;
} PRINTERS_RUNDLL_INFO, * LPPRI;
// forward prototypes
void Printer_OpenMe(HWND hwnd, LPCTSTR pName, LPCTSTR pServer, BOOL fModal);
VOID Printers_ProcessCommand(HWND hwndStub, LPPRI lpPRI, BOOL fModal);
TCHAR const c_szPrintersGetCommand_RunDLL[] = TEXT("SHELL32,PrintersGetCommand_RunDLL");
SHSTDAPI_(BOOL) SHInvokePrinterCommand(
IN HWND hwnd,
IN UINT uAction,
IN LPCTSTR lpBuf1,
IN LPCTSTR lpBuf2,
IN BOOL fModal)
{
#ifndef WINNT
UINT cchBuf1, cchBuf2 = 0;
#endif
PRINTERS_RUNDLL_INFO PRI;
BOOL fRet = FALSE;
PRI.uAction = uAction;
PRI.lpBuf1 = (LPTSTR)lpBuf1;
PRI.lpBuf2 = (LPTSTR)lpBuf2;
#ifdef WINNT
// Use same processes on NT.
#else
if (!fModal) // force modal
{
LPTSTR lpCmdLine;
cchBuf1 = lstrlen(lpBuf1);
if (lpBuf2)
{
cchBuf2 = lstrlen(lpBuf2);
}
else
{
lpBuf2 = TEXT("");
}
lpCmdLine = (LPTSTR)Alloc(SIZEOF(c_szPrintersGetCommand_RunDLL)
+ (cchBuf1 + cchBuf2 + 1 + 1 + 30) * SIZEOF(TCHAR));
// Size of "Shell32,PrintersGetComm... ###,###,###,{lpbuf1} {lpbuf2}
// 1 = space
// 1 = NUL terminator
// 30 = max length of digits in uAction,cchBuf1,cchBuf2
if (!lpCmdLine)
return FALSE;
wsprintf(lpCmdLine, TEXT("%s %d,%d,%d,%s %s"), c_szPrintersGetCommand_RunDLL,
uAction, cchBuf1, cchBuf2, lpBuf1, lpBuf2);
// Why do we want this as another process? Isn't another thread enough?
fRet = SHRunDLLProcess(hwnd, lpCmdLine, SW_SHOWNORMAL, IDS_PRINTERS, FALSE);
Free(lpCmdLine);
return TRUE;
}
#endif
Printers_ProcessCommand(hwnd, &PRI, fModal);
return TRUE;
}
#ifdef UNICODE
SHSTDAPI_(BOOL)
SHInvokePrinterCommandA(
IN HWND hwnd,
IN UINT uAction,
IN LPCSTR lpBuf1, OPTIONAL
IN LPCSTR lpBuf2, OPTIONAL
IN BOOL fModal)
{
WCHAR szBuf1[MAX_PATH];
WCHAR szBuf2[MAX_PATH];
if (lpBuf1)
{
MultiByteToWideChar(CP_ACP, 0, lpBuf1, -1, szBuf1, SIZECHARS(szBuf1));
lpBuf1 = (LPCSTR)szBuf1;
}
if (lpBuf2)
{
MultiByteToWideChar(CP_ACP, 0, lpBuf2, -1, szBuf2, SIZECHARS(szBuf2));
lpBuf2 = (LPCSTR)szBuf2;
}
return SHInvokePrinterCommand(hwnd, uAction, (LPCWSTR)lpBuf1, (LPCWSTR)lpBuf2, fModal);
}
#else
SHSTDAPI_(BOOL)
SHInvokePrinterCommandW(
IN HWND hwnd,
IN UINT uAction,
IN LPCWSTR lpBuf1, OPTIONAL
IN LPCWSTR lpBuf2, OPTIONAL
IN BOOL fModal)
{
CHAR szBuf1[MAX_PATH];
CHAR szBuf2[MAX_PATH];
if (lpBuf1)
{
WideCharToMultiByte(CP_ACP, 0, lpBuf1, -1, szBuf1, SIZECHARS(szBuf1), NULL, NULL);
lpBuf1 = (LPCWSTR)szBuf1;
}
if (lpBuf2)
{
WideCharToMultiByte(CP_ACP, 0, lpBuf2, -1, szBuf2, SIZECHARS(szBuf2), NULL, NULL);
lpBuf2 = (LPCWSTR)szBuf2;
}
return SHInvokePrinterCommand(hwnd, uAction, (LPCSTR)lpBuf1, (LPCSTR)lpBuf2, fModal);
}
#endif // UNICODE
// needs to be referenced by printobj.c for now. When NotifyOnPrinterChange
// is up and running, we will no longer need this to notify open queue views,
// as those windows can listen to the notifications directly themselves.
HDSA hdsaPrintDef = NULL;
extern IDataObjectVtbl c_CPrintersIDLDataVtbl;
VOID WINAPI PrintersGetCommand_RunDLL_Common(HWND hwndStub, HINSTANCE hAppInstance, LPTSTR lpszCmdLine, int nCmdShow)
{
PRINTERS_RUNDLL_INFO PRI;
UINT cchBuf1;
UINT cchBuf2;
LPTSTR lpComma;
LPTSTR lpCommaNext;
lpComma = StrChr(lpszCmdLine,TEXT(','));
if (lpComma == NULL)
{
goto BadCmdLine;
}
*lpComma = TEXT('\0'); // Terminate it here
PRI.uAction = StrToLong(lpszCmdLine);
lpCommaNext = StrChr(lpComma+1,TEXT(','));
if (lpCommaNext == NULL)
{
goto BadCmdLine;
}
*lpCommaNext = TEXT('\0'); // Terminate it here
cchBuf1 = StrToLong(lpComma+1);
lpComma = lpCommaNext;
lpCommaNext = StrChr(lpComma+1,TEXT(','));
if (lpCommaNext == NULL)
{
goto BadCmdLine;
}
*lpCommaNext = TEXT('\0'); // Terminate it here
cchBuf2 = StrToLong(lpComma+1);
lpComma = lpCommaNext;
PRI.lpBuf1 = lpComma+1; // Just past the comma
*(PRI.lpBuf1+cchBuf1) = '\0';
if (cchBuf2 == 0)
{
PRI.lpBuf2 = NULL;
}
else
{
PRI.lpBuf2 = PRI.lpBuf1+cchBuf1+1;
}
#ifdef WINNT
// Make this modal.
Printers_ProcessCommand(hwndStub, &PRI, TRUE);
#else
Printers_ProcessCommand(hwndStub, &PRI, FALSE);
#endif
return;
BadCmdLine:
DebugMsg(DM_ERROR, TEXT("pgc_rd: bad command line: %s"), lpszCmdLine);
return;
}
VOID WINAPI PrintersGetCommand_RunDLL(HWND hwndStub, HINSTANCE hAppInstance, LPSTR lpszCmdLine, int nCmdShow)
{
#ifdef UNICODE
UINT iLen = lstrlenA(lpszCmdLine)+1;
LPWSTR lpwszCmdLine;
lpwszCmdLine = (LPWSTR)LocalAlloc(LPTR,iLen*SIZEOF(WCHAR));
if (lpwszCmdLine)
{
MultiByteToWideChar(CP_ACP, 0,
lpszCmdLine, -1,
lpwszCmdLine, iLen);
PrintersGetCommand_RunDLL_Common( hwndStub,
hAppInstance,
lpwszCmdLine,
nCmdShow );
LocalFree(lpwszCmdLine);
}
#else
PrintersGetCommand_RunDLL_Common( hwndStub,
hAppInstance,
lpszCmdLine,
nCmdShow );
#endif
}
VOID WINAPI PrintersGetCommand_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
{
#ifdef UNICODE
PrintersGetCommand_RunDLL_Common( hwndStub,
hAppInstance,
lpwszCmdLine,
nCmdShow );
#else
UINT iLen = WideCharToMultiByte(CP_ACP, 0,
lpwszCmdLine, -1,
NULL, 0, NULL, NULL)+1;
LPSTR lpszCmdLine;
lpszCmdLine = (LPSTR)LocalAlloc(LPTR,iLen);
if (lpszCmdLine)
{
WideCharToMultiByte(CP_ACP, 0,
lpwszCmdLine, -1,
lpszCmdLine, iLen,
NULL, NULL);
PrintersGetCommand_RunDLL_Common( hwndStub,
hAppInstance,
lpszCmdLine,
nCmdShow );
LocalFree(lpszCmdLine);
}
#endif
}
/*
lpPRI structure description based on uAction.
uAction lpBuf1 lpBuf2
OPEN, printer, server
PROPERTIES, printer, SheetName
NETINSTALL, printer,
NETINSTALLLINK, printer, target directory to create link
OPENNETPRN, printer,
TESTPAGE printer
*/
VOID Printers_ProcessCommand(HWND hwndStub, LPPRI lpPRI, BOOL fModal)
{
switch (lpPRI->uAction)
{
case PRINTACTION_OPEN:
if (!lstrcmpi(lpPRI->lpBuf1, c_szNewObject))
{
#ifdef WINNT
Printers_PrinterSetup(hwndStub, MSP_NEWPRINTER_MODELESS,
lpPRI->lpBuf1, lpPRI->lpBuf2);
#else
LPITEMIDLIST pidl;
pidl = Printers_PrinterSetup(hwndStub, MSP_NEWPRINTER,
lpPRI->lpBuf1, lpPRI->lpBuf2);
if (pidl)
ILFree(pidl);
#endif
}
else
{
Printer_OpenMe(hwndStub, lpPRI->lpBuf1, lpPRI->lpBuf2, fModal);
}
break;
#ifdef WINNT
// BUGBUG
// Using hdsa and Printer_OneWindowAction guards against duplicate
// windows within a single processes. If you launch 2 explorers,
// then you can get duplicates.
case PRINTACTION_SERVERPROPERTIES:
{
static HDSA hdsaServerProp = NULL;
LPCTSTR pszServer = (LPTSTR)(lpPRI->lpBuf1);
// we should never get called with c_szNewObject
ASSERT(lstrcmpi(lpPRI->lpBuf1, c_szNewObject));
Printer_OneWindowAction(hwndStub, pszServer, &hdsaServerProp,
vServerPropPages, 0, fModal);
break;
}
case PRINTACTION_DOCUMENTDEFAULTS:
{
static HDSA hdsaDocDef = NULL;
// we should never get called with c_szNewObject
ASSERT(lstrcmpi(lpPRI->lpBuf1, c_szNewObject));
Printer_OneWindowAction(hwndStub, lpPRI->lpBuf1, &hdsaDocDef,
vDocumentDefaults, (LPARAM)(lpPRI->lpBuf2),
fModal);
break;
}
#endif
case PRINTACTION_PROPERTIES:
{
static HDSA hdsaProp = NULL;
// we should never get called with c_szNewObject
ASSERT(lstrcmpi(lpPRI->lpBuf1, c_szNewObject));
#ifdef WINNT // PRINTQ
Printer_OneWindowAction(hwndStub, lpPRI->lpBuf1, &hdsaProp,
vPrinterPropPages, (LPARAM)(lpPRI->lpBuf2),
fModal);
#else
Printer_OneWindowAction(hwndStub, lpPRI->lpBuf1, &hdsaProp,
Printer_Properties, (LPARAM)(lpPRI->lpBuf2),
fModal);
#endif
break;
}
case PRINTACTION_NETINSTALLLINK:
case PRINTACTION_NETINSTALL:
{
LPITEMIDLIST pidl = Printers_PrinterSetup(hwndStub, MSP_NETPRINTER, lpPRI->lpBuf1, NULL);
if (pidl)
{
if (lpPRI->uAction == PRINTACTION_NETINSTALLLINK)
{
// BUGBUG: Do we want Out Of Memory errors on failure?
LPITEMIDLIST pidlParent = SHCloneSpecialIDList(NULL, CSIDL_PRINTERS, FALSE);
if (pidlParent)
{
LPDATAOBJECT pDataObj;
LPCITEMIDLIST pidlChild = ILFindLastID(pidl);
HRESULT hres = CIDLData_CreateFromIDArray(pidlParent, 1, &pidlChild, &pDataObj);
if (SUCCEEDED(hres))
{
SHCreateLinks(hwndStub, lpPRI->lpBuf2, pDataObj, SHCL_USETEMPLATE, NULL);
pDataObj->lpVtbl->Release(pDataObj);
}
ILFree(pidlParent);
}
}
ILFree(pidl);
}
break;
}
case PRINTACTION_OPENNETPRN:
{
LPITEMIDLIST pidl;
pidl = Printers_GetInstalledNetPrinter(lpPRI->lpBuf1);
// Can't find it? Install one.
if (!pidl)
{
int i = ShellMessageBox(HINST_THISDLL,
hwndStub,
MAKEINTRESOURCE(IDS_INSTALLNETPRINTER),
MAKEINTRESOURCE(IDS_PRINTERS),
MB_YESNO|MB_ICONINFORMATION,
lpPRI->lpBuf1);
if (i == IDYES)
{
pidl = Printers_PrinterSetup(hwndStub, MSP_NETPRINTER, lpPRI->lpBuf1, NULL);
}
}
if (pidl)
{
LPIDPRINTER pidp = (LPIDPRINTER)ILFindLastID(pidl);
Printer_OpenMe(hwndStub, pidp->cName, NULL, fModal);
ILFree(pidl);
}
break;
} // case PRINTACTION_OPENNETPRN
case PRINTACTION_TESTPAGE:
Printers_PrinterSetup(hwndStub, MSP_TESTPAGEPARTIALPROMPT,
lpPRI->lpBuf1, NULL);
break;
default:
DebugMsg(TF_WARNING, TEXT("PrintersGetCommand_RunDLL() received unrecognized uAction %d"), lpPRI->uAction);
break;
}
}
void Printer_OpenMe(HWND hwnd, LPCTSTR pName, LPCTSTR pServer, BOOL fModal)
{
BOOL fOpened = FALSE;
HKEY hkeyPrn;
TCHAR buf[50+MAXNAMELEN];
hwnd = NULL; // BUGBUG::: set hwnd NULL to make it work correctly
wsprintf(buf, TEXT("Printers\\%s"), pName);
if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, buf, &hkeyPrn))
{
SHELLEXECUTEINFO sei =
{
SIZEOF(SHELLEXECUTEINFO),
SEE_MASK_CLASSKEY | SEE_MASK_FLAG_NO_UI, // fMask
hwnd, // hwnd
NULL, // lpVerb
pName, // lpFile
NULL, // lpParameters
NULL, // lpDirectory
SW_SHOWNORMAL, // nShow
NULL, // hInstApp
NULL, // lpIDList
NULL, // lpClass
hkeyPrn, // hkeyClass
0, // dwHotKey
NULL // hIcon
};
fOpened = ShellExecuteEx(&sei);
RegCloseKey(hkeyPrn);
}
if (!fOpened)
{
#ifdef WINNT // PRINTQ
Printer_OneWindowAction(hwnd, pName, &hdsaPrintDef,
vQueueCreate, (LPARAM)fModal, FALSE);
#else
Printer_OneWindowAction(hwnd, pName, &hdsaPrintDef,
Printer_ViewQueue, 0, FALSE);
#endif
}
}
#ifdef DEBUG
// Type checking
const RUNDLLPROCA lpfnRunDLL = PrintersGetCommand_RunDLL;
const RUNDLLPROCW lpfnRunDLLW = PrintersGetCommand_RunDLLW;
#endif
// Printers_GetInstalledNetPrinter returns the absolute pidl to the first
// printer found connected to lpNetPath. If no printers are found with this
// connection, NULL is returned.
LPITEMIDLIST Printers_GetInstalledNetPrinter(LPCTSTR lpNetPath)
{
LPITEMIDLIST pidl = NULL;
// this function is called from several places in the shell for point&print,
// so make sure we have winspool loaded
#ifdef WINNT
LPCTSTR pszPrinter = lpNetPath;
HANDLE hPrinter = Printer_OpenPrinter( lpNetPath );
LPPRINTER_INFO_2 pInfo2 = NULL;
LPPRINTER_INFO_4 pPrinter;
DWORD dwNumPrinters;
// Try and resolve to the real printer name rather than the
// share name, since the printer info from EnumPrinters info4
// are printer names, and not share names.
if (hPrinter)
{
pInfo2 = (LPPRINTER_INFO_2)Printer_GetPrinterInfo(hPrinter, 2);
if (pInfo2)
{
pszPrinter = pInfo2->pPrinterName;
}
Printer_ClosePrinter(hPrinter);
}
// Retrieve all the printers on the local machine. It's
// cheaper to use level 4 since we won't hit the net.
dwNumPrinters = Printers_EnumPrinters(NULL,
PRINTER_ENUM_LOCAL |
PRINTER_ENUM_FAVORITE,
4, &pPrinter);
#else
LPPRINTER_INFO_5 pPrinter;
DWORD dwNumPrinters = Printers_EnumPrinters(NULL, PRINTER_ENUM_LOCAL,
5, &pPrinter);
#endif
if (dwNumPrinters)
{
DWORD i;
for (i = 0; i < dwNumPrinters ; i++)
{
#ifdef WINNT
if (!lstrcmpi(pszPrinter, pPrinter[i].pPrinterName))
#else
if (!lstrcmpi(lpNetPath, pPrinter[i].pPortName))
#endif
{
DebugMsg(DM_TRACE, TEXT("Found installed net printer: %s"), pPrinter[i].pPrinterName);
pidl = Printers_GetPidl(NULL, pPrinter[i].pPrinterName);
break;
}
}
LocalFree((HLOCAL)pPrinter);
}
#ifdef WINNT
if (pInfo2)
{
LocalFree(pInfo2);
}
#endif
return pidl;
}
// Arguments:
// pidl -- (absolute) pidl to the object of interest
// Return '"""<Printer Name>""" """<Driver Name>""" """<Path>"""' if success,
// NULL if failure
// We need """ because shlexec strips the outer quotes and converts "" to "
UINT Printer_GetPrinterInfoFromPidl(LPCITEMIDLIST pidl, LPTSTR *plpParms)
{
LPCIDPRINTER pidp = (LPCIDPRINTER)ILFindLastID(pidl);
LPTSTR lpBuffer = NULL;
HANDLE hPrinter;
UINT uErr = ERROR_NOT_ENOUGH_MEMORY;
hPrinter = Printer_OpenPrinter(pidp->cName);
if (hPrinter)
{
PRINTER_INFO_5 *pPrinter;
pPrinter = Printer_GetPrinterInfo(hPrinter, 5);
if (pPrinter)
{
DRIVER_INFO_2 *pPrinterDriver;
pPrinterDriver = Printer_GetPrinterDriver(hPrinter, 2);
if (pPrinterDriver)
{
LPTSTR lpDriverName = PathFindFileName(pPrinterDriver->pDriverPath);
lpBuffer = (void*)LocalAlloc(LPTR, (2+lstrlen(pidp->cName)+1+
2+lstrlen(lpDriverName)+1+
2+lstrlen(pPrinter->pPortName)+1) * SIZEOF(TCHAR));
if (lpBuffer)
{
//wsprintf(lpBuffer,"\"\"\"%s\"\"\" \"\"\"%s\"\"\" \"\"\"%s\"\"\"",
wsprintf(lpBuffer,TEXT("\"%s\" \"%s\" \"%s\""),
pidp->cName, lpDriverName, pPrinter->pPortName);
uErr = ERROR_SUCCESS;
}
LocalFree((HLOCAL)pPrinterDriver);
}
LocalFree((HLOCAL)pPrinter);
}
Printer_ClosePrinter(hPrinter);
}
else
{
// HACK: special case this error return in calling function,
// as we need a special error message
uErr = ERROR_SUCCESS;
}
*plpParms = lpBuffer;
return(uErr);
}
// Arguments:
// hwndParent -- Specifies the parent window.
// szFilePath -- The file to printed.
void Printer_PrintFile(HWND hWnd, LPCTSTR szFilePath, LPCITEMIDLIST pidl)
{
LPTSTR lpFName; // name of file we're printing
TCHAR szFPath[MAX_PATH]; // path to file we're printing
LPCTSTR lpOperation; // ShellExecuteEx operation
LPTSTR lpParms; // parameters for above operation
UINT uErr; // return value from ShellExecuteEx
BOOL fTryOnce = TRUE; // we retry once
LPTSTR lpRelease = NULL;
SHELLEXECUTEINFO ExecInfo;
lpFName = PathFindFileName(szFilePath);
lstrcpy(szFPath, szFilePath);
PathRemoveFileSpec(szFPath);
lpOperation = c_szPrintTo;
uErr = Printer_GetPrinterInfoFromPidl(pidl, &lpParms);
if (uErr != ERROR_SUCCESS)
{
goto DisplayError;
}
if (!lpParms)
{
// If you rename a printer and then try to use a link to that
// printer, we hit this case. Also, if you get a link to a printer
// on another computer, we'll likely hit this case.
ShellMessageBox(HINST_THISDLL, hWnd,
#ifdef WINNT
MAKEINTRESOURCE(IDS_CANTPRINT),
#else
MAKEINTRESOURCE(IDS_PRINTERNAME_CHANGED),
#endif
MAKEINTRESOURCE(IDS_PRINTINGERROR),
MB_OK|MB_ICONINFORMATION);
return;
}
lpRelease = lpParms;
// print the file
// REVIEW: It would probably be better to get the context menu of
// the file and check for the verb PrintTo and then the verb Print,
// avoiding ShellExecuteEx completely. Less work being done that way.
TryAgain:
DebugMsg(TF_PRINTER, TEXT("Printer_PrintFile: verb=[%s] file=[%s|%s] parms=[%s]"),lpOperation, szFPath, lpFName, lpParms);
FillExecInfo(ExecInfo, hWnd, lpOperation, lpFName, lpParms, szFPath, SW_SHOWNORMAL);
ExecInfo.fMask |= SEE_MASK_FLAG_DDEWAIT | SEE_MASK_FLAG_NO_UI;
if (!ShellExecuteEx(&ExecInfo))
{
uErr = GetLastError();
switch (uErr)
{
case ERROR_NO_ASSOCIATION:
if (fTryOnce)
{
LPCIDPRINTER pidp = (LPCIDPRINTER)ILFindLastID(pidl);
BOOL fTryAgain;
fTryOnce = FALSE;
// we failed a "PrintTo", set up for a "Print"
fTryAgain = IsDefaultPrinter(pidp->cName, 0);
if (!fTryAgain)
{
// this isn't the default printer, ask first
fTryAgain =
IDYES == ShellMessageBox(
HINST_THISDLL, GetTopLevelAncestor(hWnd),
MAKEINTRESOURCE(IDS_CHANGEDEFAULTPRINTER),
MAKEINTRESOURCE(IDS_PRINTINGERROR),
MB_YESNO|MB_ICONINFORMATION) ;
if (fTryAgain)
{
Printer_SetAsDefault(pidp->cName);
}
}
if (fTryAgain)
{
lpOperation = c_szPrint;
lpParms = (LPTSTR)c_szNULL;
goto TryAgain;
}
break; // we're done -- no message, the user cancelled
}
// fall through
default:
{
TCHAR szTemp[MAX_PATH];
DisplayError:
if ((UINT_PTR)FindExecutable(lpFName, szFPath, szTemp) <= 32)
{
lstrcpy(szTemp, szFilePath);
}
ExecInfo.fMask &= ~SEE_MASK_FLAG_NO_UI;
_ShellExecuteError(&ExecInfo, szTemp, uErr);
break;
}
}
}
if (lpRelease)
LocalFree((HLOCAL)lpRelease);
}
BOOL Printer_ModifyPrinter(LPCTSTR lpszPrinterName, DWORD dwCommand)
{
HANDLE hPrinter = Printer_OpenPrinterAdmin(lpszPrinterName);
BOOL fRet = FALSE;
if (hPrinter)
{
fRet = SetPrinter(hPrinter, 0, NULL, dwCommand);
if (fRet)
{
PrintDef_RefreshQueue(lpszPrinterName);
}
Printer_ClosePrinter(hPrinter);
}
return fRet;
}
void Printer_CheckMenuItem(HMENU hmModify, UINT fState, UINT uChecked, UINT uUnchecked)
{
MENUITEMINFO mii;
if (fState&MF_CHECKED)
{
UINT uTemp = uChecked;
uChecked = uUnchecked;
uUnchecked = uTemp;
}
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_ID|MIIM_STATE;
mii.cch = 0; // just in case...
if (GetMenuItemInfo(hmModify, uChecked, FALSE, &mii))
{
mii.wID = uUnchecked;
mii.fState = fState;
SetMenuItemInfo(hmModify, uChecked, FALSE, &mii);
}
}
void Printer_EnableMenuItems(HMENU hmModify, BOOL bEnable)
{
int i;
for (i=GetMenuItemCount(hmModify)-1; i>=0; --i)
{
EnableMenuItem(hmModify, i,
bEnable ? MF_BYPOSITION|MF_ENABLED : MF_BYPOSITION|MF_GRAYED);
}
}
BOOL IsAvoidAutoDefaultPrinter(LPCTSTR pszPrinter);
// this will find the first printer (if any) and set it as the default
// and inform the user
void Printers_ChooseNewDefault(HWND hwnd)
{
#ifdef WINNT
PRINTER_INFO_4 *pPrinters = NULL;
#else
PRINTER_INFO_1 *pPrinters = NULL;
#endif
DWORD iPrinter, dwNumPrinters = Printers_EnumPrinters(NULL,
#ifdef WINNT
PRINTER_ENUM_LOCAL | PRINTER_ENUM_FAVORITE,
4,
#else
PRINTER_ENUM_LOCAL,
1,
#endif
&pPrinters);
if (dwNumPrinters)
{
if (pPrinters)
{
for (iPrinter = 0 ; iPrinter < dwNumPrinters ; iPrinter++)
{
#ifdef WINNT
if (!IsAvoidAutoDefaultPrinter(pPrinters[iPrinter].pPrinterName))
#else
if (!IsAvoidAutoDefaultPrinter(pPrinters[iPrinter].pName))
#endif
break;
}
if (iPrinter == dwNumPrinters)
{
dwNumPrinters = 0;
}
else
{
#ifdef WINNT
Printer_SetAsDefault(pPrinters[iPrinter].pPrinterName);
#else
Printer_SetAsDefault(pPrinters[iPrinter].pName);
#endif
}
}
else
{
dwNumPrinters = 0;
}
}
// Inform user
if (dwNumPrinters)
{
ShellMessageBox(HINST_THISDLL,
hwnd,
MAKEINTRESOURCE(IDS_DELNEWDEFAULT),
MAKEINTRESOURCE(IDS_PRINTERS),
MB_OK,
#ifdef WINNT
pPrinters[iPrinter].pPrinterName);
#else
pPrinters[iPrinter].pName);
#endif
}
else
{
#ifdef WINNT
Printer_SetAsDefault(NULL); // clear the default printer
#endif
ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_DELNODEFAULT),
MAKEINTRESOURCE(IDS_PRINTERS), MB_OK);
}
if (pPrinters)
LocalFree((HLOCAL)pPrinters);
}
BOOL Printer_SetAsDefault(LPCTSTR lpszPrinterName)
{
#ifdef WINNT // PRINTQ
// HACK for SUR.
TCHAR szDefaultPrinterString[MAX_PATH * 2];
TCHAR szBuffer[MAX_PATH * 2];
if( lpszPrinterName ){
// Not the default, set it.
if( !GetProfileString( TEXT( "Devices" ),
lpszPrinterName,
TEXT( "" ),
szBuffer,
ARRAYSIZE( szBuffer ))){
return FALSE;
}
lstrcpy( szDefaultPrinterString, lpszPrinterName );
lstrcat( szDefaultPrinterString, TEXT( "," ));
lstrcat( szDefaultPrinterString, szBuffer );
// Use the new string for Windows.Device.
lpszPrinterName = szDefaultPrinterString;
}
if( !WriteProfileString( TEXT( "Windows" ), TEXT( "Device" ), lpszPrinterName )){
return FALSE;
}
// Tell the world and make everyone flash.
SendNotifyMessage( HWND_BROADCAST, WM_WININICHANGE, 0, (LPARAM)TEXT( "Windows" ));
return TRUE;
#else
BOOL bRet = FALSE;
HANDLE hPrinter;
if (lpszPrinterName)
{
hPrinter = Printer_OpenPrinter(lpszPrinterName);
if (hPrinter)
{
PRINTER_INFO_5 * pPI5;
pPI5 = Printer_GetPrinterInfo(hPrinter, 5);
if (pPI5)
{
pPI5->Attributes |= PRINTER_ATTRIBUTE_DEFAULT;
bRet = SetPrinter(hPrinter, 5, (LPBYTE)pPI5, 0);
LocalFree((HLOCAL)pPI5);
}
Printer_ClosePrinter(hPrinter);
}
}
return(bRet);
#endif
}
LPVOID Printer_EnumProps(HANDLE hPrinter, DWORD dwLevel, DWORD *lpdwNum,
ENUMPROP lpfnEnum, LPVOID lpData)
{
DWORD dwSize, dwNeeded;
LPBYTE pEnum;
dwSize = 0;
SetLastError(0);
lpfnEnum(lpData, hPrinter, dwLevel, NULL, 0, &dwSize, lpdwNum);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
pEnum = NULL;
goto Error1;
}
ASSERT(dwSize < 0x100000L);
pEnum = (void*)LocalAlloc(LPTR, dwSize);
TryAgain:
if (!pEnum)
{
goto Error1;
}
SetLastError(0);
if (!lpfnEnum(lpData, hPrinter, dwLevel, pEnum, dwSize, &dwNeeded, lpdwNum))
{
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
LPBYTE pTmp;
dwSize = dwNeeded;
pTmp = (void*)LocalReAlloc((HLOCAL)pEnum, dwSize,
LMEM_MOVEABLE|LMEM_ZEROINIT);
if (pTmp)
{
pEnum = pTmp;
goto TryAgain;
}
}
LocalFree((HLOCAL)pEnum);
pEnum = NULL;
}
Error1:
return pEnum;
}
BOOL Printers_EnumPrintersCB(LPVOID lpData, HANDLE hPrinter, DWORD dwLevel,
LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum)
{
return EnumPrinters(PtrToUlong(lpData), (LPTSTR)hPrinter, dwLevel,
pEnum, dwSize, lpdwNeeded, lpdwNum);
}
DWORD Printers_EnumPrinters(LPCTSTR pszServer, DWORD dwType, DWORD dwLevel, void **ppPrinters)
{
DWORD dwNum = 0L;
// If the server is szNULL, pass in NULL, since EnumPrinters expects
// this for the local server.
if (pszServer && !pszServer[0])
{
pszServer = NULL;
}
*ppPrinters = Printer_EnumProps((HANDLE)pszServer, dwLevel, &dwNum, Printers_EnumPrintersCB, ULongToPtr(dwType));
if (*ppPrinters==NULL)
{
dwNum = 0;
}
return dwNum;
}
#ifdef PRN_FOLDERDATA
BOOL Printers_FolderEnumPrintersCB(LPVOID lpData, HANDLE hFolder, DWORD dwLevel,
LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum)
{
return bFolderEnumPrinters(hFolder, (PFOLDER_PRINTER_DATA)pEnum,
dwSize, lpdwNeeded, lpdwNum);
}
DWORD Printers_FolderEnumPrinters(HANDLE hFolder, LPVOID *ppPrinters)
{
DWORD dwNum = 0L;
*ppPrinters = Printer_EnumProps(hFolder, 0, &dwNum,
Printers_FolderEnumPrintersCB,
NULL);
if (*ppPrinters==NULL)
{
dwNum=0;
}
return dwNum;
}
BOOL Printers_FolderGetPrinterCB(LPVOID lpData, HANDLE hFolder, DWORD dwLevel,
LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum)
{
return bFolderGetPrinter(hFolder, (LPCTSTR)lpData, (PFOLDER_PRINTER_DATA)pEnum, dwSize, lpdwNeeded);
}
LPVOID Printer_FolderGetPrinter(HANDLE hFolder, LPCTSTR pszPrinter)
{
return Printer_EnumProps(hFolder, 0, NULL, Printers_FolderGetPrinterCB, (LPVOID)pszPrinter);
}
#endif
BOOL Printers_GetPrinterDriverCB(LPVOID lpData, HANDLE hPrinter, DWORD dwLevel,
LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum)
{
return GetPrinterDriver(hPrinter, NULL, dwLevel, pEnum, dwSize, lpdwNeeded);
}
LPVOID Printer_GetPrinterDriver(HANDLE hPrinter, DWORD dwLevel)
{
return Printer_EnumProps(hPrinter, dwLevel, NULL, Printers_GetPrinterDriverCB, NULL);
}
// cbModule = sizeof(*pszModule) and cbModule ~== MAX_PATH+slop
// returns NULL and sets *pid to the icon id in HINST_THISDLL or
// returns pszModule and sets *pid to the icon id for module pszModule
LPTSTR Printer_FindIcon(CPrinterFolder *psf, LPCTSTR pszPrinterName, LPTSTR pszModule, LONG cbModule, LPINT piIcon)
{
LPTSTR pszRet = NULL;
TCHAR szKeyName[256];
// registry override of the icon
wnsprintf(szKeyName, ARRAYSIZE(szKeyName), TEXT("Printers\\%s\\DefaultIcon"), pszPrinterName);
if (SHGetValue(HKEY_CLASSES_ROOT, szKeyName, NULL, NULL, pszModule, &cbModule) && cbModule)
{
*piIcon = PathParseIconLocation(pszModule);
pszRet = pszModule;
}
else
{
PVOID pData = NULL;
DWORD dwAttributes = 0;
LPTSTR pszPort = NULL;
BOOL fDef;
#ifdef PRN_FOLDERDATA
// Try retrieving the information from hFolder if it's remote
// to avoid hitting the net.
if (psf && psf->pszServer && psf->hFolder &&
(NULL != (pData = Printer_FolderGetPrinter(psf->hFolder, pszPrinterName))))
{
dwAttributes = ((PFOLDER_PRINTER_DATA)pData)->Attributes;
}
else
#endif
#ifdef WINNT
if (Printer_CheckNetworkPrinterByName(pszPrinterName, NULL))
{
// avoid hitting the network.
dwAttributes = PRINTER_ATTRIBUTE_NETWORK;
}
else
#endif
{
pData = Printer_GetPrinterInfoStr(pszPrinterName, 5);
if (pData)
{
dwAttributes = ((LPPRINTER_INFO_5)pData)->Attributes;
pszPort = ((LPPRINTER_INFO_5)pData)->pPortName;
}
}
fDef = IsDefaultPrinter(pszPrinterName, dwAttributes);
if (dwAttributes & PRINTER_ATTRIBUTE_NETWORK)
*piIcon = fDef ? IDI_DEF_PRINTER_NET : IDI_PRINTER_NET;
else if (pszPort && !lstrcmp(pszPort, c_szFileColon))
*piIcon = fDef ? IDI_DEF_PRINTER_FILE : IDI_PRINTER_FILE;
else
*piIcon = fDef ? IDI_DEF_PRINTER : IDI_PRINTER;
if (pData)
LocalFree((HLOCAL)pData);
}
return pszRet;
}
void Printer_LoadIcons(LPCTSTR pszPrinterName, HICON *phLargeIcon, HICON *phSmallIcon)
{
TCHAR szBuf[MAX_PATH+20];
LPTSTR pszModule;
INT iIcon;
HINSTANCE hinst = NULL;
// Guarantee that we don't do work for nothing...
ASSERT(phLargeIcon || phSmallIcon);
pszModule = Printer_FindIcon(NULL, pszPrinterName, szBuf, ARRAYSIZE(szBuf), &iIcon);
// cool! we're using someone elses dll... we gotta load it though
if (pszModule)
{
hinst = LoadLibrary(pszModule);
if (!hinst)
{
DebugMsg(TF_ERROR, TEXT("Printer_FindIcon reported an unloadable module!!!"));
}
}
// Ensure we have a valid instance handle
if (!hinst)
{
iIcon = IDI_PRINTER;
hinst = HINST_THISDLL;
}
// Get the large icon.
if (phLargeIcon)
*phLargeIcon = LoadImage(hinst, MAKEINTRESOURCE(iIcon),
IMAGE_ICON, 0, 0, LR_DEFAULTSIZE);
// Get the small icon.
if (phSmallIcon)
*phSmallIcon = LoadImage(hinst, MAKEINTRESOURCE(iIcon),
IMAGE_ICON, g_cxSmIcon, g_cxSmIcon, 0);
// Release the dll if one was loaded.
if (hinst && hinst != HINST_THISDLL)
FreeLibrary(hinst);
}