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.
1116 lines
34 KiB
1116 lines
34 KiB
#include "shellprv.h"
|
|
#pragma hdrstop
|
|
|
|
#include "printer.h"
|
|
#include "copy.h"
|
|
#include "ids.h"
|
|
|
|
typedef struct
|
|
{
|
|
UINT uAction;
|
|
LPTSTR lpBuf1;
|
|
LPTSTR lpBuf2;
|
|
} PRINTERS_RUNDLL_INFO, *LPPRI;
|
|
|
|
// forward prototypes
|
|
void Printer_OpenMe(LPCTSTR pName, LPCTSTR pServer, BOOL fModal);
|
|
void Printers_ProcessCommand(HWND hwndStub, LPPRI lpPRI, BOOL fModal);
|
|
|
|
TCHAR const c_szPrintersGetCommand_RunDLL[] = TEXT("SHELL32,PrintersGetCommand_RunDLL");
|
|
|
|
//
|
|
// if uAction IS NOT MSP_NEWDRIVER then:
|
|
// installs a printer (uAction). If successful, notifies the shell and
|
|
// returns a pidl to the printer. ILFree() is callers responsibility.
|
|
// otherwise, if uAction IS MSP_NEWDRIVER then:
|
|
// installs a printer driver (uAction). If successful, fills the new
|
|
// driver's name into pszPrinter (ASSUMED >= MAXNAMELEN).
|
|
// Always returns NULL.
|
|
// if uAction is MSP_TESTPAGEPARTIALPROMPT then:
|
|
// executes the test page code
|
|
// Always returns NULL.
|
|
//
|
|
|
|
LPITEMIDLIST Printers_PrinterSetup(HWND hwnd, UINT uAction, LPTSTR pszPrinter, DWORD cchBufSize, LPCTSTR pszServer)
|
|
{
|
|
LPITEMIDLIST pidl = NULL;
|
|
TCHAR szPrinter[MAXNAMELENBUFFER];
|
|
DWORD cchBufLen;
|
|
|
|
// HACK! This hack is related to BUG #272207
|
|
// This function is called from Printers_DeletePrinter for
|
|
// printer deletion and this case we should not check
|
|
// for REST_NOPRINTERADD restriction. -LazarI
|
|
|
|
if (MSP_NEWPRINTER == uAction ||
|
|
MSP_NETPRINTER == uAction ||
|
|
MSP_NEWPRINTER_MODELESS == uAction)
|
|
{
|
|
if (SHIsRestricted(hwnd, REST_NOPRINTERADD))
|
|
{
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
cchBufLen = ARRAYSIZE(szPrinter);
|
|
if (pszPrinter)
|
|
StringCchCopy(szPrinter, ARRAYSIZE(szPrinter), pszPrinter);
|
|
else
|
|
szPrinter[0] = 0;
|
|
|
|
// We don't have to worry about PrinterSetup failing due to the
|
|
// output buffer being too small. It's the right size (MAXNAMELENBUFFER)
|
|
if (bPrinterSetup(hwnd, LOWORD(uAction), cchBufLen, szPrinter, &cchBufLen, pszServer))
|
|
{
|
|
if (uAction == MSP_NEWDRIVER)
|
|
{
|
|
StringCchCopy(pszPrinter, cchBufSize, szPrinter);
|
|
}
|
|
else if (uAction == MSP_TESTPAGEPARTIALPROMPT)
|
|
{
|
|
// nothing to do for this case
|
|
}
|
|
else if (uAction == MSP_REMOVEPRINTER || uAction == MSP_NEWPRINTER_MODELESS || uAction == MSP_REMOVENETPRINTER)
|
|
{
|
|
// a bit ugly, but we need to pass back success for this case
|
|
pidl = (LPITEMIDLIST)TRUE;
|
|
}
|
|
else
|
|
{
|
|
// do not validate the printer PIDL here because the validation mechanism in ParseDisplayName
|
|
// is using the folder cache and since we just added it may still not be in the folder cache,
|
|
// and we fail, although this a valid local printer/connection already.
|
|
ParsePrinterNameEx(szPrinter, &pidl, TRUE, 0, 0);
|
|
}
|
|
}
|
|
|
|
return pidl;
|
|
}
|
|
|
|
SHSTDAPI_(BOOL) SHInvokePrinterCommand(
|
|
IN HWND hwnd,
|
|
IN UINT uAction,
|
|
IN LPCTSTR lpBuf1,
|
|
IN LPCTSTR lpBuf2,
|
|
IN BOOL fModal)
|
|
{
|
|
PRINTERS_RUNDLL_INFO PRI;
|
|
|
|
PRI.uAction = uAction;
|
|
PRI.lpBuf1 = (LPTSTR)lpBuf1;
|
|
PRI.lpBuf2 = (LPTSTR)lpBuf2;
|
|
|
|
Printers_ProcessCommand(hwnd, &PRI, fModal);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
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];
|
|
|
|
BOOL bSuccess = TRUE;
|
|
|
|
if (bSuccess && lpBuf1)
|
|
{
|
|
bSuccess = (MultiByteToWideChar(CP_ACP, 0, lpBuf1, -1, szBuf1, SIZECHARS(szBuf1)) > 0);
|
|
lpBuf1 = bSuccess ? (LPCSTR)szBuf1 : NULL;
|
|
}
|
|
|
|
if (bSuccess && lpBuf2)
|
|
{
|
|
bSuccess = (MultiByteToWideChar(CP_ACP, 0, lpBuf2, -1, szBuf2, SIZECHARS(szBuf2)) > 0);
|
|
lpBuf2 = bSuccess ? (LPCSTR)szBuf2 : NULL;
|
|
}
|
|
|
|
if (bSuccess)
|
|
{
|
|
bSuccess = SHInvokePrinterCommand(hwnd, uAction, (LPCWSTR)lpBuf1, (LPCWSTR)lpBuf2, fModal);
|
|
}
|
|
|
|
return bSuccess;
|
|
}
|
|
|
|
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
|
|
|
|
//
|
|
// Make sure cchBuf1 & cchBuf2 are correct which means that the length
|
|
// of the remainder of the string should be either equal to cchBuf1 if
|
|
// cchBuf2 is equal to zero, OR equal to (cchBuf1 + cchBuf2 + 1) if
|
|
// cchBuf2 is not equal to zero.
|
|
//
|
|
if (lstrlen(PRI.lpBuf1) != (int)(cchBuf1 + ((!!cchBuf2) * (cchBuf2 + 1))))
|
|
{
|
|
goto BadCmdLine;
|
|
}
|
|
|
|
*(PRI.lpBuf1+cchBuf1) = '\0';
|
|
|
|
if (cchBuf2 == 0)
|
|
{
|
|
PRI.lpBuf2 = NULL;
|
|
}
|
|
else
|
|
{
|
|
PRI.lpBuf2 = PRI.lpBuf1+cchBuf1+1;
|
|
}
|
|
|
|
// Make this modal.
|
|
Printers_ProcessCommand(hwndStub, &PRI, TRUE);
|
|
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)
|
|
{
|
|
int iLen;
|
|
HRESULT hrInit = SHOleInitialize(0);
|
|
|
|
if (lpszCmdLine)
|
|
{
|
|
//
|
|
// Call WideCharToMultiByte to properly calculate the destination buffer length.
|
|
//
|
|
iLen = MultiByteToWideChar(CP_ACP, 0, lpszCmdLine, -1, NULL, 0);
|
|
|
|
//
|
|
// MultiByteToWideChar returns zero if the conversion fails.
|
|
//
|
|
if (iLen > 0)
|
|
{
|
|
LPWSTR lpwszCmdLine = (LPWSTR)LocalAlloc(LPTR, iLen * sizeof(WCHAR));
|
|
|
|
//
|
|
// Check if LocalAlloc has succeeded.
|
|
//
|
|
if (lpwszCmdLine)
|
|
{
|
|
iLen = MultiByteToWideChar(CP_ACP, 0, lpszCmdLine, -1, lpwszCmdLine, iLen);
|
|
|
|
//
|
|
// MultiByteToWideChar returns zero if the conversion fails.
|
|
//
|
|
if (iLen > 0)
|
|
{
|
|
PrintersGetCommand_RunDLL_Common(hwndStub,
|
|
hAppInstance,
|
|
lpwszCmdLine,
|
|
nCmdShow);
|
|
}
|
|
|
|
LocalFree(lpwszCmdLine);
|
|
}
|
|
}
|
|
}
|
|
|
|
SHOleUninitialize(hrInit);
|
|
}
|
|
|
|
void WINAPI PrintersGetCommand_RunDLLW(HWND hwndStub, HINSTANCE hAppInstance, LPWSTR lpwszCmdLine, int nCmdShow)
|
|
{
|
|
PrintersGetCommand_RunDLL_Common( hwndStub,
|
|
hAppInstance,
|
|
lpwszCmdLine,
|
|
nCmdShow );
|
|
}
|
|
|
|
static void
|
|
HandleOpenPrinter(HWND hwnd, LPCTSTR pszPrinter, BOOL fModal, BOOL bConnect)
|
|
{
|
|
BOOL bPrinterOK = FALSE;
|
|
DWORD dwError = ERROR_SUCCESS;
|
|
TCHAR szPrinter[MAXNAMELENBUFFER];
|
|
HANDLE hPrinter = NULL;
|
|
LPITEMIDLIST pidl = NULL;
|
|
PRINTER_INFO_2 *pPrinter = NULL;
|
|
|
|
// we need to open the printer and get the real printer name in case
|
|
// the passed in printer name is a sharename
|
|
lstrcpyn(szPrinter, pszPrinter, ARRAYSIZE(szPrinter));
|
|
hPrinter = Printer_OpenPrinter(szPrinter);
|
|
if (hPrinter)
|
|
{
|
|
pPrinter = (PRINTER_INFO_2 *)Printer_GetPrinterInfo(hPrinter, 2);
|
|
if (pPrinter)
|
|
{
|
|
if (pPrinter->pPrinterName && pPrinter->pPrinterName[0])
|
|
{
|
|
// copy the real printer name
|
|
bPrinterOK = TRUE;
|
|
lstrcpyn(szPrinter, pPrinter->pPrinterName, ARRAYSIZE(szPrinter));
|
|
}
|
|
LocalFree((HLOCAL)pPrinter);
|
|
}
|
|
else
|
|
{
|
|
// save last error
|
|
dwError = GetLastError();
|
|
}
|
|
Printer_ClosePrinter(hPrinter);
|
|
}
|
|
else
|
|
{
|
|
// save last error
|
|
dwError = GetLastError();
|
|
}
|
|
|
|
if (bPrinterOK)
|
|
{
|
|
if (bConnect)
|
|
{
|
|
// if the printer is not installed then we'll silently install it
|
|
// since this is what most users will expect.
|
|
if (FAILED(ParsePrinterName(szPrinter, &pidl)))
|
|
{
|
|
// connect....
|
|
pidl = Printers_PrinterSetup(hwnd, MSP_NETPRINTER, szPrinter, 0, NULL);
|
|
|
|
if (pidl)
|
|
{
|
|
// get the real printer name from the printer's folder...
|
|
SHGetNameAndFlags(pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szPrinter, ARRAYSIZE(szPrinter), NULL);
|
|
ILFree(pidl);
|
|
}
|
|
else
|
|
{
|
|
// failed to install the printer (it shows UI, so we shouldn't)
|
|
bPrinterOK = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the printer is already installed
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
|
|
if (bPrinterOK)
|
|
{
|
|
Printer_OpenMe(szPrinter, NULL, fModal);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// something else failed -- show up an error message
|
|
ShowErrorMessageSC(NULL, NULL, hwnd, NULL, NULL, MB_OK|MB_ICONEXCLAMATION, dwError);
|
|
}
|
|
}
|
|
|
|
/********************************************************************
|
|
|
|
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))
|
|
{
|
|
Printers_PrinterSetup(hwndStub, MSP_NEWPRINTER_MODELESS,
|
|
lpPRI->lpBuf1, 0, lpPRI->lpBuf2);
|
|
}
|
|
else
|
|
{
|
|
HandleOpenPrinter(hwndStub, lpPRI->lpBuf1, fModal, FALSE);
|
|
}
|
|
break;
|
|
|
|
case PRINTACTION_SERVERPROPERTIES:
|
|
{
|
|
LPCTSTR pszServer = (LPTSTR)(lpPRI->lpBuf1);
|
|
|
|
// we should never get called with c_szNewObject
|
|
ASSERT(lstrcmpi(lpPRI->lpBuf1, c_szNewObject));
|
|
vServerPropPages(hwndStub, pszServer, SW_SHOWNORMAL, 0);
|
|
break;
|
|
}
|
|
case PRINTACTION_DOCUMENTDEFAULTS:
|
|
{
|
|
// we should never get called with c_szNewObject
|
|
ASSERT(lstrcmpi(lpPRI->lpBuf1, c_szNewObject));
|
|
vDocumentDefaults(hwndStub, lpPRI->lpBuf1, SW_SHOWNORMAL, (LPARAM)(lpPRI->lpBuf2));
|
|
break;
|
|
}
|
|
|
|
case PRINTACTION_PROPERTIES:
|
|
{
|
|
// we should never get called with c_szNewObject
|
|
ASSERT(lstrcmpi(lpPRI->lpBuf1, c_szNewObject));
|
|
vPrinterPropPages(hwndStub, lpPRI->lpBuf1, SW_SHOWNORMAL, (LPARAM)(lpPRI->lpBuf2));
|
|
break;
|
|
}
|
|
|
|
case PRINTACTION_NETINSTALLLINK:
|
|
case PRINTACTION_NETINSTALL:
|
|
{
|
|
LPITEMIDLIST pidl = Printers_PrinterSetup(hwndStub, MSP_NETPRINTER, lpPRI->lpBuf1, 0, NULL);
|
|
if (pidl)
|
|
{
|
|
if (lpPRI->uAction == PRINTACTION_NETINSTALLLINK)
|
|
{
|
|
IDataObject *pdtobj;
|
|
if (SUCCEEDED(SHGetUIObjectFromFullPIDL(pidl, NULL, IID_PPV_ARG(IDataObject, &pdtobj))))
|
|
{
|
|
SHCreateLinks(hwndStub, lpPRI->lpBuf2, pdtobj, SHCL_USETEMPLATE, NULL);
|
|
pdtobj->lpVtbl->Release(pdtobj);
|
|
}
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
case PRINTACTION_OPENNETPRN:
|
|
{
|
|
HandleOpenPrinter(hwndStub, lpPRI->lpBuf1, fModal, TRUE);
|
|
break;
|
|
} // case PRINTACTION_OPENNETPRN
|
|
|
|
case PRINTACTION_TESTPAGE:
|
|
Printers_PrinterSetup(hwndStub, MSP_TESTPAGEPARTIALPROMPT,
|
|
lpPRI->lpBuf1, 0, NULL);
|
|
break;
|
|
|
|
default:
|
|
DebugMsg(TF_WARNING, TEXT("PrintersGetCommand_RunDLL() received unrecognized uAction %d"), lpPRI->uAction);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void Printer_OpenMe(LPCTSTR pName, LPCTSTR pServer, BOOL fModal)
|
|
{
|
|
BOOL fOpened = FALSE;
|
|
HKEY hkeyPrn;
|
|
TCHAR buf[50+MAXNAMELEN];
|
|
|
|
StringCchPrintf(buf, ARRAYSIZE(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
|
|
NULL, // hwnd - queue view should not be modal on the printer's folder, make it top level
|
|
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)
|
|
{
|
|
vQueueCreate(NULL, pName, SW_SHOWNORMAL, (LPARAM)fModal);
|
|
}
|
|
}
|
|
|
|
//
|
|
// 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)
|
|
{
|
|
LPTSTR lpBuffer = NULL;
|
|
UINT uErr = ERROR_NOT_ENOUGH_MEMORY;
|
|
HANDLE hPrinter;
|
|
TCHAR szPrinter[MAXNAMELENBUFFER];
|
|
|
|
SHGetNameAndFlags(pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szPrinter, ARRAYSIZE(szPrinter), NULL);
|
|
hPrinter = Printer_OpenPrinter(szPrinter);
|
|
if (NULL == hPrinter)
|
|
{
|
|
// fallback to the full name in case this was as \\server\share
|
|
// printer drop target
|
|
SHGetNameAndFlags(pidl, SHGDN_FORPARSING, szPrinter, ARRAYSIZE(szPrinter), NULL);
|
|
hPrinter = Printer_OpenPrinter(szPrinter);
|
|
}
|
|
|
|
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);
|
|
DWORD cchBufSize = 2+lstrlen(szPrinter)+1+
|
|
2+lstrlen(lpDriverName)+1+
|
|
2+lstrlen(pPrinter->pPortName)+1;
|
|
lpBuffer = (void*)LocalAlloc(LPTR, cchBufSize * SIZEOF(TCHAR));
|
|
if (lpBuffer)
|
|
{
|
|
StringCchPrintf(lpBuffer, cchBufSize, TEXT("\"%s\" \"%s\" \"%s\""),
|
|
szPrinter, 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 pszFilePath, LPCITEMIDLIST pidl)
|
|
{
|
|
UINT uErr;
|
|
LPTSTR lpParms = NULL;
|
|
BOOL bTryPrintVerb = TRUE;
|
|
BOOL bShowError = FALSE;
|
|
LPITEMIDLIST pidlFull = NULL;
|
|
SHELLEXECUTEINFO ExecInfo = {0};
|
|
|
|
|
|
uErr = Printer_GetPrinterInfoFromPidl(pidl, &lpParms);
|
|
if (uErr != ERROR_SUCCESS)
|
|
{
|
|
bShowError = TRUE;
|
|
}
|
|
if (!bShowError && !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,
|
|
MAKEINTRESOURCE(IDS_CANTPRINT),
|
|
MAKEINTRESOURCE(IDS_PRINTERS),
|
|
MB_OK|MB_ICONEXCLAMATION);
|
|
return;
|
|
}
|
|
|
|
//
|
|
// Get the context menu for the file
|
|
//
|
|
|
|
pidlFull = ILCreateFromPath( pszFilePath );
|
|
if (!bShowError && pidlFull)
|
|
{
|
|
//
|
|
// Try the "printto" verb first...
|
|
//
|
|
|
|
ExecInfo.cbSize = sizeof(ExecInfo);
|
|
ExecInfo.fMask = SEE_MASK_UNICODE | SEE_MASK_INVOKEIDLIST |
|
|
SEE_MASK_IDLIST | SEE_MASK_FLAG_NO_UI;
|
|
ExecInfo.hwnd = hWnd;
|
|
ExecInfo.lpVerb = c_szPrintTo;
|
|
ExecInfo.lpParameters = lpParms;
|
|
ExecInfo.nShow = SW_SHOWNORMAL;
|
|
ExecInfo.lpIDList = pidlFull;
|
|
|
|
if (!ShellExecuteEx( &ExecInfo ))
|
|
{
|
|
//
|
|
// Since we can't print specifying the printer name (i.e., printto),
|
|
// our next option is to print to the default printer. However,
|
|
// that might not be the printer the user dragged the files onto
|
|
// so check here and let the user set the desired printer to be
|
|
// the default if they want...
|
|
//
|
|
|
|
TCHAR szPrinter[MAXNAMELENBUFFER];
|
|
SHGetNameAndFlags(pidl, SHGDN_INFOLDER | SHGDN_FORPARSING, szPrinter, ARRAYSIZE(szPrinter), NULL);
|
|
|
|
if (!IsDefaultPrinter(szPrinter, 0))
|
|
{
|
|
//
|
|
// this isn't the default printer, ask first
|
|
//
|
|
|
|
if (IDYES==ShellMessageBox(
|
|
HINST_THISDLL, GetTopLevelAncestor(hWnd),
|
|
MAKEINTRESOURCE(IDS_CHANGEDEFAULTPRINTER),
|
|
MAKEINTRESOURCE(IDS_PRINTERS),
|
|
MB_YESNO|MB_ICONEXCLAMATION))
|
|
{
|
|
Printer_SetAsDefault(szPrinter);
|
|
}
|
|
else
|
|
{
|
|
bTryPrintVerb = FALSE;
|
|
}
|
|
|
|
}
|
|
|
|
if (bTryPrintVerb)
|
|
{
|
|
//
|
|
// Try the "print" verb
|
|
//
|
|
|
|
ExecInfo.lpVerb = c_szPrint;
|
|
|
|
if (!ShellExecuteEx( &ExecInfo ))
|
|
{
|
|
uErr = GetLastError();
|
|
bShowError = TRUE;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
ILFree(pidlFull);
|
|
}
|
|
|
|
if (lpParms)
|
|
LocalFree((HLOCAL)lpParms);
|
|
|
|
if (bShowError)
|
|
{
|
|
ShellMessageBox(HINST_THISDLL, hWnd,
|
|
MAKEINTRESOURCE(IDS_ERRORPRINTING),
|
|
MAKEINTRESOURCE(IDS_PRINTERS),
|
|
MB_OK|MB_ICONEXCLAMATION);
|
|
}
|
|
}
|
|
|
|
|
|
BOOL Printer_ModifyPrinter(LPCTSTR lpszPrinterName, DWORD dwCommand)
|
|
{
|
|
HANDLE hPrinter = Printer_OpenPrinterAdmin(lpszPrinterName);
|
|
BOOL fRet = FALSE;
|
|
if (hPrinter)
|
|
{
|
|
fRet = SetPrinter(hPrinter, 0, NULL, dwCommand);
|
|
Printer_ClosePrinter(hPrinter);
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
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)
|
|
{
|
|
PRINTER_INFO_4 *pPrinters = NULL;
|
|
DWORD iPrinter, dwNumPrinters = Printers_EnumPrinters(NULL,
|
|
PRINTER_ENUM_LOCAL | PRINTER_ENUM_FAVORITE,
|
|
4,
|
|
&pPrinters);
|
|
if (dwNumPrinters)
|
|
{
|
|
if (pPrinters)
|
|
{
|
|
for (iPrinter = 0 ; iPrinter < dwNumPrinters ; iPrinter++)
|
|
{
|
|
if (!IsAvoidAutoDefaultPrinter(pPrinters[iPrinter].pPrinterName))
|
|
break;
|
|
}
|
|
if (iPrinter == dwNumPrinters)
|
|
{
|
|
dwNumPrinters = 0;
|
|
}
|
|
else
|
|
{
|
|
Printer_SetAsDefault(pPrinters[iPrinter].pPrinterName);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwNumPrinters = 0;
|
|
}
|
|
}
|
|
|
|
// Inform user
|
|
if (dwNumPrinters)
|
|
{
|
|
ShellMessageBox(HINST_THISDLL,
|
|
hwnd,
|
|
MAKEINTRESOURCE(IDS_DELNEWDEFAULT),
|
|
MAKEINTRESOURCE(IDS_PRINTERS),
|
|
MB_OK,
|
|
pPrinters[iPrinter].pPrinterName);
|
|
}
|
|
else
|
|
{
|
|
Printer_SetAsDefault(NULL); // clear the default printer
|
|
ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_DELNODEFAULT),
|
|
MAKEINTRESOURCE(IDS_PRINTERS), MB_OK);
|
|
}
|
|
|
|
if (pPrinters)
|
|
LocalFree((HLOCAL)pPrinters);
|
|
}
|
|
|
|
BOOL Printer_SetAsDefault(LPCTSTR lpszPrinterName)
|
|
{
|
|
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;
|
|
}
|
|
|
|
StringCchCopy(szDefaultPrinterString, ARRAYSIZE(szDefaultPrinterString), lpszPrinterName);
|
|
StringCchCat(szDefaultPrinterString, ARRAYSIZE(szDefaultPrinterString), TEXT( "," ));
|
|
StringCchCat(szDefaultPrinterString, ARRAYSIZE(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;
|
|
}
|
|
|
|
void *Printer_EnumProps(HANDLE hPrinter, DWORD dwLevel, DWORD *lpdwNum,
|
|
ENUMPROP lpfnEnum, void *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(void *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;
|
|
}
|
|
|
|
BOOL Printers_FolderEnumPrintersCB(void *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, void **ppPrinters)
|
|
{
|
|
DWORD dwNum = 0L;
|
|
|
|
*ppPrinters = Printer_EnumProps(hFolder, 0, &dwNum,
|
|
Printers_FolderEnumPrintersCB,
|
|
NULL);
|
|
if (*ppPrinters==NULL)
|
|
{
|
|
dwNum=0;
|
|
}
|
|
return dwNum;
|
|
}
|
|
|
|
BOOL Printers_FolderGetPrinterCB(void *lpData, HANDLE hFolder, DWORD dwLevel,
|
|
LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum)
|
|
{
|
|
return bFolderGetPrinter(hFolder, (LPCTSTR)lpData, (PFOLDER_PRINTER_DATA)pEnum, dwSize, lpdwNeeded);
|
|
}
|
|
|
|
|
|
void *Printer_FolderGetPrinter(HANDLE hFolder, LPCTSTR pszPrinter)
|
|
{
|
|
return Printer_EnumProps(hFolder, 0, NULL, Printers_FolderGetPrinterCB, (LPVOID)pszPrinter);
|
|
}
|
|
|
|
BOOL Printers_GetPrinterDriverCB(void *lpData, HANDLE hPrinter, DWORD dwLevel,
|
|
LPBYTE pEnum, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum)
|
|
{
|
|
return GetPrinterDriver(hPrinter, NULL, dwLevel, pEnum, dwSize, lpdwNeeded);
|
|
}
|
|
|
|
|
|
void *Printer_GetPrinterDriver(HANDLE hPrinter, DWORD dwLevel)
|
|
{
|
|
return Printer_EnumProps(hPrinter, dwLevel, NULL, Printers_GetPrinterDriverCB, NULL);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////
|
|
// code moved from prcache.c
|
|
//
|
|
HANDLE Printer_OpenPrinterAdmin(LPCTSTR lpszPrinterName)
|
|
{
|
|
HANDLE hPrinter = NULL;
|
|
|
|
PRINTER_DEFAULTS PrinterDefaults;
|
|
PrinterDefaults.pDatatype = NULL;
|
|
PrinterDefaults.pDevMode = NULL;
|
|
PrinterDefaults.DesiredAccess = PRINTER_ALL_ACCESS;
|
|
|
|
// PRINTER_READ ? READ_CONTROL
|
|
|
|
if (!OpenPrinter((LPTSTR)lpszPrinterName, &hPrinter, &PrinterDefaults))
|
|
{
|
|
hPrinter = NULL; // OpenPrinter may trash hPrinter
|
|
}
|
|
|
|
return(hPrinter);
|
|
}
|
|
|
|
HANDLE Printer_OpenPrinter(LPCTSTR lpszPrinterName)
|
|
{
|
|
HANDLE hPrinter = NULL;
|
|
|
|
if (!OpenPrinter((LPTSTR)lpszPrinterName, &hPrinter, NULL))
|
|
{
|
|
hPrinter = NULL; // OpenPrinter may trash hPrinter
|
|
}
|
|
|
|
return(hPrinter);
|
|
}
|
|
|
|
VOID Printer_ClosePrinter(HANDLE hPrinter)
|
|
{
|
|
ClosePrinter(hPrinter);
|
|
}
|
|
|
|
BOOL Printers_DeletePrinter(HWND hWnd, LPCTSTR pszFullPrinter, DWORD dwAttributes, LPCTSTR pszServer, BOOL bQuietMode)
|
|
{
|
|
DWORD dwCommand = MSP_REMOVEPRINTER;
|
|
|
|
if (SHIsRestricted(hWnd, REST_NOPRINTERDELETE))
|
|
return FALSE;
|
|
|
|
if ((dwAttributes & PRINTER_ATTRIBUTE_NETWORK) && !(dwAttributes & PRINTER_ATTRIBUTE_LOCAL))
|
|
{
|
|
//
|
|
// If it's not local, then it must be a remote connection. Note
|
|
// that we can't just check for PRINTER_ATTRIBUTE_NETWORK because
|
|
// NT's spooler has 'masq' printers that are local printers
|
|
// that masquarade as network printers. Even though they
|
|
// are created by connecting to a printer, the have both LOCAL
|
|
// and NETWORK bits set.
|
|
//
|
|
dwCommand = MSP_REMOVENETPRINTER;
|
|
}
|
|
|
|
//
|
|
// Don't show the confirmation dialog box if in quiet mode
|
|
//
|
|
if (!bQuietMode)
|
|
{
|
|
if (pszServer && pszServer[0])
|
|
{
|
|
//
|
|
// It's a printer on the remote server. (Skip \\ prefix on server.)
|
|
//
|
|
if (ShellMessageBox(HINST_THISDLL, hWnd,
|
|
MAKEINTRESOURCE(IDS_SUREDELETEREMOTE),
|
|
MAKEINTRESOURCE(IDS_PRINTERS), MB_YESNO|MB_ICONQUESTION,
|
|
pszFullPrinter, SkipServerSlashes(pszServer)) != IDYES)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else if (dwAttributes & PRINTER_ATTRIBUTE_NETWORK)
|
|
{
|
|
TCHAR szScratch[MAXNAMELENBUFFER];
|
|
LPTSTR pszPrinter, pszServer;
|
|
|
|
Printer_SplitFullName(szScratch, ARRAYSIZE(szScratch), pszFullPrinter, &pszServer, &pszPrinter);
|
|
|
|
if (pszServer && *pszServer)
|
|
{
|
|
//
|
|
// It's a printer connection.
|
|
//
|
|
if (ShellMessageBox(HINST_THISDLL, hWnd,
|
|
MAKEINTRESOURCE(IDS_SUREDELETECONNECTION),
|
|
MAKEINTRESOURCE(IDS_PRINTERS), MB_YESNO|MB_ICONQUESTION,
|
|
pszPrinter, SkipServerSlashes(pszServer)) != IDYES)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// It's a printer connection with a printer name that
|
|
// does not have a server name prefix i.e. \\server\printer. This
|
|
// is true for the http connected printer, which have printer names
|
|
// of the form http://server/printer on NT these printers are
|
|
// 'masq' printers. A 'masq' printer is a printer which
|
|
// is a local printer acting as network connection.
|
|
//
|
|
if (ShellMessageBox(HINST_THISDLL, hWnd,
|
|
MAKEINTRESOURCE(IDS_SUREDELETECONNECTIONNOSERVERNAME),
|
|
MAKEINTRESOURCE(IDS_PRINTERS), MB_YESNO|MB_ICONQUESTION,
|
|
pszPrinter) != IDYES)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
|
|
//
|
|
// Neither a remote printer nor a local connection. The final
|
|
// upcoming else clause is a local printer.
|
|
//
|
|
if (ShellMessageBox(HINST_THISDLL, hWnd, MAKEINTRESOURCE(IDS_SUREDELETE),
|
|
MAKEINTRESOURCE(IDS_PRINTERS), MB_YESNO|MB_ICONQUESTION, pszFullPrinter)
|
|
!= IDYES)
|
|
{
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
if (CallPrinterCopyHooks(hWnd, PO_DELETE, 0, pszFullPrinter, 0, NULL, 0)
|
|
!= IDYES)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Cast away const. Safe since Printers_PrinterSetup only modifies
|
|
// pszPrinter if dwCommand is MSP_NEWDRIVER.
|
|
//
|
|
return BOOLFROMPTR(Printers_PrinterSetup(hWnd, dwCommand,
|
|
(LPTSTR)pszFullPrinter, 0, pszServer));
|
|
}
|
|
|
|
BOOL Printer_GPI2CB(LPVOID lpData, HANDLE hPrinter, DWORD dwLevel,
|
|
LPBYTE pBuf, DWORD dwSize, DWORD *lpdwNeeded, DWORD *lpdwNum)
|
|
{
|
|
return GetPrinter(hPrinter, dwLevel, pBuf, dwSize, lpdwNeeded);
|
|
}
|
|
|
|
//
|
|
// Old NT printers don't support the level 5. So we try for the 2 after 5.
|
|
// Win96 WILL PROBABLY WANT TO DO THIS TOO!
|
|
//
|
|
LPPRINTER_INFO_5 Printer_MakePrinterInfo5( HANDLE hPrinter )
|
|
{
|
|
LPPRINTER_INFO_5 pPI5 = NULL;
|
|
DWORD cbPI5 = 0;
|
|
DWORD cbName = 0;
|
|
DWORD cbPort = 0;
|
|
LPPRINTER_INFO_2 pPI2 = Printer_EnumProps(hPrinter, 2, NULL, Printer_GPI2CB, (LPVOID)0);
|
|
if (!pPI2)
|
|
return NULL;
|
|
|
|
cbName = (lstrlen(pPI2->pPrinterName)+1) * SIZEOF(TCHAR);
|
|
|
|
cbPI5 = SIZEOF(PRINTER_INFO_5) + cbName;
|
|
|
|
//
|
|
// Port name may not be supported (e.g., downlevel machines).
|
|
//
|
|
if (pPI2->pPortName)
|
|
{
|
|
cbPort = (lstrlen(pPI2->pPortName)+1) * SIZEOF(TCHAR);
|
|
cbPI5 += cbPort;
|
|
}
|
|
|
|
pPI5 = (LPPRINTER_INFO_5)LocalAlloc(LPTR, cbPI5);
|
|
if (pPI5)
|
|
{
|
|
ASSERT(pPI5->pPrinterName==NULL); // These should be null for the
|
|
ASSERT(pPI5->pPortName==NULL); // no names case
|
|
|
|
if (pPI2->pPrinterName)
|
|
{
|
|
pPI5->pPrinterName = (LPTSTR)(pPI5+1);
|
|
//
|
|
// Use byte-size here.
|
|
//
|
|
StringCbCopy(pPI5->pPrinterName, cbName, pPI2->pPrinterName);
|
|
}
|
|
if (pPI2->pPortName)
|
|
{
|
|
pPI5->pPortName = (LPTSTR)((LPBYTE)(pPI5+1) + cbName);
|
|
//
|
|
// User byte-size here.
|
|
//
|
|
StringCbCopy(pPI5->pPortName, cbPort, pPI2->pPortName);
|
|
}
|
|
pPI5->Attributes = pPI2->Attributes;
|
|
pPI5->DeviceNotSelectedTimeout = 0;
|
|
pPI5->TransmissionRetryTimeout = 0;
|
|
}
|
|
LocalFree(pPI2);
|
|
|
|
return(pPI5);
|
|
}
|
|
|
|
LPVOID Printer_GetPrinterInfo(HANDLE hPrinter, DWORD dwLevel)
|
|
{
|
|
LPVOID pPrinter = Printer_EnumProps(hPrinter, dwLevel, NULL, Printer_GPI2CB, (LPVOID)0);
|
|
//
|
|
// Old NT printers don't support the level 5. So we try for the 2 after 5.
|
|
// Win96 WILL PROBABLY WANT TO DO THIS TOO!
|
|
//
|
|
if (!pPrinter && dwLevel == 5)
|
|
return(Printer_MakePrinterInfo5(hPrinter));
|
|
return pPrinter;
|
|
|
|
}
|
|
|
|
LPVOID Printer_GetPrinterInfoStr(LPCTSTR lpszPrinterName, DWORD dwLevel)
|
|
{
|
|
LPPRINTER_INFO_2 pPI2 = NULL;
|
|
HANDLE hPrinter = Printer_OpenPrinter(lpszPrinterName);
|
|
if (hPrinter)
|
|
{
|
|
pPI2 = Printer_GetPrinterInfo(hPrinter, dwLevel);
|
|
Printer_ClosePrinter(hPrinter);
|
|
}
|
|
return pPI2;
|
|
}
|
|
|