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

578 lines
16 KiB
C

#include "shellprv.h"
#pragma hdrstop
#include "printer.h"
typedef struct tagPREVPRINTER
{
TCHAR szPrinterName[MAXNAMELENBUFFER];
HWND hwndStub;
} PREVPRINTER, *LPPREVPRINTER;
int FindPrinter(HDSA hPrevPrinters, LPCTSTR lpszPrinterName)
{
int i = -1;
if (hPrevPrinters)
{
for (i=DSA_GetItemCount(hPrevPrinters)-1; i>=0; --i)
{
LPPREVPRINTER pPrevPrinter = DSA_GetItemPtr(hPrevPrinters, i);
if (lstrcmpi(pPrevPrinter->szPrinterName, lpszPrinterName) == 0)
{
break;
}
}
}
return(i);
}
// 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 lpBuffer (ASSUMED >= MAXNAMELEN).
// Always returns NULL.
// if uAction is MSP_TESTPAGEPARTIALPROMPT then:
// executes the test page code
// Always returns NULL.
HWND hwndPrinterSetup = NULL; // active printer setup window, if any
LPITEMIDLIST Printers_PrinterSetup(HWND hwndStub, UINT uAction, LPTSTR lpBuffer, LPCTSTR pszServer)
{
#ifndef WINNT // PRINTQ
HINSTANCE hmMSPrint;
PRINTERSETUPPROC32 pfnPrinterSetup;
#endif
LPITEMIDLIST pidl = NULL;
// 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(hwndStub, REST_NOPRINTERADD))
{
return NULL;
}
}
#ifndef WINNT
// Windows 95 permits a user to printer a test page from the help trouble
// shooter, with out any contextual information. i.e. a printer has
// not been selected. In this case they will prompt the user for a
// printer from a list of printers. In Windows NT we do not support
// printing a test page from the help trouble shooter.
// In Windows NT we prevent multiple add printer wizards from within
// the bPrinterSetup API located in printui.dll, where as Windows 95
// prevents multiple add printer wizards here using a global.
// we only want one PrinterSetup window up at a time
if (uAction != MSP_TESTPAGEPARTIALPROMPT)
{
BOOL fRet = FALSE;
HWND hwnd;
ENTERCRITICAL;
if (hwndPrinterSetup != NULL &&
SetForegroundWindow(
hwnd = GetLastActivePopup(hwndPrinterSetup) ))
{
fRet = TRUE;
}
else
{
hwndPrinterSetup = hwndStub;
}
LEAVECRITICAL;
if (fRet)
{
// In the case of deleting a printer, it is very unclear
// why the "install wizard" popped up into the user's face.
// Put up a generic "you must complete this operation" message.
ShellMessageBox(HINST_THISDLL, hwnd,
MAKEINTRESOURCE(IDS_MUSTCOMPLETE), MAKEINTRESOURCE(IDS_PRINTERS), MB_OK);
return NULL;
}
}
#endif
#if WINNT // PRINTQ
if (1)
{
DWORD cchBufLen = 0;
#else
hmMSPrint = LoadLibrary(TEXT("MSPRINT2.DLL"));
if (!ISVALIDHINSTANCE(hmMSPrint))
{
DebugMsg(DM_WARNING,TEXT("sh WN - Printers_PrinterSetup could not load MSPRINT2.DLL"));
ASSERT(FALSE);
return NULL;
}
// BUGBUG: pop up a message box on error?
pfnPrinterSetup = (PRINTERSETUPPROC32)GetProcAddress(hmMSPrint, "PrinterSetup32");
if (pfnPrinterSetup)
{
WORD cchBufLen = 0;
#endif // PRINTQ
LPIDPRINTER pidp;
if (lpBuffer)
cchBufLen = lstrlen(lpBuffer) + 1;
if (cchBufLen < ARRAYSIZE(pidp->cName))
cchBufLen = ARRAYSIZE(pidp->cName);
pidp = (void*)LocalAlloc(LPTR, SIZEOF(IDPRINTER) - SIZEOF(pidp->cName) + cchBufLen * SIZEOF(TCHAR));
if (pidp)
{
pidp->cName[0] = TEXT('\0');
if (lpBuffer)
lstrcpy(pidp->cName, lpBuffer);
// We don't have to worry about PrinterSetup failing due to the
// output buffer being too small. It's the right size
// (32 bytes for Win9x, MAXNAMELENBUFFER for WinNT).
// On Win9x, this is ANSI, on WinNT, setup expects UNICODE.
// Also, there are no alignment problems since pidp is
// allocated by us.
#ifdef WINNT // PRINTQ
if (bPrinterSetup(hwndStub,LOWORD(uAction),cchBufLen, pidp->cName,&cchBufLen, pszServer))
{
#else
if (pfnPrinterSetup(hwndStub,LOWORD(uAction),cchBufLen,
(LPBYTE)pidp->cName,&cchBufLen))
{
#endif
if (uAction == MSP_NEWDRIVER)
{
ASSERT(lstrlen(pidp->cName) < MAXNAMELEN);
lstrcpy(lpBuffer, (LPTSTR)(pidp->cName));
}
else if (uAction == MSP_TESTPAGEPARTIALPROMPT)
{
// nothing to do for this case
}
else if (uAction == MSP_REMOVEPRINTER)
{
// a bit ugly, but we need to pass back success for this case
pidl = (LPITEMIDLIST)TRUE;
}
#ifdef WINNT
else if (uAction == MSP_NEWPRINTER_MODELESS)
{
// a bit ugly, but we need to pass back success for this case
pidl = (LPITEMIDLIST)TRUE;
}
#endif
else
{
LPITEMIDLIST pidlParent = SHCloneSpecialIDList(NULL, CSIDL_PRINTERS, FALSE);
if (pidlParent)
{
pidp->cb = (USHORT)(FIELD_OFFSET(IDPRINTER,cName) + (lstrlen(pidp->cName) + 1) * SIZEOF(TCHAR));
*(USHORT *)((LPBYTE)(pidp) + pidp->cb) = 0;
pidl = ILCombine(pidlParent, (LPCITEMIDLIST)pidp);
ILFree(pidlParent);
}
}
}
else
{
DebugMsg(DM_TRACE,TEXT("sh TR - PrinterSetup32() failed (%x)"), GetLastError());
}
LocalFree((HLOCAL)pidp);
}
}
else
{
DebugMsg(DM_ERROR,TEXT("sh ER - GetProcAddress(MSPRINT32.DLL,PrinterSetup32) failed"));
}
hwndPrinterSetup = NULL;
#ifndef WINNT // PRINTQ
FreeLibrary(hmMSPrint);
#endif
return(pidl);
}
// Printer_OneWindowAction calls pfn iff it's not in use for printer lpName.
// If it's already in use for printer lpName, bring focus to that window.
void Printer_OneWindowAction(HWND hwndStub, LPCTSTR lpName, HDSA *lphdsa, LPFNPRINTACTION pfn, LPARAM lParam, BOOL fModal)
{
int i;
PREVPRINTER sThisPrinter;
LPPREVPRINTER pPrevPrinter;
EnterCriticalSection(&g_csPrinters);
// Initialize the DSA if we need to
if (!*lphdsa)
{
*lphdsa = DSA_Create(SIZEOF(sThisPrinter), 4);
if (!*lphdsa)
{
goto error_exit;
}
}
// Bring window up if lpName is in use
i = FindPrinter(*lphdsa, lpName);
if (i >= 0)
{
HWND hwnd;
if (fModal) {
ShellMessageBox(HINST_THISDLL,
hwndStub,
MAKEINTRESOURCE(IDS_CANTOPENMODALPROP),
MAKEINTRESOURCE(IDS_PRINTERS),
MB_OK|MB_ICONERROR);
}
pPrevPrinter = DSA_GetItemPtr(*lphdsa, i);
if (pPrevPrinter->hwndStub == NULL ||
(hwnd = GetLastActivePopup(pPrevPrinter->hwndStub)) == NULL ||
!ShowWindow(hwnd, SW_SHOW) ||
!SetForegroundWindow(hwnd) )
{
// The window must have crashed before. Remove it from the
// hdsa and add it again.
// WARNING: This window could be stuck waiting for a
// PRINTER_INFO_2 before putting up the window. In this case, the
// ShowWindow will fail and we'll wind up with two windows up.
DSA_DeleteItem(*lphdsa, i);
}
else
{
// We brought the existing window to the front. We're done.
goto exit;
}
}
// We're bringing up the window
lstrcpy(sThisPrinter.szPrinterName, lpName);
sThisPrinter.hwndStub = hwndStub;
if (DSA_AppendItem(*lphdsa, &sThisPrinter) < 0)
{
goto error_exit;
}
// Call the printer function. Make sure winspool is loaded
LeaveCriticalSection(&g_csPrinters);
pfn(hwndStub, lpName, SW_SHOWNORMAL, lParam);
EnterCriticalSection(&g_csPrinters);
// The window is gone
// (Use sThisPrinter.szPrinterName since it may have been renamed)
i = FindPrinter(*lphdsa, sThisPrinter.szPrinterName);
if (i >= 0)
{
DSA_DeleteItem(*lphdsa, i);
// Don't use up memory we don't need
if (DSA_GetItemCount(*lphdsa) == 0)
{
DSA_Destroy(*lphdsa);
*lphdsa = NULL;
}
}
goto exit;
error_exit:
// BUGBUG: do we want to put up an Out Of Memory error?
DebugMsg(DM_WARNING, TEXT("sh WN - Printer_OneWindowAction() - Out Of Memory"));
exit:
LeaveCriticalSection(&g_csPrinters);
return;
}
extern HDSA hdsaPrintDef;
void PrintDef_UpdateHwnd(LPCTSTR lpszPrinterName, HWND hWnd)
{
int i;
LPPREVPRINTER pThisPrinter;
EnterCriticalSection(&g_csPrinters);
if (!hdsaPrintDef)
{
goto Error0;
}
i = FindPrinter(hdsaPrintDef, lpszPrinterName);
if (i < 0)
{
goto Error0;
}
pThisPrinter = DSA_GetItemPtr(hdsaPrintDef, i);
pThisPrinter->hwndStub = hWnd;
Error0:
LeaveCriticalSection(&g_csPrinters);
}
void PrintDef_UpdateName(LPCTSTR lpszPrinterName, LPCTSTR lpszNewName)
{
int i;
LPPREVPRINTER pThisPrinter;
EnterCriticalSection(&g_csPrinters);
if (!hdsaPrintDef)
{
goto Error0;
}
i = FindPrinter(hdsaPrintDef, lpszPrinterName);
if (i < 0)
{
goto Error0;
}
pThisPrinter = DSA_GetItemPtr(hdsaPrintDef, i);
lstrcpy(pThisPrinter->szPrinterName, lpszNewName);
Error0:
LeaveCriticalSection(&g_csPrinters);
}
void PrintDef_RefreshQueue(LPCTSTR lpszPrinterName)
{
int i;
LPPREVPRINTER pThisPrinter;
EnterCriticalSection(&g_csPrinters);
if (!hdsaPrintDef)
{
goto Error0;
}
if (lpszPrinterName)
{
i = FindPrinter(hdsaPrintDef, lpszPrinterName);
if (i < 0)
{
goto Error0;
}
pThisPrinter = DSA_GetItemPtr(hdsaPrintDef, i);
SendMessage(pThisPrinter->hwndStub, WM_COMMAND, ID_VIEW_REFRESH, 0L);
}
else
{
for (i=DSA_GetItemCount(hdsaPrintDef)-1; i>=0; --i)
{
pThisPrinter = DSA_GetItemPtr(hdsaPrintDef, i);
SendMessage(pThisPrinter->hwndStub, WM_COMMAND, ID_VIEW_REFRESH, 0L);
}
}
Error0:
LeaveCriticalSection(&g_csPrinters);
}
// IDropTarget stuff
STDMETHODIMP CPrintObjs_DragEnter(IDropTarget *pdt, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
CIDLDropTarget *this = IToClass(CIDLDropTarget, dropt, pdt);
// let the base-class process it now to save away pdwEffect
CIDLDropTarget_DragEnter(pdt, pDataObj, grfKeyState, pt, pdwEffect);
// We allow files to be dropped for printing
// if it is from the bitbucket only DROEFFECT_MOVE will be set in *pdwEffect
// so this will keep us from printing wastbasket items.
if (this->dwData & DTID_HDROP)
*pdwEffect &= DROPEFFECT_COPY;
else
*pdwEffect = DROPEFFECT_NONE; // Default action is nothing
this->dwEffectLastReturned = *pdwEffect;
return S_OK;
}
void Printer_PrintHDROPFiles(HWND hwnd, HDROP hdrop, LPCITEMIDLIST pidlPrinter)
{
DRAGINFO di;
di.uSize = SIZEOF(di);
if (DragQueryInfo(hdrop, &di))
{
LPTSTR lpFileName = di.lpFileList;
int i = IDYES;
// Printing more than one file at a time can easily fail.
// Ask the user to confirm this operation.
if (*lpFileName && *(lpFileName + lstrlen(lpFileName) + 1))
{
i = ShellMessageBox(HINST_THISDLL,
NULL,
MAKEINTRESOURCE(IDS_MULTIPLEPRINTFILE),
MAKEINTRESOURCE(IDS_PRINTERS),
MB_YESNO|MB_ICONINFORMATION);
}
if (i == IDYES)
{
// BUGBUG: It would be really nice to have a progress bar when
// printing multiple files. And there should definitely be a way
// to cancel this operation. Oh well, we warned them...
while (*lpFileName != TEXT('\0'))
{
Printer_PrintFile(hwnd, lpFileName, pidlPrinter);
lpFileName += lstrlen(lpFileName) + 1;
}
}
SHFree(di.lpFileList);
}
}
void FreePrinterThreadParam(PRNTHREADPARAM *pthp)
{
if (pthp->pDataObj)
pthp->pDataObj->lpVtbl->Release(pthp->pDataObj);
if (pthp->pstmDataObj)
pthp->pstmDataObj->lpVtbl->Release(pthp->pstmDataObj);
ILFree(pthp->pidl);
LocalFree((HLOCAL)pthp);
}
// This is the entry of "drop thread"
DWORD CALLBACK CPrintObj_DropThreadProc(void *pv)
{
PRNTHREADPARAM *pthp = (PRNTHREADPARAM *)pv;
STGMEDIUM medium;
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
CoGetInterfaceAndReleaseStream(pthp->pstmDataObj, &IID_IDataObject, (void **)&pthp->pDataObj);
pthp->pstmDataObj = NULL;
if (pthp->pDataObj && SUCCEEDED(pthp->pDataObj->lpVtbl->GetData(pthp->pDataObj, &fmte, &medium)))
{
Printer_PrintHDROPFiles(pthp->hwnd, medium.hGlobal, pthp->pidl);
ReleaseStgMedium(&medium);
}
FreePrinterThreadParam(pthp);
return 0;
}
HRESULT PrintObj_DropPrint(IDataObject *pDataObj, HWND hwnd, DWORD dwEffect, LPCITEMIDLIST pidl, LPTHREAD_START_ROUTINE pfn)
{
HRESULT hres;
PRNTHREADPARAM *pthp = (PRNTHREADPARAM *)LocalAlloc(LPTR, SIZEOF(*pthp));
if (pthp)
{
CoMarshalInterThreadInterfaceInStream(&IID_IDataObject, (IUnknown *)pDataObj, &pthp->pstmDataObj);
pthp->hwnd = hwnd;
pthp->dwEffect = dwEffect;
pthp->pidl = ILClone(pidl);
if (hwnd)
ShellFolderView_GetAnchorPoint(hwnd, FALSE, &pthp->ptDrop);
if (SHCreateThread(pfn, pthp, CTF_COINIT, NULL))
{
hres = S_OK;
}
else
{
FreePrinterThreadParam(pthp);
hres = E_OUTOFMEMORY;
}
}
return hres;
}
STDMETHODIMP CPrintObjs_DropCallback(IDropTarget *pdt, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect, LPTHREAD_START_ROUTINE pfn)
{
CIDLDropTarget *this = IToClass(CIDLDropTarget, dropt, pdt);
HRESULT hres;
*pdwEffect = this->dwEffectLastReturned;
if (*pdwEffect)
hres = CIDLDropTarget_DragDropMenu(this, DROPEFFECT_COPY, pDataObj,
pt, pdwEffect, NULL, NULL, MENU_PRINTOBJ_DD, grfKeyState);
else
hres = S_FALSE;
if (*pdwEffect)
hres = PrintObj_DropPrint(pDataObj, this->hwndOwner, *pdwEffect, this->pidl, pfn);
CIDLDropTarget_DragLeave(pdt);
return hres;
}
STDMETHODIMP CPrintObjs_Drop(IDropTarget *pdt, IDataObject *pDataObj, DWORD grfKeyState, POINTL pt, DWORD *pdwEffect)
{
return CPrintObjs_DropCallback(pdt, pDataObj, grfKeyState, pt, pdwEffect, CPrintObj_DropThreadProc);
}
const IDropTargetVtbl c_CPrintObjsDropTargetVtbl =
{
CIDLDropTarget_QueryInterface, CIDLDropTarget_AddRef, CIDLDropTarget_Release,
CPrintObjs_DragEnter,
CIDLDropTarget_DragOver,
CIDLDropTarget_DragLeave,
CPrintObjs_Drop,
};