Windows NT 4.0 source code leak
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.
 
 
 
 
 
 

4031 lines
114 KiB

//---------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation 1991-1996
//
// File: printer.c
//
// History:
// 01-07-93 GeorgeP Modified from win\core\shell\commui\handler.c
//
//---------------------------------------------------------------------------
#include "shellprv.h"
#pragma hdrstop
#include "copy.h"
#ifndef PRN_FOLDERDATA
#ifndef WINNT
WINBASEAPI
#endif
VOID WINAPI UninitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection);
#endif
#ifdef WINNT
#include <lm.h>
typedef NET_API_STATUS (*PFNNETSERVERGETINFO)(LPWSTR servername, DWORD level, LPBYTE *bufptr);
typedef NET_API_STATUS (*PFNNETAPIBUFFERFREE)(IN LPVOID Buffer);
HINSTANCE s_hmodNetApi32 = NULL;
PFNNETSERVERGETINFO g_pfnNetServerGetInfo = NULL;
PFNNETAPIBUFFERFREE g_pfnNetApiBufferFree = NULL;
#endif
//
// The many different ways of getting to printer information:
// Win9x -> PRINTER_INFO_1
// WINNT (old) -> PRINTER_INFO_4
// WINNT (with PRN_FOLDERDATA) -> FOLDER_PRINTER_DATA
//
#ifdef PRN_FOLDERDATA
#define GetPrinterName( p ) ((p).pName)
#else
#ifdef WINNT
#define GetPrinterName( p ) ((p).pPrinterName)
#define PRINTER_INFO_TYPE PRINTER_INFO_4
#define PRINTER_INFO_LEVEL 4
#define TYPICAL_PRINTER_INFO_2_SIZE 900
#else
#define PRINTER_INFO_TYPE PRINTER_INFO_1
#define GetPrinterName( p ) ((p).pName)
#define PRINTER_INFO_LEVEL 1
#define TYPICAL_PRINTER_INFO_2_SIZE 700
#endif
#endif
//
// In WINNT, the server name is stored in this. However,
// if it's not WINNT, then always pass NULL.
//
#ifdef WINNT
#define GetServerFromPSF( psf ) ((psf)->szServer)
#else
#define GetServerFromPSF( psf ) NULL
#endif
#ifdef WINNT
LPCTSTR Printer_BuildPrinterName(LPTSTR pszFullPrinter,
UNALIGNED const TCHAR* pszPrinter, PPrintersShellFolder psf);
#endif
//---------------------------------------------------------------------------
// We need cross-interface memory to store the machine name.
typedef struct _SPrintersObj
{
LPTSTR pszMachineName;
} SPrintersObj;
//
// Data structure to represent the IShellFolder of the directory that contains
// our printer shortcuts
//
typedef struct _CPrintHood
{
IShellFolder *psfPrintHood;
LPITEMIDLIST pidlPrintHood;
} CPrintHood, * LPPRINTHOOD;
CPrintHood c_printHood = { NULL, NULL };
//
// CPrintRoot::GetPIDL
//
LPITEMIDLIST CPrintRoot_GetPIDL(HWND hwnd)
{
if (!c_printHood.pidlPrintHood) {
c_printHood.pidlPrintHood = SHCloneSpecialIDList( hwnd, CSIDL_PRINTHOOD, TRUE);
}
return c_printHood.pidlPrintHood;
}
//
// CPrintRoot::GetPSF
//
LPSHELLFOLDER CPrintRoot_GetPSF(HWND hwnd)
{
if (!c_printHood.psfPrintHood)
{
LPITEMIDLIST pidlPrintHood = CPrintRoot_GetPIDL(hwnd);
if (pidlPrintHood)
{
HRESULT hres = CFSFolder_CreateFromIDList(c_printHood.pidlPrintHood,
&IID_IShellFolder,
&c_printHood.psfPrintHood);
if (FAILED(hres))
{
DebugMsg(DM_TRACE, TEXT("Failed to create print hood IShellFolder!"));
}
}
}
return c_printHood.psfPrintHood;
}
//
// CPrintRoot_GetPIDLType
//
typedef enum
{
HOOD_COL_PRINTER = 0,
HOOD_COL_FILE = 1
} PIDLTYPE ;
PIDLTYPE CPrintRoot_GetPIDLType(LPCITEMIDLIST pidl)
{
UNALIGNED struct _IDPRINTER * pidlprint = (LPIDPRINTER) pidl;
if (pidlprint->cb >= sizeof(DWORD) + FIELDOFFSET(IDPRINTER, dwMagic) &&
pidlprint->dwMagic == PRINTER_MAGIC)
{
return HOOD_COL_PRINTER;
}
else
{
return HOOD_COL_FILE;
}
}
//---------------------------------------------------------------------------
//
// Helper functions
//
BOOL Printers_RegisterWindow(LPCTSTR pszPrinter, DWORD dwType,
PHANDLE phClassPidl, HWND *phwnd)
/*++
Routine Description:
Registers a modeless, non-top level window with the shell. When
the user requests a window, we search for other instances of that
window. If we find one, we switch to it rather than creating
a new window.
Arguments:
pszPrinter - Name of the printer resource. Generally a fully
qualified printer name (\\server\printer for remote print
folders) or a server name for the folder itself.
dwType - Type of property window. May refer to properties, document
defaults, or job details. Should use the PRINTER_PIDL_TYPE_*
flags.
phClassPidl - Receives the newly created handle to the registered
object. NULL if window already exists.
phwnd - Receives the newly created hwndStub. The property sheet
should use this as the parent, since subsequent calls to
this function will set focus to the last active popup of
hwndStub. phwnd will be set to NULL if the window already
exists.
Return Value:
TRUE - Success, either the printer was registered, or a window
already exists.
FALSE - Call failed.
--*/
{
HANDLE hClassPidl;
IDPRINTER idp;
HWND hwndStub;
LPITEMIDLIST pidl;
LPITEMIDLIST pidlParent;
BOOL bReturn = TRUE;
*phClassPidl = NULL;
*phwnd = NULL;
pidlParent = SHCloneSpecialIDList(NULL, CSIDL_PRINTERS, FALSE);
Printers_FillPidl(&idp, pszPrinter);
idp.dwType = dwType;
pidl = ILCombine(pidlParent, (LPITEMIDLIST)&idp);
if (!pidl)
{
bReturn = FALSE;
}
else
{
//
// Search for a pre-existing window.
//
hwndStub = FindStubForPidl(pidl);
if (hwndStub)
{
SwitchToThisWindow(GetLastActivePopup(hwndStub), TRUE);
}
else
{
//
// Not found, create a new stub.
//
hwndStub = _CreateStubWindow();
if (hwndStub)
{
*phClassPidl = StuffStubWindowWithPidl(hwndStub, pidl);
if (!*phClassPidl)
{
DestroyWindow(hwndStub);
bReturn = FALSE;
}
else
{
*phwnd = hwndStub;
}
}
}
ILFree(pidl);
}
//
// Ensure we relase the pidlParent it's not needed
// in all cases. ILCombine will make a copy of it.
//
if( pidlParent )
{
ILFree(pidlParent);
}
return bReturn;
}
VOID Printers_UnregisterWindow(HANDLE hClassPidl, HWND hwnd)
/*++
Routine Description:
Unregister a window handle.
Arguments:
hClassPidl - Registration handle returned from Printers_RegisterWindow.
Return Value:
--*/
{
Assert(hClassPidl);
SHFreeShared(hClassPidl, GetCurrentProcessId());
Assert(hwnd);
DestroyWindow(hwnd);
}
void Printers_FillPidl(LPIDPRINTER pidl, LPCTSTR szName)
{
ualstrcpyn(pidl->cName, szName, ARRAYSIZE(pidl->cName));
pidl->cb = FIELDOFFSET(IDPRINTER, cName) + (ualstrlen(pidl->cName) + 1) * SIZEOF(TCHAR);
*(UNALIGNED USHORT *)((LPBYTE)(pidl) + pidl->cb) = 0;
pidl->uFlags = 0;
pidl->dwType = 0;
pidl->dwMagic = PRINTER_MAGIC;
}
LPITEMIDLIST Printers_GetPidl(LPCITEMIDLIST pidlParent, LPCTSTR szName)
{
//
// pidlParent can be NULL to indicate this is a local printer.
//
LPITEMIDLIST pidl = NULL;
if (!pidlParent)
{
pidlParent = GetSpecialFolderIDList(NULL, CSIDL_PRINTERS, FALSE);
}
if (!szName)
{
pidl = ILClone(pidlParent);
return pidl;
}
if (pidlParent)
{
IDPRINTER idp;
Printers_FillPidl(&idp, szName);
pidl = ILCombine(pidlParent, (LPITEMIDLIST)&idp);
}
return pidl;
}
//---------------------------------------------------------------------------
//
// IEnumShellFolder stuff
//
// Note: there is some extra stuff in WCommonUnknown we don't need, but this
// allows us to use the Common Unknown routines
//
typedef struct
{
WCommonUnknown cunk;
ULONG uFlags;
int nLastFound;
#ifdef WINNT
LPSHELLFOLDER psf;
#endif
#ifdef PRN_FOLDERDATA
PFOLDER_PRINTER_DATA pPrinters;
#else
//
// On NT it is actually faster to use info level 4 for just this sort of
// enumeration operation. Since only the name is used, we will do that.
// I suspect that it will be easy to ADD LEVEL 4 ENUMERATION TO WIN96.
// -BobDay
//
// PRINTER_INFO_TYPE is level 4 for WINNT, 1 for WIN9x.
//
PRINTER_INFO_TYPE *pPrinters;
#endif
DWORD dwNumPrinters;
DWORD dwRemote;
// Following used for enumerating persistent links
LPENUMIDLIST peunk;
} CPrintersEnumShellFolder, *PPrintersEnumShellFolder;
//
// Flags for the dwRemote field
//
#define RMF_SHOWLINKS 0x00000001 // Hoodlinks need to be shown
STDMETHODIMP_(ULONG) CPrinters_ESF_Release(LPENUMIDLIST pesf)
{
PPrintersEnumShellFolder this = IToClass(CPrintersEnumShellFolder, cunk.unk, pesf);
this->cunk.cRef--;
if (this->cunk.cRef > 0)
{
return(this->cunk.cRef);
}
#ifdef WINNT
//
// Release the psf.
//
this->psf->lpVtbl->Release(this->psf);
#endif
if (this->pPrinters)
LocalFree((HLOCAL)this->pPrinters);
//
// Release the link (filesystem) enumerator.
//
if (this->peunk)
{
this->peunk->lpVtbl->Release(this->peunk);
}
LocalFree((HLOCAL)this);
return(0);
}
TCHAR const c_szNewObject[] = TEXT("WinUtils_NewObject");
TCHAR const c_szFileColon[] = TEXT("FILE:");
TCHAR const c_szPrinters[] = TEXT("Printers");
STDMETHODIMP CPrinters_ESF_Next(LPENUMIDLIST pesf, ULONG celt, LPITEMIDLIST *ppidl, ULONG *pceltFetched)
{
PPrintersEnumShellFolder this = IToClass(CPrintersEnumShellFolder, cunk.unk, pesf);
IDPRINTER idp;
if (pceltFetched)
*pceltFetched = 0;
// We don't do any form of folder
if (!(this->uFlags & SHCONTF_NONFOLDERS))
{
return S_FALSE;
}
// Are we looking for the links right now?
if (this->dwRemote & RMF_SHOWLINKS)
{
// Yes, use the link (PrintHood folder) enumerator
if (this->peunk)
{
HRESULT hres;
hres = this->peunk->lpVtbl->Next(this->peunk, 1, ppidl, pceltFetched);
if (hres == S_OK && *pceltFetched == 1)
{
return S_OK; // Added link
}
}
this->dwRemote &= ~RMF_SHOWLINKS; // Done enumerating links
}
// Carry on with enumerating printers now
Assert(this->nLastFound >= 0 || this->nLastFound == -1);
if (this->nLastFound == -1)
{
//
// We will return the Add Printer Wizard icon as the
// first item (when nLastFound == -1).
//
// We need to enumerate printers before returning the
// Add Printer Wizard since the refresh tells us if
// we are an administrator on the folder.
//
#ifdef WINNT
DWORD dwFlags;
BOOL bShowAddPrinter;
PPrintersShellFolder that = IToClass(CPrintersShellFolder,
cunk.ck.unk, this->psf);
#ifdef PRN_FOLDERDATA
//
// Request a refresh. This completes synchronously.
//
g_pfnFolderRefresh(that->hFolder, &bShowAddPrinter);
//
// If we're not an admin, and we're on the local machine
// show the wizard since anyone can make a connection.
//
if (!that->szServer[0])
{
bShowAddPrinter = TRUE;
}
this->dwNumPrinters = Printers_FolderEnumPrinters(
that->hFolder,
&this->pPrinters);
#else
//
// Always show the Add Printer Wizard.
//
bShowAddPrinter = TRUE;
if( that->szServer[0] )
{
//
// Remote case: enum the printers on the server.
//
dwFlags = PRINTER_ENUM_NAME;
}
else
{
//
// Enum both local and connections if the print folder
// is local.
//
dwFlags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_FAVORITE;
}
this->dwNumPrinters =
Printers_EnumPrinters(GetServerFromPSF(that),
dwFlags,
PRINTER_INFO_LEVEL,
&this->pPrinters);
#endif
#else
//
// Always show the Add Printer Wizard.
//
bShowAddPrinter = TRUE;
dwFlags = PRINTER_ENUM_LOCAL | PRINTER_ENUM_FAVORITE;
this->dwNumPrinters =
Printers_EnumPrinters(NULL,
PRINTER_ENUM_LOCAL,
PRINTER_INFO_LEVEL,
&this->pPrinters);
#endif
//
// Note that pPrinters may be NULL if no printers are installed.
//
if (bShowAddPrinter)
{
//
// Special case the Add Printer Wizard.
//
Printers_FillPidl(&idp, c_szNewObject);
goto Done;
}
//
// Not an admin, skip the add printer wizard and return the
// first item.
//
this->nLastFound = 0;
}
#ifndef WINNT
ESF_TryAgain:
#endif
if (this->nLastFound >= (int)this->dwNumPrinters)
return S_FALSE;
#ifndef WINNT
//
// HACK: hide MS Fax's "Rendering Subsystem" printer.
// We need to do this for win9x, not for NT since the fax code
// will use print to file instead of the hidden printer.
//
if (!lstrcmp(GetPrinterName(this->pPrinters[this->nLastFound]),
TEXT("Rendering Subsystem")))
{
DebugMsg(DM_TRACE, TEXT("sh - TR Ignoring MS FAX's 'Rendering Subsystem' printer"));
++this->nLastFound;
goto ESF_TryAgain;
}
else
#endif
{
Printers_FillPidl(
&idp,
GetPrinterName(this->pPrinters[this->nLastFound]));
}
Done:
++this->nLastFound;
*ppidl = ILClone((LPCITEMIDLIST)&idp);
if (*ppidl == NULL)
return E_OUTOFMEMORY;
if (pceltFetched)
*pceltFetched = 1;
return S_OK;
}
STDMETHODIMP CPrinters_ESF_Skip(LPENUMIDLIST pesf, ULONG celt)
{
return E_NOTIMPL;
}
STDMETHODIMP CPrinters_ESF_Reset(LPENUMIDLIST pesf)
{
PPrintersEnumShellFolder this = IToClass(CPrintersEnumShellFolder, cunk.unk, pesf);
// BUGBUG
//
// This should either (a) fail or (b) reset the outer enumerator correctly
Assert(0 && "Broken CPrinters_ESF_Reset member called");
this->nLastFound = -1;
return NOERROR;
}
STDMETHODIMP CPrinters_ESF_Clone(LPENUMIDLIST pesf, LPENUMIDLIST *lplpenum)
{
return E_NOTIMPL;
}
#pragma data_seg(".text", "CODE")
IEnumIDListVtbl s_PrintersESFVtbl =
{
Common_ESF_QueryInterface,
WCommonUnknown_AddRef,
CPrinters_ESF_Release,
CPrinters_ESF_Next,
CPrinters_ESF_Skip,
CPrinters_ESF_Reset,
CPrinters_ESF_Clone,
};
#pragma data_seg()
#ifndef WINNT
//---------------------------------------------------------------------------
//
// this implements IContextMenu via defcm.c for a printer object
//
BOOL Printer_WorkOnLine(LPIDPRINTER pidp, BOOL fWorkOnLine)
{
LPPRINTER_INFO_5 ppi5;
HANDLE hPrinter;
BOOL bRet = FALSE;
LPCTSTR pszPrinter;
#ifdef ALIGNMENT_SCENARIO
TCHAR szPrinter[MAXNAMELENBUFFER];
ualstrcpyn(szPrinter, pidp->cName, ARRAYSIZE(szPrinter));
pszPrinter = szPrinter;
#else
pszPrinter = pidp->cName;
#endif
hPrinter = Printer_OpenPrinterAdmin(pszPrinter);
if (hPrinter)
{
ppi5 = Printer_GetPrinterInfo(hPrinter, 5);
if (ppi5)
{
if (fWorkOnLine)
ppi5->Attributes &= ~PRINTER_ATTRIBUTE_WORK_OFFLINE;
else
ppi5->Attributes |= PRINTER_ATTRIBUTE_WORK_OFFLINE;
bRet = g_pfnSetPrinter(hPrinter, 5, (LPBYTE)ppi5, 0);
PrintDef_RefreshQueue(pszPrinter);
LocalFree((HLOCAL)ppi5);
}
Printer_ClosePrinter(hPrinter);
}
return bRet;
}
#endif
TCHAR const c_szConfig[] = TEXT("Config");
#ifdef WINNT // PRINTQ
//
// HACK for SUR since PRINTER_ATTRIBUTE_DEFAULT doesn't work yet.
//
BOOL
bDefaultPrinter(
IN LPCTSTR pszPrinter
)
/*++
Routine Description:
Determines the default printer status.
Arguments:
pszPrinter - Check if this printer is the default (optional).
Return Value:
TRUE - pszPrinter is the default printer.
FALSE - not the default.
--*/
{
LPTSTR psz;
TCHAR szPrinterDefault[280];
szPrinterDefault[0] = 0;
if( !GetProfileString( TEXT( "Windows" ),
TEXT( "Device" ),
szPrinterDefault,
szPrinterDefault,
ARRAYSIZE( szPrinterDefault )) ||
!szPrinterDefault[0] ){
return FALSE;
}
//
// If pszPrinter passed in, see if it's the default.
//
#ifdef UNICODE
psz = wcschr( szPrinterDefault, TEXT( ',' ));
#else
psz = strchr( szPrinterDefault, TEXT( ',' ));
#endif
//
// We should find a comma, but let's be safe and check.
// Convert "superp,winspool,Ne00:" to "superp."
//
if( psz ){
*psz = 0;
if( !lstrcmpi( szPrinterDefault, pszPrinter )){
return TRUE;
}
}
return FALSE;
}
#endif
VOID Printer_MergeMenu(PPrintersShellFolder psf, LPQCMINFO pqcm,
LPCTSTR pszPrinter, BOOL fForcePause)
{
INT idCmdFirst = pqcm->idCmdFirst;
//
// pszPrinter may be the share name of a printer rather than
// the "real" printer name. Use the real printer name instead,
// which is returned from GetPrinter().
//
// These three only valid if pData != NULL.
//
LPCTSTR pszRealPrinterName;
DWORD dwAttributes;
DWORD dwStatus;
PPRINTER_INFO_2 pData = NULL;
#ifdef PRN_FOLDERDATA
TCHAR szFullPrinter[MAXNAMELENBUFFER];
#else
BOOL bUsedCommonPPI2 = FALSE;
//
// Valid only if pData != NULL.
//
LPCTSTR pszPortName;
#endif
// Insert verbs
CDefFolderMenu_MergeMenu(HINST_THISDLL, MENU_PRINTOBJ_VERBS, 0, pqcm);
//
// Now this function only takes a printer name. We will
// query pData from psf if it is available.
//
if (pszPrinter)
{
#ifdef PRN_FOLDERDATA
if (psf && psf->hFolder)
{
pData = Printer_FolderGetPrinter(psf->hFolder, pszPrinter);
if (pData)
{
Printer_BuildPrinterName(szFullPrinter,
((PFOLDER_PRINTER_DATA)pData)->pName,
psf);
pszRealPrinterName = szFullPrinter;
dwStatus = ((PFOLDER_PRINTER_DATA)pData)->Status;
dwAttributes = ((PFOLDER_PRINTER_DATA)pData)->Attributes;
}
}
else
{
pData = Printer_GetPrinterInfoStr(pszPrinter, 2);
if (pData)
{
pszRealPrinterName = pData->pPrinterName;
dwStatus = pData->Status;
dwAttributes = pData->Attributes;
}
}
#else
if (psf)
{
pData = CPrinters_SF_GetPrinterInfo2(psf, pszPrinter);
if (pData)
{
bUsedCommonPPI2 = TRUE;
}
}
else
{
pData = Printer_GetPrinterInfoStr(pszPrinter, 2);
}
if (pData)
{
pszRealPrinterName = pData->pPrinterName;
pszPortName = pData->pPortName;
dwStatus = pData->Status;
dwAttributes = pData->Attributes;
}
#endif
}
// disable/check/rename verbs
if (pData)
{
#ifdef WINNT // PRINTQ
//
// HACK for SUR until the NT spooler supports this attribute
// (it will be difficult to support it).
//
if( bDefaultPrinter( pszRealPrinterName ))
#else
if (dwAttributes & PRINTER_ATTRIBUTE_DEFAULT)
#endif
{
// we need to check "Set As Default"
CheckMenuItem(pqcm->hmenu,
idCmdFirst + FSIDM_SETDEFAULTPRN,
MF_BYCOMMAND|MF_CHECKED);
}
#ifndef WINNT // PRINTQ
// network printers don't get pause/purge
if (dwAttributes & PRINTER_ATTRIBUTE_NETWORK && !fForcePause)
{
DeleteMenu(pqcm->hmenu,
idCmdFirst + FSIDM_PAUSEPRN,
MF_BYCOMMAND);
DeleteMenu(pqcm->hmenu,
idCmdFirst + FSIDM_PURGEPRN,
MF_BYCOMMAND);
}
// FILE: printers don't get pause/purge
if (!lstrcmp(pszPortName, c_szFileColon))
{
EnableMenuItem(pqcm->hmenu,
idCmdFirst + FSIDM_PURGEPRN,
MF_BYCOMMAND|MF_GRAYED);
EnableMenuItem(pqcm->hmenu,
idCmdFirst + FSIDM_PAUSEPRN,
MF_BYCOMMAND|MF_GRAYED);
}
else
#endif
// DIRECT printers don't get pause
if (dwAttributes & PRINTER_ATTRIBUTE_DIRECT)
{
EnableMenuItem(pqcm->hmenu,
idCmdFirst + FSIDM_PAUSEPRN,
MF_BYCOMMAND|MF_GRAYED);
}
else // PAUSE/RESUME depends on state of printer
{
if (dwStatus & PRINTER_STATUS_PAUSED)
{
MENUITEMINFO mii;
// we need to check "Paused" (so, if clicked again, we RESUME)
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_STATE | MIIM_ID;
mii.fState = MF_CHECKED;
mii.wID = idCmdFirst + FSIDM_RESUMEPRN;
SetMenuItemInfo(pqcm->hmenu,
idCmdFirst + FSIDM_PAUSEPRN,
MF_BYCOMMAND, &mii);
}
}
#ifdef WINNT
//
// Remove default printer if it's a remote print folder.
//
if (!psf || GetServerFromPSF(psf)[0])
{
DeleteMenu(pqcm->hmenu,
idCmdFirst + FSIDM_SETDEFAULTPRN,
MF_BYCOMMAND);
}
//
// Check whether the printer is already installed. If it
// is, remove the option to install it.
//
{
TCHAR szScratch[2];
if (GetProfileString(TEXT( "Devices" ), pszRealPrinterName,
szNULL, szScratch, ARRAYSIZE( szScratch )))
{
//
// Printer exists in [devices] section. Remove
// install option.
//
DeleteMenu(pqcm->hmenu,
idCmdFirst + FSIDM_NETPRN_INSTALL,
MF_BYCOMMAND);
}
}
#else // def WINNT
// Remove work on/off-line iff all of the following
// - 1 user configuration exists
// - local printer
// - printer is on-line (should always be true,
// but we should make sure. if it ain't the
// user will be stuck w/ an off-line printer.)
if (!(dwAttributes & PRINTER_ATTRIBUTE_NETWORK) &&
!(dwAttributes & PRINTER_ATTRIBUTE_WORK_OFFLINE))
{
HKEY hkey;
if (ERROR_SUCCESS ==
RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szConfig,
0, KEY_QUERY_VALUE, &hkey))
{
DWORD dwNumSubKeys = 2; // something > 1
RegQueryInfoKey(hkey, NULL, NULL, NULL,
&dwNumSubKeys, NULL, NULL, NULL,
NULL, NULL, NULL, NULL);
RegCloseKey(hkey);
if (dwNumSubKeys == 1)
goto turn_off_less_stuff;
}
}
if (dwAttributes & PRINTER_ATTRIBUTE_WORK_OFFLINE)
{
MENUITEMINFO mii;
// we need to check "Offline" (so, if clicked again, we ONLINE)
mii.cbSize = SIZEOF(MENUITEMINFO);
mii.fMask = MIIM_STATE | MIIM_ID;
mii.fState = MF_CHECKED;
mii.wID = idCmdFirst + FSIDM_WORKONLINE;
SetMenuItemInfo(pqcm->hmenu,
idCmdFirst + FSIDM_WORKOFFLINE,
MF_BYCOMMAND, &mii);
}
#endif
#ifndef PRN_FOLDERDATA
if (bUsedCommonPPI2)
{
CPrinters_SF_FreePrinterInfo2(psf);
}
else
#endif
{
LocalFree((HLOCAL)pData);
}
}
else
{
// we have multiple printers selected
DeleteMenu(pqcm->hmenu,
idCmdFirst + FSIDM_SETDEFAULTPRN,
MF_BYCOMMAND);
EnableMenuItem(pqcm->hmenu,
idCmdFirst + FSIDM_PAUSEPRN,
MF_BYCOMMAND|MF_GRAYED);
DeleteMenu(pqcm->hmenu,
idCmdFirst + FSIDM_WORKOFFLINE,
MF_BYCOMMAND);
#ifdef WINNT
DeleteMenu(pqcm->hmenu,
idCmdFirst + FSIDM_NETPRN_INSTALL,
MF_BYCOMMAND);
DeleteMenu(pqcm->hmenu,
idCmdFirst + FSIDM_SETDEFAULTPRN,
MF_BYCOMMAND);
#endif
}
}
VOID Printer_WarnOnError(HWND hwnd, LPCTSTR pName, int idsError)
{
#ifndef WINNT
LPPRINTER_INFO_5 ppi5;
BOOL fWarn;
ppi5 = Printer_GetPrinterInfoStr(pName, 5);
fWarn = !ppi5 || !(ppi5->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE);
if (ppi5)
LocalFree((HLOCAL)ppi5);
if (fWarn)
#endif
{
ShellMessageBox(HINST_THISDLL, hwnd,
MAKEINTRESOURCE(idsError),
MAKEINTRESOURCE(IDS_PRINTERS),
MB_OK|MB_ICONINFORMATION);
}
}
#ifdef WINNT
//
// Three HACK functions to parse the printer name string.
// Printer_SplitFullName
// Printer_BuildPrinterName
// Printer_CheckNetworkPrinterByName
//
// All string parsing functions should be localized here.
//
VOID Printer_SplitFullName(LPTSTR pszScratch, LPCTSTR pszFullName,
LPCTSTR *ppszServer, 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. Must
be MAXNAMELENBUFFER in size.
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;
lstrcpyn(pszScratch, pszFullName, MAXNAMELENBUFFER);
if (pszFullName[0] != TEXT('\\') || pszFullName[1] != TEXT('\\'))
{
//
// Set *ppszServer to szNULL since it's the local machine.
//
*ppszServer = szNULL;
pszPrinter = pszScratch;
}
else
{
*ppszServer = pszScratch;
pszPrinter = StrChr(*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 = szNULL;
Assert(FALSE);
}
else
{
//
// We found the third backslash; null terminate our
// copy and set bRemote TRUE to format the string.
//
*pszPrinter++ = 0;
}
}
if (ppszPrinter)
{
*ppszPrinter = pszPrinter;
}
}
LPCTSTR Printer_BuildPrinterName(LPTSTR pszFullPrinter,
UNALIGNED const TCHAR* pszPrinter, PPrintersShellFolder psf)
/*++
Routine Description:
Parses an unaligned partial printer name and printer shell folder
into a fullly qualified printer name, and pointer to aligned printer
name.
Arguments:
pszFullPrinter - Buffer to receive fully qualified printer name
Must be MAXNAMELENBUFFER is size.
pszPrinter - Unaligned partial (local) printer name.
psf - PrinterShellFolder that owns the printer.
Return Value:
LPCTSTR pointer to aligned partal (local) printer name.
--*/
{
LPCTSTR pszServerName = GetServerFromPSF(psf);
UINT cchLen = 0;
if (pszServerName[0])
{
Assert(lstrlen(pszServerName) < MAXCOMPUTERNAME);
Assert(ualstrlen(pszPrinter) < MAXNAMELEN);
lstrcpy(pszFullPrinter, pszServerName);
lstrcat(pszFullPrinter, TEXT( "\\" ));
cchLen = lstrlen(pszFullPrinter);
}
ualstrcpyn(&pszFullPrinter[cchLen], pszPrinter, MAXNAMELEN);
Assert(lstrlen(pszFullPrinter) < MAXNAMELENBUFFER);
return pszFullPrinter+cchLen;
}
BOOL Printer_CheckNetworkPrinterByName(LPCTSTR pszPrinter, LPCTSTR* ppszLocal)
/*++
Routine Description:
Check whether the printer is a local printer by looking at
the name for the "\\localmachine\" prefix or no server prefix.
This is a HACK: we should check by printer attributes, but when
it's too costly or impossible (e.g., if the printer connection
no longer exists), then we use this routine.
Note: this only works for WINNT since the WINNT spooler forces
printer connections to be prefixed with "\\server\." Win9x
allows the user to rename the printer connection to any arbitrary
name.
We determine if it's a masq printer by looking for the
weird format "\\localserver\\\remoteserver\printer."
Arguments:
pszPrinter - Printer name.
ppszLocal - Returns local name only if the printer is a local printer.
(May be network and local if it's a masq printer.)
Return Value:
TRUE: it's a network printer (true or masq).
FALSE: it's a local printer.
--*/
{
BOOL bNetwork = FALSE;
LPCTSTR pszLocal = NULL;
if (pszPrinter[0] == TEXT( '\\' ) && pszPrinter[1] == TEXT( '\\' ))
{
TCHAR szComputer[MAX_COMPUTERNAME_LENGTH+1];
DWORD cchComputer = ARRAYSIZE(szComputer);
bNetwork = TRUE;
pszLocal = NULL;
//
// Check if it's a masq printer. If it has the format
// \\localserver\\\server\printer then it's a masq case.
//
if (GetComputerName(szComputer, &cchComputer))
{
if (IntlStrEqNI(&pszPrinter[2], szComputer, cchComputer) &&
pszPrinter[cchComputer] == TEXT( '\\' ))
{
if( pszPrinter[cchComputer+1] == TEXT('\\') &&
pszPrinter[cchComputer+2] == TEXT('\\'))
{
//
// It's a masq printer.
//
pszLocal = &pszPrinter[cchComputer+1];
}
}
}
} else {
//
// It's a local printer.
//
pszLocal = pszPrinter;
}
if (ppszLocal)
{
*ppszLocal = pszLocal;
}
return bNetwork;
}
#endif
HRESULT Printer_InvokeCommand(HWND hwndView, PPrintersShellFolder psf,
LPIDPRINTER pidp, WPARAM wParam, LPARAM lParam,
LPBOOL pfChooseNewDefault)
{
HRESULT hres = NOERROR;
BOOL bNewObject = !ualstrcmp(c_szNewObject, pidp->cName);
LPCTSTR pszPrinter;
LPCTSTR pszFullPrinter;
#ifdef WINNT
//
// If it's a remote machine, prepend server name.
//
TCHAR szFullPrinter[MAXNAMELENBUFFER];
if (bNewObject)
{
pszFullPrinter = pszPrinter = c_szNewObject;
}
else
{
pszPrinter = Printer_BuildPrinterName(szFullPrinter,
pidp->cName, psf );
pszFullPrinter = szFullPrinter;
}
#else
pszFullPrinter = pszPrinter = pidp->cName;
#endif
switch(wParam)
{
case FSIDM_OPENPRN:
Printers_DoCommand(hwndView,
PRINTACTION_OPEN,
pszFullPrinter,
GetServerFromPSF(psf));
break;
#ifdef WINNT
case FSIDM_DOCUMENTDEFAULTS:
if (!bNewObject)
{
Printers_DoCommandEx(hwndView,
PRINTACTION_DOCUMENTDEFAULTS,
pszFullPrinter,
NULL, 0 );
}
break;
case FSIDM_SHARING:
#endif
case DFM_CMD_PROPERTIES:
case DFM_CMD_MODALPROP:
if (!bNewObject)
{
Printers_DoCommandEx(hwndView,
PRINTACTION_PROPERTIES,
pszFullPrinter,
#ifdef WINNT
wParam == FSIDM_SHARING ?
(LPCTSTR)PRINTER_SHARING_PAGE :
NULL,
#else
NULL,
#endif
wParam==DFM_CMD_MODALPROP);
}
break;
case DFM_CMD_DELETE:
if (!bNewObject &&
IDYES == CallPrinterCopyHooks(hwndView, PO_DELETE,
0, pszFullPrinter, 0, NULL, 0))
{
BOOL bNukedDefault = FALSE;
BOOL fSuccess;
DWORD dwAttributes = PRINTER_ATTRIBUTE_NETWORK;
#ifdef PRN_FOLDERDATA
PFOLDER_PRINTER_DATA pData;
LPCTSTR pszPrinterCheck = pszFullPrinter;
pData = Printer_FolderGetPrinter(psf->hFolder, pszFullPrinter);
if (pData)
{
dwAttributes = pData->Attributes;
pszPrinterCheck = pData->pName;
}
//
// If this is a local print folder (szServer is szNULL), then
// we need to check if we're deleting the default printer.
//
if (!psf->szServer[0])
{
//
// HACK for SUR. Fundamentally, then Enum/GetPrinter apis
// are broken: PRINTER_ATTRIBUTE_DEFAULT is mixed in with the
// other printer attributes. This attribute is a per-user
// attribute, while PRINTER_INFO_# are really global settings
// for the printer.
//
bNukedDefault = bDefaultPrinter(pszPrinterCheck);
}
if (pData)
{
LocalFree((HLOCAL)pData);
}
#else
LPPRINTER_INFO_5 pData = Printer_GetPrinterInfoStr(pszFullPrinter, 5);
// are we about to nuke the default printer?
if (pData)
{
bNukedDefault = pData->Attributes & PRINTER_ATTRIBUTE_DEFAULT;
LocalFree((HLOCAL)pData);
}
#endif
fSuccess = Printers_DeletePrinter(hwndView, pszPrinter,
dwAttributes,
GetServerFromPSF(psf));
#ifndef PRN_FOLDERDATA
if (fSuccess && psf)
{
CPrinters_SF_RemovePrinterInfo2(psf, pidp->cName);
}
#endif
// if so, make another one the default
if (bNukedDefault && fSuccess && pfChooseNewDefault)
{
// don't choose in the middle of deletion,
// or we might delete the "default" again.
*pfChooseNewDefault = TRUE;
}
}
break;
case FSIDM_SETDEFAULTPRN:
// cannot be selected for c_szNewObject
Printer_SetAsDefault(pszFullPrinter);
break;
case FSIDM_PAUSEPRN:
// cannot be selected for c_szNewObject
// Since this command isn't available for net printers,
// don't worry about IDS_SECURITYDENIED message on err.
if (!Printer_ModifyPrinter(pszFullPrinter, PRINTER_CONTROL_PAUSE))
goto WarnOnError;
break;
case FSIDM_RESUMEPRN:
// cannot be selected for c_szNewObject
// Since this command isn't available for net printers,
// don't worry about IDS_SECURITYDENIED message on err.
if (!Printer_ModifyPrinter(pszFullPrinter, PRINTER_CONTROL_RESUME))
goto WarnOnError;
break;
case FSIDM_PURGEPRN:
if (!bNewObject)
{
if (!Printer_ModifyPrinter(pszFullPrinter, PRINTER_CONTROL_PURGE))
{
WarnOnError:
Printer_WarnOnError(hwndView, pszFullPrinter, IDS_SECURITYDENIED);
}
}
break;
#ifdef WINNT
case FSIDM_NETPRN_INSTALL:
{
Printers_DoCommand(hwndView, PRINTACTION_NETINSTALL,
pszFullPrinter, NULL );
break;
}
#endif
#ifndef WINNT
case FSIDM_WORKONLINE:
// cannot be selected for c_szNewObject
if (!Printer_WorkOnLine(pidp, TRUE))
{
// The only reason this can really fail is due to a network port
// not validating, but we should double check just to make sure
LPPRINTER_INFO_5 ppi5 = Printer_GetPrinterInfoStr(pszFullPrinter, 5);
if (ppi5)
{
if (ppi5->Attributes & PRINTER_ATTRIBUTE_NETWORK)
{
ShellMessageBox(HINST_THISDLL, hwndView,
MAKEINTRESOURCE(IDS_NO_WORKONLINE),
MAKEINTRESOURCE(IDS_PRINTERS),
MB_OK|MB_ICONINFORMATION);
}
LocalFree(ppi5);
}
}
break;
case FSIDM_WORKOFFLINE:
// cannot be selected for c_szNewObject
Printer_WorkOnLine(pidp, FALSE);
break;
#endif
case DFM_CMD_LINK:
// GetAttributesOf returns _CANLINK,
// let defcm handle this
hres = S_FALSE;
break;
default:
// GetAttributesOf doesn't set other SFGAO_ bits,
// BUT accelerator keys will get unavailable menu items,
// so we need to return failure here.
hres = E_NOTIMPL;
break;
} // switch(wParam)
#ifndef WINNT
if (hres == NOERROR && psf)
{
// since the PRINTER_INFO_2 state may have changed,
// set the UPDATE_NOW bit in the cache.
CPrinters_SF_UpdatePrinterInfo2(psf, pszFullPrinter, UPDATE_NOW);
}
#endif
return hres;
}
HRESULT CALLBACK CPrinters_DFMCallBack(LPSHELLFOLDER psf, HWND hwndView,
IDataObject * pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PPrintersShellFolder this = IToClass(CPrintersShellFolder, cunk.ck.unk, psf);
HRESULT hres = E_NOTIMPL;
if (pdtobj)
{
STGMEDIUM medium;
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
if (pida)
{
hres = NOERROR;
switch(uMsg)
{
case DFM_MERGECONTEXTMENU:
//
// Returning S_FALSE indicates no need to use default verbs
//
hres = S_FALSE;
break;
case DFM_MERGECONTEXTMENU_TOP:
{
LPQCMINFO pqcm = (LPQCMINFO)lParam;
if (pida->cidl == 1 && !ualstrcmp(c_szNewObject,
((LPIDPRINTER)IDA_GetIDListPtr(pida, 0))->cName))
{
// The only selected object is the "New Printer" thing
// insert verbs
CDefFolderMenu_MergeMenu(HINST_THISDLL, MENU_GENERIC_OPEN_VERBS, 0, pqcm);
}
else
{
LPCTSTR pszFullPrinter = NULL;
#ifdef WINNT
TCHAR szFullPrinter[MAXNAMELENBUFFER];
#endif
// We're dealing with printer objects
if (!(wParam & CMF_DEFAULTONLY))
{
LPIDPRINTER pidp;
if (pida->cidl == 1)
{
pidp = (LPIDPRINTER)IDA_GetIDListPtr(pida, 0);
if (pidp)
{
#ifdef WINNT
Printer_BuildPrinterName(szFullPrinter,
pidp->cName, this);
pszFullPrinter = szFullPrinter;
#else
pszFullPrinter = pidp->cName;
#endif
}
}
}
Printer_MergeMenu(this, pqcm, pszFullPrinter, FALSE);
} // if (pida->cidl=...), else case
SetMenuDefaultItem(pqcm->hmenu, 0, MF_BYPOSITION);
break;
} // case DFM_MERGECONTEXTMENU
case DFM_GETHELPTEXT:
case DFM_GETHELPTEXTW:
{
int idCmd = LOWORD(wParam);
int cchMax = HIWORD(wParam);
LPBYTE pBuf = (LPBYTE)lParam;
// map checkmark items to the correct message
switch (idCmd) {
case FSIDM_RESUMEPRN: idCmd = FSIDM_PAUSEPRN; break;
case FSIDM_WORKONLINE: idCmd = FSIDM_WORKOFFLINE; break;
}
if (uMsg == DFM_GETHELPTEXTW)
LoadStringW(HINST_THISDLL, idCmd + IDS_MH_FSIDM_FIRST,
(LPWSTR)pBuf, cchMax);
else
LoadStringA(HINST_THISDLL, idCmd + IDS_MH_FSIDM_FIRST,
(LPSTR)pBuf, cchMax);
break;
}
case DFM_INVOKECOMMAND:
{
BOOL fChooseNewDefault = FALSE;
int i;
for (i = pida->cidl - 1; i >= 0; i--)
{
LPIDPRINTER pidp = (LPIDPRINTER)IDA_GetIDListPtr(pida, i);
hres = Printer_InvokeCommand(hwndView, this, pidp, wParam, lParam, &fChooseNewDefault);
if (hres != NOERROR)
goto Bail;
}
if (fChooseNewDefault)
Printers_ChooseNewDefault(hwndView);
break;
} // case DFM_INVOKECOMMAND
default:
hres = E_NOTIMPL;
break;
} // switch (uMsg)
Bail:
HIDA_ReleaseStgMedium(pida, &medium);
} // if (medium.hGlobal)
} // if (ptdobj)
else
{
// on operation on the background -- we don't do anything.
}
return hres;
}
//---------------------------------------------------------------------------
//
// IDropTarget stuff
//
// printer thread data -- passed to ..._DropThreadInit
typedef struct {
LPIDLDROPTARGET pdt;
IDataObject *pDataObj;
DWORD grfKeyState;
POINTL pt;
DWORD dwEffect;
} PRNTD;
HRESULT CPrinter_DT_DragEnter(LPDROPTARGET pdt, IDataObject * pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdt);
DebugMsg(DM_TRACE, TEXT("sh - TR CPrinter::DragEnter"));
// We allow printer shares to be dropped for installing
// But we don't want to spend the time on DragEnter finding out if it's
// a printer share, so allow drops of any net resource or HIDA
// REVIEW: Actually, it wouldn't take long to check the first one, but
// sequencing through everything does seem like a pain.
// let the base-class process it now to save away the pdwEffect
CIDLDropTarget_DragEnter(pdt, pDataObj, grfKeyState, pt, pdwEffect);
if ((this->dwData & DTID_NETRES) || (this->dwData & DTID_HIDA))
{
*pdwEffect &= DROPEFFECT_LINK;
}
else
{
*pdwEffect = DROPEFFECT_NONE;
}
/*
if (this->dwData & DTID_NETRES)
*pdwEffect &= DROPEFFECT_LINK;
else
*pdwEffect = DROPEFFECT_NONE;
*/
this->dwEffectLastReturned = *pdwEffect;
return S_OK;
}
DWORD WINAPI CPrinter_DT_DropThreadInit(LPVOID pv)
{
PRNTD *pthp = (PRNTD *)pv;
STGMEDIUM medium;
#ifdef WINNT
HRESULT hres = E_FAIL;
LPITEMIDLIST pidlRemainder; // The part after the remote regitem
FORMATETC fmte = {g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
#else
FORMATETC fmte = {g_cfNetResource, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
#endif
#ifdef WINNT
// First try to drop as a link to a remote print folder
LPIDA pida = DataObj_GetHIDA(pthp->pDataObj, &medium);
if (pida)
{
// Make sure that if one item in the dataobject is a
// remote print folder, that they are all remote print folders.
// If none are, we just give up on dropping as a RPF link, and
// fall through to checking for printer shares via the
// NETRESOURCE clipboard format, below.
UINT i;
UINT cRPFs = 0;
UINT cNonRPFs = 0;
for (i = 0; i < pida->cidl; i++)
{
LPITEMIDLIST pidlTo = IDA_ILClone(pida, i);
if (pidlTo)
{
// *pidlRemainder will be NULL for remote print folders,
// and non-NULL for printers under remote print folders
if (NET_IsRemoteRegItem(pidlTo, &CLSID_CPrinters, &pidlRemainder)) // && (pidlRemainder->mkid.cb == 0))
{
ILFree(pidlTo);
cRPFs++;
}
else
{
ILFree(pidlTo);
cNonRPFs++;
}
}
}
if ((cRPFs > 0) && (cNonRPFs == 0))
{
// All the items in the dataobject are remote print folders or
// printers under remote printer folders
for (i = 0; i < pida->cidl; i++)
{
LPITEMIDLIST pidlTo = IDA_ILClone(pida, i);
if (pidlTo)
{
NET_IsRemoteRegItem(pidlTo, &CLSID_CPrinters, &pidlRemainder);
if (pidlRemainder->mkid.cb == 0)
{
// This is a remote printer folder. Drop a link to the
// 'PrintHood' directory
LPDROPTARGET pdt;
LPSHELLFOLDER psf = CPrintRoot_GetPSF(NULL);
if (psf)
{
hres = psf->lpVtbl->CreateViewObject(psf,
pthp->pdt->hwndOwner,
&IID_IDropTarget,
&pdt);
if (SUCCEEDED(hres))
{
DWORD dwEffect = DROPEFFECT_LINK;
//
// Note that we need to pass this->grfKeyStateLast so that Drop will
// use it to handle right-drag correctly with OLE.
//
POINTL pt;
hres = pdt->lpVtbl->DragEnter(pdt,
pthp->pDataObj,
pthp->grfKeyState,
pthp->pt,
&pthp->dwEffect);
if (SUCCEEDED(hres) && pthp->dwEffect)
{
hres = pdt->lpVtbl->Drop(pdt,
pthp->pDataObj,
pthp->grfKeyState,
pthp->pt,
&pthp->dwEffect);
}
pdt->lpVtbl->DragLeave(pdt);
}
}
}
else
{
// This should be a printer under a remote printer
// folder. Offer to install it.
LPITEMIDLIST pidl;
LPSHELLFOLDER psfRPF;
STRRET strret;
TCHAR szPrinter[MAXNAMELENBUFFER];
LPSHELLFOLDER psfDesktop = Desktop_GetShellFolder(TRUE);
LPITEMIDLIST pidlRemainderClone = ILClone(pidlRemainder);
if (!pidlRemainderClone)
{
ILFree(pidlTo);
break;
}
if (!ILRemoveLastID(pidlTo))
{
// pidlTo was a single idl
ILFree(pidlTo);
ILFree(pidlRemainderClone);
break;
}
// We use the root of evil to bind...
hres=psfDesktop->lpVtbl->BindToObject(psfDesktop, pidlTo,
NULL, &IID_IShellFolder, &psfRPF);
if (SUCCEEDED(hres))
{
hres = psfRPF->lpVtbl->GetDisplayNameOf(psfRPF, pidlRemainderClone,
SHGDN_FORPARSING, &strret);
if (SUCCEEDED(hres))
{
StrRetToStrN(szPrinter, ARRAYSIZE(szPrinter), &strret, pidlRemainderClone);
//
// Setup if not the add printer wizard.
//
if (lstrcmpi(szPrinter, c_szNewObject))
{
pidl = Printers_PrinterSetup(pthp->pdt->hwndOwner,
MSP_NETPRINTER, szPrinter, NULL);
if (pidl)
ILFree(pidl);
}
}
psfRPF->lpVtbl->Release(psfRPF);
}
ILFree(pidlRemainderClone);
}
ILFree(pidlTo);
if (FAILED(hres))
break;
}
}
HIDA_ReleaseStgMedium(pida, &medium);
SHChangeNotifyHandleEvents(); // force update now
goto Cleanup;
}
else if ((cRPFs > 0) && (cNonRPFs > 0))
{
// At least one, but not all, item(s) in this dataobject
// was a remote printer folder. Jump out now.
goto Cleanup;
}
// else none of the items in the dataobject were remote print
// folders, so fall through to the NETRESOURCE parsing
}
// Reset FORMATETC to NETRESOURCE clipformat for next GetData call
fmte.cfFormat = g_cfNetResource;
#endif // WINNT
// DragEnter only allows network resources to be DROPEFFECT_LINKed
Assert(NOERROR == pthp->pDataObj->lpVtbl->QueryGetData(pthp->pDataObj, &fmte));
if (SUCCEEDED(pthp->pDataObj->lpVtbl->GetData(pthp->pDataObj, &fmte, &medium)))
{
LPNETRESOURCE pnr = (LPNETRESOURCE)LocalAlloc(LPTR, 1024);
if (pnr)
{
BOOL fNonPrnShare = FALSE;
UINT iItem, cItems = SHGetNetResource(medium.hGlobal, (UINT)-1, NULL, 0);
for (iItem = 0; iItem < cItems; iItem++)
{
if (SHGetNetResource(medium.hGlobal, iItem, pnr, 1024) &&
pnr->dwDisplayType == RESOURCEDISPLAYTYPE_SHARE &&
pnr->dwType == RESOURCETYPE_PRINT)
{
LPITEMIDLIST pidl = Printers_GetInstalledNetPrinter(pnr->lpRemoteName);
if (pidl)
{
#ifdef ALIGNMENT_SCENARIO
TCHAR szPrinter[MAXNAMELENBUFFER];
#endif
LPCTSTR pszPrinter;
LPCIDPRINTER pidp = (LPCIDPRINTER)ILFindLastID(pidl);
int i;
// a printer connected to this share already exists,
// does the user really want to install another one?
SetForegroundWindow(pthp->pdt->hwndOwner);
#ifdef ALIGNMENT_SCENARIO
ualstrcpyn(szPrinter, pidp->cName, ARRAYSIZE(szPrinter));
pszPrinter = szPrinter;
#else
pszPrinter = (LPTSTR)pidp->cName;
#endif
i = ShellMessageBox(HINST_THISDLL,
pthp->pdt->hwndOwner,
MAKEINTRESOURCE(IDS_REINSTALLNETPRINTER), NULL,
MB_YESNO|MB_ICONINFORMATION,
pszPrinter, (LPTSTR)pnr->lpRemoteName);
ILFree(pidl);
if (i != IDYES)
continue;
}
pidl = Printers_PrinterSetup(pthp->pdt->hwndOwner,
MSP_NETPRINTER, pnr->lpRemoteName, NULL);
if (pidl)
ILFree(pidl);
}
else
{
if (!fNonPrnShare)
{
// so we don't get > 1 of these messages per drop
fNonPrnShare = TRUE;
// let the user know that they can't drop non-printer
// shares into the printers folder
SetForegroundWindow(pthp->pdt->hwndOwner);
ShellMessageBox(HINST_THISDLL,
pthp->pdt->hwndOwner,
MAKEINTRESOURCE(IDS_CANTINSTALLRESOURCE), NULL,
MB_OK|MB_ICONINFORMATION,
(LPTSTR)pnr->lpRemoteName);
}
}
}
LocalFree((HLOCAL)pnr);
}
SHReleaseStgMedium(&medium);
}
#ifdef WINNT
Cleanup:
#endif
pthp->pDataObj->lpVtbl->Release(pthp->pDataObj);
pthp->pdt->dropt.lpVtbl->Release(&pthp->pdt->dropt);
LocalFree((HLOCAL)pthp);
return 0;
}
HRESULT CPrinter_DT_Drop(LPDROPTARGET pdt, IDataObject * pDataObj, DWORD grfKeyState, POINTL pt, LPDWORD pdwEffect)
{
LPIDLDROPTARGET this = IToClass(CIDLDropTarget, dropt, pdt);
HRESULT hres;
DebugMsg(DM_TRACE, TEXT("sh - TR CPrinter::Drop"));
*pdwEffect = DROPEFFECT_LINK;
hres = CIDLDropTarget_DragDropMenu(this, DROPEFFECT_LINK, pDataObj,
pt, pdwEffect, NULL, NULL, MENU_PRINTOBJ_NEWPRN_DD, grfKeyState);
if (*pdwEffect)
{
//
// Note that we need to create another thread to avoid
// blocking the source thread.
//
PRNTD *pthp = (PRNTD *)LocalAlloc(LPTR, SIZEOF(PRNTD));
pthp->grfKeyState = grfKeyState;
pthp->pt = pt;
pthp->dwEffect = *pdwEffect;
if (pthp)
{
extern HRESULT CIDLData_Clone(LPDATAOBJECT pdtobjIn, UINT acf[], UINT ccf, LPDATAOBJECT *ppdtobjOut);
UINT acf[] = { g_cfNetResource, g_cfHIDA };
hres = CIDLData_Clone(pDataObj, acf, ARRAYSIZE(acf), &pthp->pDataObj);
if (SUCCEEDED(hres))
{
DWORD idThread;
HANDLE hthread;
this->dropt.lpVtbl->AddRef(pdt);
pthp->pdt = this;
if (NULL != (hthread = CreateThread(NULL, 0, CPrinter_DT_DropThreadInit, pthp, 0, &idThread)))
{
// We don't need to communicate with this thread any more.
// Close the handle and let it run and terminate itself.
//
CloseHandle(hthread);
pthp = NULL; // the thread will free it.
hres = NOERROR;
}
else
{
// Thread creation failed, we should release thread parameter.
pDataObj->lpVtbl->Release(pDataObj);
this->dropt.lpVtbl->Release(pdt);
hres = E_OUTOFMEMORY;
}
}
if (pthp) {
LocalFree((HLOCAL)pthp);
}
}
}
return hres;
}
#pragma data_seg(".text", "CODE")
IDropTargetVtbl c_CPrinterDropTargetVtbl =
{
CIDLDropTarget_QueryInterface,
CIDLDropTarget_AddRef,
CIDLDropTarget_Release,
CPrinter_DT_DragEnter,
CIDLDropTarget_DragOver,
CIDLDropTarget_DragLeave,
CPrinter_DT_Drop,
};
#pragma data_seg()
//---------------------------------------------------------------------------
//
// IDataObject stuff
//
// A printer's IDataObject is built on top of CIDL's IDataObject implementation
HRESULT CPrintersIDLData_QueryGetData(IDataObject * pdtobj, LPFORMATETC pformatetc)
{
if ((pformatetc->cfFormat == g_cfPrinterFriendlyName) &&
(pformatetc->tymed & TYMED_HGLOBAL))
{
return NOERROR; // same as S_OK
}
return CIDLData_QueryGetData(pdtobj, pformatetc);
}
HRESULT CPrintersIDLData_GetData(IDataObject * pdtobj, LPFORMATETC pformatetcIn, LPSTGMEDIUM pmedium)
{
HRESULT hres = E_INVALIDARG;
// g_cfPrinterFriendlyName creates an HDROP-like structure that contains
// friendly printer names (instead of absolute paths) for the objects
// in pdtobj. The handle returned from this can be used by the HDROP
// functions DragQueryFile, DragQueryInfo, ...
//
if ((pformatetcIn->cfFormat == g_cfPrinterFriendlyName) &&
(pformatetcIn->tymed & TYMED_HGLOBAL))
{
STGMEDIUM medium;
UINT i, cbRequired = SIZEOF(DROPFILES) + SIZEOF(TCHAR); // dbl null terminated
LPIDA pida = DataObj_GetHIDA(pdtobj, &medium);
for (i = 0; i < pida->cidl; i++)
{
LPIDPRINTER pidp = (LPIDPRINTER)IDA_GetIDListPtr(pida, i);
cbRequired += ualstrlen(pidp->cName ) * SIZEOF(TCHAR) + SIZEOF(TCHAR);
}
pmedium->pUnkForRelease = NULL; // caller should release hmem
pmedium->tymed = TYMED_HGLOBAL;
pmedium->hGlobal = GlobalAlloc(GPTR, cbRequired);
if (pmedium->hGlobal)
{
LPDROPFILES pdf = (LPDROPFILES)pmedium->hGlobal;
LPTSTR lps;
pdf->pFiles = SIZEOF(DROPFILES);
Assert(pdf->fWide == FALSE);
lps = (LPTSTR)((LPBYTE)pdf + pdf->pFiles);
for (i = 0; i < pida->cidl; i++)
{
LPIDPRINTER pidp = (LPIDPRINTER)IDA_GetIDListPtr(pida, i);
ualstrcpy(lps, pidp->cName);
lps += lstrlen(lps) + 1;
}
Assert(*lps == 0);
hres = NOERROR;
}
else
hres = E_OUTOFMEMORY;
HIDA_ReleaseStgMedium(pida, &medium);
}
else
{
hres = CIDLData_GetData(pdtobj, pformatetcIn, pmedium);
}
return hres;
}
#pragma data_seg(".text", "CODE")
IDataObjectVtbl c_CPrintersIDLDataVtbl = {
CIDLData_QueryInterface,
CIDLData_AddRef,
CIDLData_Release,
CPrintersIDLData_GetData,
CIDLData_GetDataHere,
CPrintersIDLData_QueryGetData,
CIDLData_GetCanonicalFormatEtc,
CIDLData_SetData,
CIDLData_EnumFormatEtc,
CIDLData_Advise,
CIDLData_Unadvise,
CIDLData_EnumAdvise
};
#pragma data_seg()
//---------------------------------------------------------------------------
//
// IShellFolder stuff
//
//
// The new notification system uses printui.dll's Folder library
// to cache printer data and retrieve notifications.
//
#ifndef PRN_FOLDERDATA
//
// Stuff to play with the hdpaPrinterInfo
//
#ifdef DEBUG
void CPrinters_EnterCriticalSection(PPrintersShellFolder psf)
{
if (psf->nRefCount)
DebugMsg(DM_TRACE,TEXT("sh TR - PRINTER_INFO_2 cache: waiting for critical section (%d)"), psf->nRefCount-1);
EnterCriticalSection(&(psf->csPrinterInfo));
psf->nRefCount++;
}
void CPrinters_LeaveCriticalSection(PPrintersShellFolder psf)
{
psf->nRefCount--;
LeaveCriticalSection(&(psf->csPrinterInfo));
}
#else
#define CPrinters_EnterCriticalSection(psf) EnterCriticalSection(&((psf)->csPrinterInfo))
#define CPrinters_LeaveCriticalSection(psf) LeaveCriticalSection(&((psf)->csPrinterInfo))
#endif
// CPrinters_SF_GetPrinterInfo2 -- gets a PRINTER_INFO_2 structure for pPrinter
// MUST use CPrinters_SF_FreePrinterInfo2 to free
LPPRINTER_INFO_2 CPrinters_SF_GetPrinterInfo2(PPrintersShellFolder psf, LPCTSTR pPrinterName)
{
PPrinterInfo pi;
int i;
CPrinters_EnterCriticalSection(psf);
for (i = DPA_GetPtrCount(psf->hdpaPrinterInfo)-1 ; i >= 0 ; --i)
{
pi = DPA_GetPtr(psf->hdpaPrinterInfo, i);
if (!lstrcmp(pi->pi2.pPrinterName, pPrinterName))
{
goto found;
}
}
pi = NULL;
found:
// i < 0 && pi == NULL || i >= 0 && pi != NULL
// if i<0 then we need to add a PRINTER_INFO_2 for pPrinterName
// if UPDATE_ON_TIMER and PRINTER_POLL_INTERVAL has elapsed, force update
if (i < 0 ||
(pi->flags & UPDATE_NOW) ||
(pi->flags & UPDATE_ON_TIMER &&
GetTickCount() - pi->dwTimeUpdated > PRINTER_POLL_INTERVAL))
{
HANDLE hPrinter = Printer_OpenPrinter(pPrinterName);
DWORD dwTime;
if (!hPrinter)
{
goto error;
}
if (i < 0)
{
// We add a typical amount to the PRINTER_INFO_2 to cut down on
// the number of calls into the subsystem. We want 700 bytes
// for the entire pi2 structure.
pi = (PPrinterInfo)(void*)LocalAlloc(LPTR, SIZEOF_PRINTERINFO(TYPICAL_PRINTER_INFO_2_SIZE));
if (!pi)
goto error;
pi->dwSize = TYPICAL_PRINTER_INFO_2_SIZE;
#undef TYPICAL_PRINTER_INFO_2_SIZE
}
dwTime = GetTickCount();
TryAgain:
SetLastError(0);
if (!g_pfnGetPrinter(hPrinter, 2, (LPBYTE)&(pi->pi2), pi->dwSize, &(pi->dwSize)))
{
DWORD dwSize = pi->dwSize;
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
{
pi = (void*)LocalReAlloc((HLOCAL)pi, SIZEOF_PRINTERINFO(dwSize),
LMEM_MOVEABLE|LMEM_ZEROINIT);
pi->dwSize = dwSize;
goto TryAgain;
}
else
{
LocalFree((HLOCAL)pi);
pi = NULL;
}
}
if (pi)
{
pi->dwTimeUpdated = dwTime;
pi->flags = FALSE;
// Always set this bit for now, since there's no way to hook the
// FSNotify stuff into this to turn the bit on. If this cache
// goes global, then we can do it right...
//if (pi->pi2.Attributes & PRINTER_ATTRIBUTE_NETWORK)
pi->flags = UPDATE_ON_TIMER;
if (i < 0)
{
// insert a new PRINTER_INFO_2
DebugMsg(DM_TRACE, TEXT("sh TR - PRINTER_INFO_2 cache: Inserting %s (%x)"), pi->pi2.pPrinterName, pi);
DPA_InsertPtr(psf->hdpaPrinterInfo,MAXSHORT,pi);
}
else
{
// update existing PRINTER_INFO_2
DebugMsg(DM_TRACE, TEXT("sh TR - PRINTER_INFO_2 cache: Updating %d: %s (%x)"), i, pi->pi2.pPrinterName, pi);
DPA_SetPtr(psf->hdpaPrinterInfo,i,pi);
}
}
Printer_ClosePrinter(hPrinter);
}
else
{
DebugMsg(DM_TRACE, TEXT("sh TR - PRINTER_INFO_2 cache: Found %d: %s (%x)"), i, pi->pi2.pPrinterName, pi);
}
error:
if (!pi)
{
CPrinters_LeaveCriticalSection(psf);
return FALSE;
}
// Seems to me like it's cheaper to keep the critical section locked
// and unlocking it an the _FreePrinterInfo2 call instead of allocating
// a block of memory, copying the PRINTER_INFO_2 structure, and then
// freeing the memory. (Since all calls to this only use
// the PRINTER_INFO_2 for a short period of time.) Plus, we will probably
// only have one thread running through here at any time anyhow.
return(&(pi->pi2));
}
// CPrinters_SF_FreePrinterInfo2 "frees" a printer_info_2 from
// the above function. Since we're really cachine these and forcing serial
// access to them, all psf function really does is free a critical section,
// so we don't need to pass the printer_info_2 pointer.
// REVIEW: how do we inline things like psf?
void CPrinters_SF_FreePrinterInfo2(PPrintersShellFolder psf)
{
CPrinters_LeaveCriticalSection(psf);
}
// psf function forces the next GetPrinterInfo2 to update.
// I want this to be called when an SHCNE_UPDATEITEM occurs on a printer,
// so we can turn on the UPDATE_ON_TIMER bit. But that's not going to happen.
// So we call this whenever the shell updates something, setting UPDATE_NOW.
void CPrinters_SF_UpdatePrinterInfo2(PPrintersShellFolder psf, LPCTSTR pPrinterName, UINT flags)
{
PPrinterInfo pi;
int i;
CPrinters_EnterCriticalSection(psf);
for (i = DPA_GetPtrCount(psf->hdpaPrinterInfo)-1 ; i >= 0 ; --i)
{
pi = DPA_GetPtr(psf->hdpaPrinterInfo, i);
if (!lstrcmp(pi->pi2.pPrinterName, pPrinterName))
{
DebugMsg(DM_TRACE, TEXT("sh TR - PRINTER_INFO_2 cache: Marking %d: %s (%x) as %x"), i, pi->pi2.pPrinterName, pi, flags);
pi->flags |= flags;
break;
}
}
CPrinters_LeaveCriticalSection(psf);
}
// psf removes a printer_info_2 from the cache.
// Called when a printer is deleted.
void CPrinters_SF_RemovePrinterInfo2(PPrintersShellFolder psf, LPCTSTR pPrinterName)
{
PPrinterInfo pi;
int i;
CPrinters_EnterCriticalSection(psf);
for (i = DPA_GetPtrCount(psf->hdpaPrinterInfo)-1 ; i >= 0 ; --i)
{
pi = DPA_GetPtr(psf->hdpaPrinterInfo, i);
if (!lstrcmp(pi->pi2.pPrinterName, pPrinterName))
{
DebugMsg(DM_TRACE, TEXT("sh TR - PRINTER_INFO_2 cache: Removing %d: %s (%x)"), i, pi->pi2.pPrinterName, pi);
LocalFree((HLOCAL)pi);
DPA_DeletePtr(psf->hdpaPrinterInfo, i);
break;
}
}
CPrinters_LeaveCriticalSection(psf);
}
// When the sf is going away, we need to free all our pointers
void CPrinters_SF_FreeHDPAPrinterInfo(HDPA hdpa)
{
PPrinterInfo pi;
int i;
DebugMsg(DM_TRACE,TEXT("sh TR - PRINTER_INFO_2 cache: freeing everything."));
// Since this is only called when the shell folder is going away,
// nobody is using the cache. So we don't take the critical section.
for (i = DPA_GetPtrCount(hdpa)-1 ; i >= 0 ; --i)
{
pi = DPA_GetPtr(hdpa, i);
LocalFree((HLOCAL)pi);
}
DPA_Destroy(hdpa);
}
#endif // ndef PRN_FOLDERDATA
ULONG CPrinters_SF_Release(IUnknown * punk)
{
PPrintersShellFolder this = IToClass(CPrintersShellFolder, cunk.unk, punk);
this->cunk.cRef--;
if (this->cunk.cRef > 0)
{
return(this->cunk.cRef);
}
#ifdef PRN_FOLDERDATA
if (this->hFolder)
{
g_pfnFolderUnregister(this->hFolder);
}
#else
// Gotta free this or we leak memory
UninitializeCriticalSection(&(this->csPrinterInfo));
CPrinters_SF_FreeHDPAPrinterInfo(this->hdpaPrinterInfo);
#endif
LocalFree((HLOCAL)this);
return(0);
}
STDMETHODIMP CPrinters_SF_ParseDisplayName(LPSHELLFOLDER psf, HWND hwndOwner,
LPBC pbc, LPOLESTR lpszDisplayName, ULONG * pchEaten, LPITEMIDLIST * ppidl, ULONG * pdwAttributes)
{
if (HOOD_COL_FILE == CPrintRoot_GetPIDLType(ppidl[0]))
{
LPSHELLFOLDER psf = CPrintRoot_GetPSF(NULL);
return psf->lpVtbl->ParseDisplayName(psf, hwndOwner, pbc, lpszDisplayName, pchEaten, ppidl, pdwAttributes);
}
return E_NOTIMPL;
}
STDMETHODIMP CPrinters_SF_GetAttributesOf(LPSHELLFOLDER psf, UINT cidl, LPCITEMIDLIST *apidl, ULONG *prgfInOut)
{
PPrintersShellFolder this = IToClass(CPrintersShellFolder, cunk.ck.unk, psf);
HRESULT hres = NOERROR;
ULONG rgfOut = SFGAO_CANLINK|SFGAO_CANDELETE|SFGAO_CANRENAME|SFGAO_HASPROPSHEET|SFGAO_DROPTARGET;
UINT i;
LPCTSTR pszPrinter;
LPCTSTR pszFullPrinter;
if ((cidl != 0) && HOOD_COL_FILE == CPrintRoot_GetPIDLType(apidl[0]))
{
LPSHELLFOLDER psf = CPrintRoot_GetPSF(NULL);
return psf->lpVtbl->GetAttributesOf(psf, cidl, apidl, prgfInOut);
}
// if c_szNewObject is selected, we support CANLINK *only*
for (i = 0 ; i < cidl ; i++)
{
LPIDPRINTER pidp = (LPIDPRINTER)apidl[i];
#ifdef ALIGNMENT_SCENARIO
TCHAR szPrinter[MAXNAMELENBUFFER];
ualstrcpyn(szPrinter, pidp->cName, ARRAYSIZE(szPrinter));
pszPrinter = szPrinter;
#else
pszPrinter = pidp->cName;
#endif
//
// if c_szNewObject is selected, we support CANLINK *only*
//
if (!lstrcmp(pszPrinter, c_szNewObject))
{
rgfOut &= SFGAO_CANLINK;
}
#ifdef WINNT
else
{
//
// Don't allow renaming of printer connections on WINNT.
// This is disallowed becase on WINNT, the printer connection
// name _must_ be the in the format \\server\printer. On
// win9x, the user can rename printer connections.
//
if (Printer_CheckNetworkPrinterByName(pszPrinter, NULL))
{
rgfOut &= ~SFGAO_CANRENAME;
}
}
#endif
}
*prgfInOut &= rgfOut;
if (cidl == 1 && rgfOut != SFGAO_CANLINK)
{
LPIDPRINTER pidp = (LPIDPRINTER)apidl[0];
LPVOID pData = NULL;
DWORD dwAttributes;
#ifdef WINNT
TCHAR szFullPrinter[MAXNAMELENBUFFER];
pszPrinter = Printer_BuildPrinterName(szFullPrinter, pidp->cName, this);
pszFullPrinter = szFullPrinter;
#else
pszFullPrinter = pszPrinter = pidp->cName;
#endif
#ifdef PRN_FOLDERDATA
//
// If we have notification code, use the hFolder to get
// printer data instead of querying the printer directly.
//
if (this->hFolder)
{
pData = Printer_FolderGetPrinter(this->hFolder, pszPrinter);
if (pData)
{
dwAttributes = ((PFOLDER_PRINTER_DATA)pData)->Attributes;
}
}
else
#endif
{
pData = Printer_GetPrinterInfoStr(pszFullPrinter, 5);
if (pData)
{
dwAttributes = ((PPRINTER_INFO_5)pData)->Attributes;
}
}
if (pData)
{
if (dwAttributes & PRINTER_ATTRIBUTE_SHARED
#ifdef WINNT
//
// NT appears to return all network printers with their
// share bit on. I think this is intentional.
//
&& (dwAttributes & PRINTER_ATTRIBUTE_NETWORK) == 0
#endif
)
{
*prgfInOut |= SFGAO_SHARE;
}
#ifndef WINNT
if (Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE)
{
*prgfInOut |= SFGAO_GHOSTED;
}
#endif
LocalFree((HLOCAL)pData);
}
else
hres = E_OUTOFMEMORY;
}
return(hres);
}
//
// Stolen almost verbatim from netviewx.c's CNetRoot_MakeStripToLikeKinds
//
// Takes a possibly-heterogenous pidl array, and strips out the pidls that
// don't match the requested type. (If fPrinterObjects is TRUE, we're asking
// for printers pidls, otherwise we're asking for the filesystem/link
// objects.) The return value is TRUE if we had to allocate a new array
// in which to return the reduced set of pidls (in which case the caller
// should free the array with LocalFree()), FALSE if we are returning the
// original array of pidls (in which case no cleanup is required).
//
BOOL CPrinters_ReduceToLikeKinds(UINT *pcidl, LPCITEMIDLIST **papidl, BOOL fPrintObjects)
{
LPITEMIDLIST *apidl = (LPITEMIDLIST*)*papidl;
int cidl = *pcidl;
int iidl;
LPITEMIDLIST *apidlHomo;
int cpidlHomo;
for (iidl = 0; iidl < cidl; iidl++)
{
if ((HOOD_COL_PRINTER == CPrintRoot_GetPIDLType(apidl[iidl])) != fPrintObjects)
{
apidlHomo = (LPITEMIDLIST *)LocalAlloc(LPTR, SIZEOF(LPITEMIDLIST) * cidl);
if (!apidlHomo)
return FALSE;
cpidlHomo = 0;
for (iidl = 0; iidl < cidl; iidl++)
{
if ((HOOD_COL_PRINTER == CPrintRoot_GetPIDLType(apidl[iidl])) == fPrintObjects)
apidlHomo[cpidlHomo++] = apidl[iidl];
}
// Setup to use the stripped version of the pidl array...
*pcidl = cpidlHomo;
*papidl = apidlHomo;
return TRUE;
}
}
return FALSE;
}
STDMETHODIMP CPrinters_SF_GetUIObjectOf(LPSHELLFOLDER psf, HWND hwndOwner, UINT cidl,
LPCITEMIDLIST *apidl, REFIID riid, UINT *prgfInOut, LPVOID *ppvOut)
{
PPrintersShellFolder this = IToClass(CPrintersShellFolder, cunk.ck.unk, psf);
HRESULT hres = E_INVALIDARG;
LPCTSTR pszPrinter;
LPTSTR pszFullPrinter;
BOOL fStripped = FALSE;
#ifdef WINNT
TCHAR szFullPrinter[MAXNAMELENBUFFER];
#endif
//
// If we have a multi-select case, then we'll strip out any objects
// not of the same type (link vs. printer) in the pidl array before
// either deferring to the PrintHood CFSFolder implementation, or
// falling through to the previous code, which was printers-only.
//
if ((cidl != 0) && HOOD_COL_FILE == CPrintRoot_GetPIDLType(apidl[0]))
{
// Defer to the filesystem for links
LPSHELLFOLDER psfPrintRoot;
fStripped = CPrinters_ReduceToLikeKinds(&cidl, &apidl, FALSE);
psfPrintRoot = CPrintRoot_GetPSF(NULL);
hres = psfPrintRoot->lpVtbl->GetUIObjectOf(psfPrintRoot, hwndOwner, cidl, apidl, riid, prgfInOut, ppvOut);
}
else
{
fStripped = CPrinters_ReduceToLikeKinds(&cidl, &apidl, TRUE);
#ifdef WINNT
pszPrinter = Printer_BuildPrinterName(szFullPrinter,
((LPIDPRINTER)apidl[0])->cName,
this);
pszFullPrinter = szFullPrinter;
#else
pszFullPrinter = pszPrinter = ((LPIDPRINTER)apidl[0])->cName;
#endif
if (cidl==1 && (IsEqualIID(riid, &IID_IExtractIcon)
#ifdef UNICODE
|| IsEqualIID(riid, &IID_IExtractIconA)
#endif
))
{
int iIcon;
TCHAR szBuf[MAX_PATH+20];
LPTSTR pszModule = NULL;
if (!lstrcmp(pszPrinter, c_szNewObject))
{
iIcon = IDI_NEWPRN;
}
else
{
pszModule = Printer_FindIcon(pszFullPrinter, szBuf,
ARRAYSIZE(szBuf), &iIcon, this);
}
hres = SHCreateDefExtIcon(pszModule, EIRESID(iIcon), -1, GIL_PERINSTANCE,
(LPEXTRACTICON *)ppvOut);
#ifdef UNICODE
if (SUCCEEDED(hres) && IsEqualIID(riid, &IID_IExtractIconA))
{
LPEXTRACTICON pxicon = *ppvOut;
hres = pxicon->lpVtbl->QueryInterface(pxicon,riid,ppvOut);
pxicon->lpVtbl->Release(pxicon);
}
#endif
}
else if (cidl>0 && IsEqualIID(riid, &IID_IContextMenu))
{
HKEY hkeyBaseProgID = NULL;
int nCount=0;
if (ERROR_SUCCESS == RegOpenKey(HKEY_CLASSES_ROOT, c_szPrinters, &hkeyBaseProgID))
nCount++;
hres = CDefFolderMenu_Create2(this->lpcinfo->pidl, hwndOwner,
cidl, apidl, psf, CPrinters_DFMCallBack,
nCount, &hkeyBaseProgID, (LPCONTEXTMENU *)ppvOut);
if (hkeyBaseProgID)
RegCloseKey(hkeyBaseProgID);
}
else if (cidl>0 && IsEqualIID(riid, &IID_IDataObject))
{
hres = CIDLData_CreateFromIDArray2(&c_CPrintersIDLDataVtbl,
this->lpcinfo->pidl, cidl, apidl, (IDataObject * *)ppvOut);
}
else if (cidl==1 && IsEqualIID(riid, &IID_IDropTarget))
{
LPIDPRINTER pidp = (LPIDPRINTER)apidl[0];
#ifdef WINNT
//
// Only allow drag and drop operations to the local print
// folder.
//
if (!GetServerFromPSF(this)[0])
#endif
{
if (!lstrcmp(pszPrinter, c_szNewObject))
{
// "NewPrinter" accepts network printer shares
hres = CIDLDropTarget_Create(hwndOwner, &c_CPrinterDropTargetVtbl,
NULL, (LPDROPTARGET *)ppvOut);
}
else
{
extern IDropTargetVtbl c_CPrintObjsDropTargetVtbl;
// regular printer objects accept files
hres = CIDLDropTarget_Create(hwndOwner, &c_CPrintObjsDropTargetVtbl,
(LPITEMIDLIST)pidp, (LPDROPTARGET *)ppvOut);
}
}
}
}
if (fStripped)
{
LocalFree((HLOCAL)apidl);
}
return hres;
}
STDMETHODIMP CPrinters_SF_EnumObjects(LPSHELLFOLDER psf, HWND hwndOwner, DWORD grfFlags, LPENUMIDLIST *ppenumUnknown)
{
#ifdef WINNT
PPrintersShellFolder this = IToClass(CPrintersShellFolder, cunk.ck.unk, psf);
#endif
LPSHELLFOLDER psfPrintHood;
PPrintersEnumShellFolder pesf = (PPrintersEnumShellFolder)LocalAlloc(LPTR, SIZEOF(CPrintersEnumShellFolder));
if (!pesf)
{
*ppenumUnknown = NULL;
return E_OUTOFMEMORY;
}
#ifdef PRN_FOLDERDATA
//
// Create the folder structure that holds all printer information.
// We store this in SF rather than ESF since we want this data
// to be persistent across enumerations.
//
if (!PrintUIDLL_Init())
{
LocalFree((HLOCAL)pesf);
*ppenumUnknown = NULL;
return E_OUTOFMEMORY;
}
if (!this->hFolder)
{
this->hFolder = g_pfnFolderRegister(this->szServer, this->lpcinfo->pidl);
if (!this->hFolder)
{
LocalFree((HLOCAL)pesf);
*ppenumUnknown = NULL;
return E_OUTOFMEMORY;
}
}
#endif
//
// Always try to enum links.
//
psfPrintHood = CPrintRoot_GetPSF(hwndOwner);
// By default we always do standard (printer) enumeration
pesf->dwRemote = 0;
// Only add links (from the PrintHood directory) to the enumeration
// if this is the local print folder
if (!GetServerFromPSF(this)[0])
{
if (psfPrintHood)
{
psfPrintHood->lpVtbl->EnumObjects(psfPrintHood, NULL, grfFlags, &(pesf->peunk));
}
if (pesf->peunk)
{
// If this went OK, we will also enumerate links
pesf->dwRemote |= RMF_SHOWLINKS;
}
}
pesf->cunk.unk.lpVtbl = (IUnknownVtbl *) &s_PrintersESFVtbl;
pesf->cunk.cRef = 1;
pesf->cunk.riid = &IID_IEnumIDList;
pesf->uFlags = grfFlags;
pesf->nLastFound = -1;
#ifdef WINNT
//
// Grab a reference to psf so that we can get the server name.
//
psf->lpVtbl->AddRef(psf);
pesf->psf = psf;
#endif
*ppenumUnknown = (IEnumIDList *)&(pesf->cunk.unk);
return NOERROR;
}
STDMETHODIMP CPrinters_SF_CompareIDs(LPSHELLFOLDER psf, LPARAM iCol,
LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
UNALIGNED IDPRINTER *pidp1 = (UNALIGNED IDPRINTER*)pidl1;
UNALIGNED IDPRINTER *pidp2 = (UNALIGNED IDPRINTER*)pidl2;
PIDLTYPE ColateType1 = CPrintRoot_GetPIDLType(pidl1);
PIDLTYPE ColateType2 = CPrintRoot_GetPIDLType(pidl2);
if (ColateType1 == ColateType2) {
// pidls are of same type.
if (ColateType1 == HOOD_COL_FILE) {
// pidls are both of type file, so pass on to the IShellFolder
// interface for the hoods custom directory.
psf = CPrintRoot_GetPSF(NULL);
if (psf)
{
return psf->lpVtbl->CompareIDs(psf, iCol, pidl1, pidl2);
}
}
else
{
// pidls are same and not files, so much be printers
INT i;
if (pidp1->dwType != pidp2->dwType)
{
return (pidp1->dwType < pidp2->dwType) ?
ResultFromShort(-1) :
ResultFromShort(1);
}
i = ualstrcmpi(pidp1->cName, pidp2->cName);
if (i != 0)
{
// c_szNewObject is "less" than everything else
if (!ualstrcmp(pidp1->cName, c_szNewObject))
return( ResultFromShort(-1) );
else if (!ualstrcmp(pidp2->cName, c_szNewObject))
return( ResultFromShort(1) );
}
return( ResultFromShort(i) );
}
}
else {
// pidls are not of same type, so have already been correctly
// collated (consequently, sorting is first by type and
// then by subfield).
return ResultFromShort((( (INT)(ColateType2 - ColateType1)) > 0) ? -1 : 1);
}
}
//===========================================================================
//
// To be called back from within CDefFolderMenu
//
HRESULT CALLBACK CPrinters_DFMCallBackBG(LPSHELLFOLDER psf, HWND hwndOwner,
IDataObject * pdtobj, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
PPrintersShellFolder this = IToClass(CPrintersShellFolder, cunk.ck.unk, psf);
HRESULT hres = NOERROR;
LPQCMINFO pqcm;
UINT idCmdBase;
switch(uMsg)
{
case DFM_MERGECONTEXTMENU:
pqcm = (LPQCMINFO)lParam;
idCmdBase = pqcm->idCmdFirst; // must be called before merge
CDefFolderMenu_MergeMenu(HINST_THISDLL, POPUP_DRIVES_PRINTERS, POPUP_PRINTERS_POPUPMERGE, pqcm);
break;
case DFM_GETHELPTEXT:
LoadStringA(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPSTR)lParam, HIWORD(wParam));
break;
case DFM_GETHELPTEXTW:
LoadStringW(HINST_THISDLL, LOWORD(wParam) + IDS_MH_FSIDM_FIRST, (LPWSTR)lParam, HIWORD(wParam));
break;
case DFM_INVOKECOMMAND:
switch(wParam)
{
case FSIDM_CONNECT_PRN:
SHNetConnectionDialog(hwndOwner, NULL, RESOURCETYPE_PRINT);
break;
case FSIDM_DISCONNECT_PRN:
WNetDisconnectDialog(hwndOwner, RESOURCETYPE_PRINT);
break;
case FSIDM_SORTBYNAME:
ShellFolderView_ReArrange(hwndOwner, 0);
break;
#ifdef WINNT
case FSIDM_SERVERPROPERTIES:
Printers_DoCommandEx(hwndOwner,
PRINTACTION_SERVERPROPERTIES,
this->szServer,
NULL, 0 );
break;
#endif
default:
// This is one of view menu items, use the default code.
hres = S_FALSE;
break;
}
break;
default:
hres = E_NOTIMPL;
break;
}
return hres;
}
HRESULT CALLBACK Printers_FNVCallBack(LPSHELLVIEW psvOuter,
LPSHELLFOLDER psf,
HWND hwndOwner,
UINT uMsg,
WPARAM wParam,
LPARAM lParam)
{
HRESULT hres = NOERROR; // assume no error
PPrintersShellFolder this = IToClass(CPrintersShellFolder, cunk.ck.unk, psf);
switch(uMsg)
{
case DVM_FOLDERISPARENT:
{
// View needs to know if we are the parent of a particular pidl, so test
// against the two possible "parents" of our confused children
LPCITEMIDLIST pidlChild = (LPCITEMIDLIST) lParam;
if (FALSE == ILIsParent(CPrintRoot_GetPIDL(NULL), pidlChild, TRUE) &&
FALSE == ILIsParent(this->lpcinfo->pidl, pidlChild, TRUE))
{
hres = S_FALSE;
}
else
{
hres = S_OK;
}
break;
}
case DVM_MERGEMENU:
CDefFolderMenu_MergeMenu(HINST_THISDLL, 0, POPUP_PRINTERS_POPUPMERGE, (LPQCMINFO)lParam);
break;
case DVM_WINDOWCREATED:
{
// Register change notifications for the pidl of the PrintHood dir
HWND hwndView = (HWND) wParam;
SHChangeNotifyEntry fsne;
LPSHELLBROWSER psb;
if (hwndView)
{
fsne.pidl = CPrintRoot_GetPIDL(NULL);
fsne.fRecursive = FALSE;
this->uRegister = SHChangeNotifyRegister(hwndView,
SHCNRF_NewDelivery | SHCNRF_ShellLevel | SHCNRF_InterruptLevel,
SHCNE_DISKEVENTS,
WM_DSV_FSNOTIFY,
1,
&fsne);
}
break;
}
case DVM_WINDOWDESTROY:
{
if (this->uRegister)
{
SHChangeNotifyDeregister(this->uRegister);
}
break;
}
case DVM_INVOKECOMMAND:
hres = CPrinters_DFMCallBackBG(psf, hwndOwner, NULL, DFM_INVOKECOMMAND, wParam, lParam);
break;
case DVM_GETHELPTEXT:
#ifdef UNICODE
hres = CPrinters_DFMCallBackBG(psf, hwndOwner, NULL, DFM_GETHELPTEXTW, wParam, lParam);
#else
hres = CPrinters_DFMCallBackBG(psf, hwndOwner, NULL, DFM_GETHELPTEXT, wParam, lParam);
#endif
break;
#ifdef WINNT
//
// Enumerating printer over the net may be slow; do this in a
// separate thread. If szServer is valid (not-szNull), then
// background the operation.
//
case DVM_BACKGROUNDENUM:
hres = this->szServer[0] ?
S_OK :
E_FAIL;
break;
#endif
default:
hres = E_FAIL;
}
return hres;
}
HRESULT CPrinters_SD_Create(LPSHELLFOLDER psf, HWND hwndMain, LPVOID * ppvOut);
STDMETHODIMP CPrinters_SF_CreateViewObject(LPSHELLFOLDER psf, HWND hwnd,
REFIID riid, LPVOID * ppvOut)
{
PPrintersShellFolder this = IToClass(CPrintersShellFolder, cunk.ck.unk, psf);
if (IsEqualIID(riid, &IID_IShellView))
{
HRESULT hres;
CSFV csfv = {
SIZEOF(CSFV), // cbSize
psf, // pshf
NULL, // psvOuter
this->lpcinfo->pidl, // pidl
SHCNE_UPDATEITEM|SHCNE_DELETE|SHCNE_RENAMEITEM|SHCNE_CREATE|SHCNE_ATTRIBUTES, // lEvents
Printers_FNVCallBack, // pfnCallback
0,
};
hres = SHCreateShellFolderViewEx(&csfv, (LPSHELLVIEW *)ppvOut);
return hres;
}
else if (IsEqualIID(riid, &IID_IDropTarget))
{
return CIDLDropTarget_Create(hwnd, &c_CPrinterDropTargetVtbl,
NULL, (LPDROPTARGET *)ppvOut);
}
else if (IsEqualIID(riid, &IID_IShellDetails))
{
return(CPrinters_SD_Create(psf, hwnd, ppvOut));
}
else if (IsEqualIID(riid, &IID_IContextMenu))
{
return CDefFolderMenu_Create2(NULL, hwnd,
0, NULL, psf, CPrinters_DFMCallBackBG,
0, NULL, (LPCONTEXTMENU *)ppvOut);
}
*ppvOut = NULL;
return E_NOINTERFACE;
}
STDMETHODIMP CPrinters_SF_GetDisplayNameOf(LPSHELLFOLDER psf,
LPCITEMIDLIST pidl, DWORD uFlags, LPSTRRET lpName)
{
LPIDPRINTER pidc = (LPIDPRINTER)pidl;
#ifdef UNICODE
BOOL bPrinterOnServerFormat = FALSE;
LPTSTR pszServer;
TCHAR szBuffer[MAXNAMELENBUFFER];
TCHAR szTemp[MAXNAMELENBUFFER];
LPTSTR pszTemp;
LPTSTR pszPrinter = szBuffer;
#endif
PPrintersShellFolder this = IToClass(CPrintersShellFolder,
cunk.ck.unk, psf);
if (pidl && HOOD_COL_FILE == CPrintRoot_GetPIDLType(pidl))
{
LPSHELLFOLDER psf = CPrintRoot_GetPSF(NULL);
return psf->lpVtbl->GetDisplayNameOf(psf, pidl, uFlags, lpName);
}
// BUGBUG: I should do this with a flag instead of a string
if (ualstrcmpi(pidc->cName, c_szNewObject))
{
UINT uOffset = 0;
#if defined(WINNT) && !defined(PRN_FOLDERDATA)
//
// If remoted, then strip off server prefix. We only need to
// do this for EnumPrinters, since the notifications strips
// them off for us.
//
if (TEXT('\0') != this->szServer[0])
{
Assert(pidc->cName[0] == TEXT('\\') &&
pidc->cName[1] == TEXT('\\') &&
IntlStrEqNI(this->szServer,
pidc->cName,
lstrlen(this->szServer)));
uOffset += (lstrlen(this->szServer)+1)*sizeof(TCHAR);
}
#endif
#ifdef UNICODE
#ifdef ALIGNMENT_SCENARIO
ualstrcpyn(szBuffer, pidc->cName+uOffset, ARRAYSIZE(szBuffer));
pszPrinter = szBuffer;
#else
pszPrinter = pidc->cName+uOffset;
#endif
switch (uFlags)
{
case SHGDN_INFOLDER:
//
// Show just the printer name, not fully qualified.
// Note: this assumes that the cName is not fully
// qualified for local printers in the local printer
// folder.
//
//
// If it's a connection then format as "printer on server."
//
Printer_SplitFullName(szTemp, pszPrinter, &pszServer, &pszTemp);
if (pszServer[0])
{
bPrinterOnServerFormat = TRUE;
pszPrinter = pszTemp;
}
break;
case SHGDN_NORMAL:
//
// If it's a RPF then extract the server name from psf.
// Note in the case of masq connections, we still do this
// (for gateway services: sharing a masq printer).
//
if (this->szServer[0])
{
pszServer = this->szServer;
bPrinterOnServerFormat = TRUE;
}
else
{
//
// If it's a connection then format as "printer on server."
//
Printer_SplitFullName(szTemp, pszPrinter, &pszServer, &pszTemp);
if (pszServer[0])
{
bPrinterOnServerFormat = TRUE;
pszPrinter = pszTemp;
}
}
break;
default:
Assert(uFlags == SHGDN_FORPARSING);
//
// Fully qualify the printer name if it's not
// the add printer wizard.
//
if (lstrcmpi(c_szNewObject, pszPrinter))
{
Printer_BuildPrinterName(szTemp, pszPrinter, this);
pszPrinter = szTemp;
}
break;
}
#else
lpName->uOffset = FIELDOFFSET(IDPRINTER, cName) + uOffset;
lpName->uType = STRRET_OFFSET;
#endif // ndef UNICODE
}
else
{
#ifdef UNICODE
LoadString(HINST_THISDLL, IDS_NEWPRN, szBuffer, ARRAYSIZE(szBuffer));
//
// Use "Add Printer Wizard on \\server" description only if not
// remote and if not in folder view (e.g., on the desktop).
//
if (this->szServer[0] && (uFlags == SHGDN_NORMAL))
{
bPrinterOnServerFormat = TRUE;
pszServer = this->szServer;
pszPrinter = szBuffer;
}
#else
lpName->uType = STRRET_CSTR;
LoadString(HINST_THISDLL, IDS_NEWPRN, lpName->cStr, ARRAYSIZE(lpName->cStr));
#endif
}
#ifdef UNICODE
if (bPrinterOnServerFormat)
{
//
// When bRemote is set, we want to translate the name to
// "printer on server." Note: we should not have a rename problem
// since renaming connections is disallowed.
//
// pszServer and pszPrinter must be initialize if bRemote is TRUE.
// Also skip the leading backslashes for the server name.
//
LPTSTR pszRet;
Assert(pszServer[0] == TEXT('\\') && pszServer[1] == TEXT('\\'));
pszRet = ShellConstructMessageString(HINST_THISDLL,
MAKEINTRESOURCE(IDS_DSPTEMPLATE_WITH_ON),
&pszServer[2], pszPrinter);
if (!pszRet)
{
return E_FAIL;
}
lpName->uType = STRRET_OLESTR;
lpName->pOleStr = pszRet;
}
else
{
//
// Convert to STRET_OLESTR.
//
lpName->pOleStr = (LPOLESTR)SHAlloc((lstrlen(pszPrinter)+1)*SIZEOF(TCHAR));
if ( lpName->pOleStr != NULL ) {
lpName->uType = STRRET_OLESTR;
lstrcpy(lpName->pOleStr, pszPrinter);
} else {
return E_OUTOFMEMORY;
}
}
#endif
return(NOERROR);
}
HRESULT Printer_SetNameOf(PPrintersShellFolder psf, HWND hwndOwner,
LPTSTR pOldName, LPTSTR pNewName, LPITEMIDLIST *ppidlOut)
{
HRESULT hres = E_OUTOFMEMORY;
HANDLE hPrinter;
LPCTSTR pFullOldName;
#ifdef WINNT
TCHAR szFullPrinter[MAXNAMELENBUFFER];
if (psf)
{
Printer_BuildPrinterName( szFullPrinter, pOldName, psf );
pFullOldName = szFullPrinter;
}
#else
pFullOldName = pOldName;
#endif
hPrinter = Printer_OpenPrinterAdmin(pFullOldName);
if (hPrinter)
{
LPPRINTER_INFO_2 pPrinter = Printer_GetPrinterInfo(hPrinter, 2);
if (pPrinter)
{
int nTmp;
if (0 != (nTmp = Printer_IllegalName(pNewName)))
{
ShellMessageBox(HINST_THISDLL, hwndOwner, MAKEINTRESOURCE(nTmp),
MAKEINTRESOURCE(IDS_PRINTERS),
MB_OK|MB_ICONEXCLAMATION);
}
else if (IDYES != CallPrinterCopyHooks(hwndOwner, PO_RENAME, 0,
pNewName, 0, pFullOldName, 0))
{
// user canceled a shared printer name change, bail.
hres = E_FAIL;
}
else
{
pPrinter->pPrinterName = pNewName;
if (g_pfnSetPrinter(hPrinter, 2, (LPBYTE)pPrinter, 0))
{
hres = NOERROR;
#ifndef WINNT
// It looks like we need to generate a SHCNE_RENAMEITEM event
Printer_SHChangeNotifyRename(pFullOldName, pNewName);
// And we need to rename any open queue view
PrintDef_UpdateName(pFullOldName, pNewName);
PrintDef_RefreshQueue(pNewName);
#endif
// return the new pidl if requested
if (ppidlOut)
{
LPIDPRINTER pidp;
USHORT cb = FIELDOFFSET(IDPRINTER, cName) + (lstrlen(pNewName) + 1) * SIZEOF(TCHAR);
pidp = (LPIDPRINTER)SHAlloc(cb + SIZEOF(USHORT));
if (pidp)
{
Printers_FillPidl(pidp, pNewName);
*ppidlOut = (LPITEMIDLIST)pidp;
}
else
{
hres = E_OUTOFMEMORY;
}
} // if (ppidlOut)
} // if (g_pfnSetPrinter...
}
LocalFree((HLOCAL)pPrinter);
}
Printer_ClosePrinter(hPrinter);
}
return hres;
}
STDMETHODIMP CPrinters_SF_SetNameOf(LPSHELLFOLDER psf, HWND hwndOwner,
LPCITEMIDLIST pidl, LPCOLESTR lpszName, DWORD dwReserved, LPITEMIDLIST *ppidlOut)
{
LPIDPRINTER pidc = (LPIDPRINTER)pidl;
TCHAR szNewName[MAX_PATH];
PPrintersShellFolder this = IToClass(CPrintersShellFolder, cunk.ck.unk, psf);
Assert(ualstrcmp(pidc->cName, c_szNewObject));
if (HOOD_COL_FILE == CPrintRoot_GetPIDLType(pidl))
{
LPSHELLFOLDER psf = CPrintRoot_GetPSF(NULL);
return psf->lpVtbl->SetNameOf(psf, hwndOwner, pidl, lpszName, dwReserved, ppidlOut);
}
OleStrToStr(szNewName, lpszName);
PathRemoveBlanks(szNewName);
return Printer_SetNameOf(this, hwndOwner, pidc->cName, szNewName, ppidlOut);
}
#pragma data_seg(".text", "CODE")
IUnknownVtbl s_PrintersAggSFVtbl =
{
WCommonUnknown_QueryInterface,
WCommonUnknown_AddRef,
CPrinters_SF_Release,
};
IShellFolderVtbl s_PrintersSFVtbl =
{
WCommonKnown_QueryInterface,
WCommonKnown_AddRef,
WCommonKnown_Release,
CPrinters_SF_ParseDisplayName,
CPrinters_SF_EnumObjects,
CDefShellFolder_BindToObject,
CDefShellFolder_BindToStorage,
CPrinters_SF_CompareIDs,
CPrinters_SF_CreateViewObject,
CPrinters_SF_GetAttributesOf,
CPrinters_SF_GetUIObjectOf,
CPrinters_SF_GetDisplayNameOf,
CPrinters_SF_SetNameOf,
};
#pragma data_seg()
HRESULT Printers_CreateSF(IUnknown *punkOuter, LPCOMMINFO lpcinfo, REFIID riid,
IUnknown **punkAgg)
{
HRESULT hres;
// if someone's creating an IShellFolder, let's assume they will call
// into the interface. Since just about every function requires the
// winspool functions, make sure the subsystem is loaded here.
if (!WinspoolDLL_Init())
return E_OUTOFMEMORY;
hres = WU_CreateInterface(SIZEOF(CPrintersShellFolder), &IID_IShellFolder,
&s_PrintersAggSFVtbl, &s_PrintersSFVtbl, punkOuter, riid, punkAgg);
if (SUCCEEDED(hres))
{
PPrintersShellFolder this = IToClass(CPrintersShellFolder, cunk.unk, *punkAgg);
SPrintersObj* that;
this->lpcinfo = lpcinfo;
#ifdef WINNT
that = (SPrintersObj*)this->lpcinfo->lpData;
//
// Copy the server name in.
//
if (that->pszMachineName)
{
ualstrcpyn(this->szServer, that->pszMachineName, MAXCOMPUTERNAME);
}
#endif
#ifdef PRN_FOLDERDATA
//
// Note: register the folder only when ESF is created, rather
// than here, since we don't want to start the notifications
// if we are just binding.
//
#else
// Allocated zero-init, so this is ok.
ReinitializeCriticalSection(&(this->csPrinterInfo));
this->hdpaPrinterInfo = DPA_Create(4);
if (!this->hdpaPrinterInfo)
{
CPrinters_SF_Release(*punkAgg);
hres = E_OUTOFMEMORY;
}
#endif
}
return hres;
}
//---------------------------------------------------------------------------
//
// IShellDetails stuff
//
enum
{
PRINTERS_ICOL_NAME = 0,
PRINTERS_ICOL_QUEUESIZE,
PRINTERS_ICOL_STATUS,
PRINTERS_ICOL_COMMENT,
PRINTERS_ICOL_MAX
} ;
#pragma data_seg(DATASEG_READONLY)
struct _PRINTERCOLS
{
UINT uID;
int fmt;
int cxChar;
} s_printers_cols[] =
{
{ IDS_PSD_PRNNAME , LVCFMT_LEFT , 20, },
{ IDS_PSD_QUEUESIZE, LVCFMT_CENTER, 12, },
{ IDS_PRQ_STATUS , LVCFMT_LEFT , 12, },
{ IDS_PSD_COMMENT , LVCFMT_LEFT , 25, },
} ;
STATUSSTUFF ssPrinterStatus[] =
{
PRINTER_STATUS_PAUSED, IDS_PRQSTATUS_PAUSED,
PRINTER_STATUS_ERROR, IDS_PRQSTATUS_ERROR,
PRINTER_STATUS_PENDING_DELETION, IDS_PRQSTATUS_PENDING_DELETION,
PRINTER_STATUS_PAPER_JAM, IDS_PRQSTATUS_PAPER_JAM,
PRINTER_STATUS_PAPER_OUT, IDS_PRQSTATUS_PAPER_OUT,
PRINTER_STATUS_MANUAL_FEED, IDS_PRQSTATUS_MANUAL_FEED,
PRINTER_STATUS_PAPER_PROBLEM, IDS_PRQSTATUS_PAPER_PROBLEM,
PRINTER_STATUS_OFFLINE, IDS_PRQSTATUS_OFFLINE,
PRINTER_STATUS_IO_ACTIVE, IDS_PRQSTATUS_IO_ACTIVE,
PRINTER_STATUS_BUSY, IDS_PRQSTATUS_BUSY,
PRINTER_STATUS_PRINTING, IDS_PRQSTATUS_PRINTING,
PRINTER_STATUS_OUTPUT_BIN_FULL, IDS_PRQSTATUS_OUTPUT_BIN_FULL,
PRINTER_STATUS_NOT_AVAILABLE, IDS_PRQSTATUS_NOT_AVAILABLE,
PRINTER_STATUS_WAITING, IDS_PRQSTATUS_WAITING,
PRINTER_STATUS_PROCESSING, IDS_PRQSTATUS_PROCESSING,
PRINTER_STATUS_INITIALIZING, IDS_PRQSTATUS_INITIALIZING,
PRINTER_STATUS_WARMING_UP, IDS_PRQSTATUS_WARMING_UP,
PRINTER_STATUS_TONER_LOW, IDS_PRQSTATUS_TONER_LOW,
PRINTER_STATUS_NO_TONER, IDS_PRQSTATUS_NO_TONER,
PRINTER_STATUS_PAGE_PUNT, IDS_PRQSTATUS_PAGE_PUNT,
PRINTER_STATUS_USER_INTERVENTION, IDS_PRQSTATUS_USER_INTERVENTION,
PRINTER_STATUS_OUT_OF_MEMORY, IDS_PRQSTATUS_OUT_OF_MEMORY,
PRINTER_STATUS_DOOR_OPEN, IDS_PRQSTATUS_DOOR_OPEN,
PRINTER_HACK_WORK_OFFLINE, IDS_PRQSTATUS_WORK_OFFLINE,
0, 0
} ;
#pragma data_seg()
typedef struct _CPrintersSD
{
SH32Unknown SH32Unk;
HWND hwndMain;
LPSHELLFOLDER psf;
} CPrintersSD;
STDMETHODIMP_(ULONG) CPrinters_SD_Release(IShellDetails * psd)
{
CPrintersSD * this = IToClass(CPrintersSD, SH32Unk.unk, psd);
this->SH32Unk.cRef--;
if (this->SH32Unk.cRef > 0)
{
return this->SH32Unk.cRef;
}
this->psf->lpVtbl->Release(this->psf);
LocalFree((HLOCAL)this);
return 0;
}
STDMETHODIMP CPrinters_SD_GetDetailsOf(IShellDetails * psd, LPCITEMIDLIST pidl,
UINT iColumn, LPSHELLDETAILS lpDetails)
{
CPrintersSD * this = IToClass(CPrintersSD, SH32Unk.unk, psd);
LPIDPRINTER pidp = (LPIDPRINTER)pidl;
HRESULT hres = NOERROR;
PPrintersShellFolder that = IToClass(CPrintersShellFolder, cunk.ck.unk, this->psf);
LPCTSTR pszPrinter;
#ifdef UNICODE
TCHAR szTemp[MAX_PATH];
TCHAR szPrinter[MAXNAMELENBUFFER];
#endif
if (iColumn >= PRINTERS_ICOL_MAX)
{
return(E_NOTIMPL);
}
// BUGBUG This case doesn't ever seem to get called, but is technically
// required
if (pidl && HOOD_COL_FILE == CPrintRoot_GetPIDLType(pidl))
{
LPSHELLFOLDER psf = CPrintRoot_GetPSF(NULL);
if (iColumn >= 1)
{
return E_NOTIMPL;
}
hres = psf->lpVtbl->GetDisplayNameOf(psf, pidl, SHGDN_INFOLDER, &(lpDetails->str));
return hres;
}
lpDetails->str.uType = STRRET_CSTR;
lpDetails->str.cStr[0] = '\0';
if (!pidp)
{
#ifdef UNICODE
LoadString(HINST_THISDLL, s_printers_cols[iColumn].uID,
szTemp, ARRAYSIZE(szTemp));
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, szTemp);
} else {
return E_OUTOFMEMORY;
}
#else
LoadString(HINST_THISDLL, s_printers_cols[iColumn].uID,
lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr));
#endif
lpDetails->fmt = s_printers_cols[iColumn].fmt;
lpDetails->cxChar = s_printers_cols[iColumn].cxChar;
return(NOERROR);
}
#ifdef ALIGNMENT_SCENARIO
ualstrcpyn(szPrinter, pidp->cName, ARRAYSIZE(szPrinter));
pszPrinter = szPrinter;
#else
pszPrinter = pidp->cName;
#endif
if (iColumn == PRINTERS_ICOL_NAME)
{
#ifdef UNICODE
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(pszPrinter)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, pidp->cName);
} else {
hres = E_OUTOFMEMORY;
}
#else
lstrcpyn(lpDetails->str.cStr, pidp->cName, ARRAYSIZE(lpDetails->str.cStr));
#endif
}
else if (lstrcmp(c_szNewObject, pszPrinter))
{
#ifdef PRN_FOLDERDATA
PFOLDER_PRINTER_DATA pData = Printer_FolderGetPrinter(that->hFolder,
pszPrinter);
#else
LPPRINTER_INFO_2 pData = CPrinters_SF_GetPrinterInfo2(that, pszPrinter);
#endif // ndef PRN_FOLDERDATA
if (pData)
{
switch (iColumn)
{
case PRINTERS_ICOL_QUEUESIZE:
#ifdef UNICODE
wsprintf(szTemp, TEXT("%ld"), pData->cJobs);
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, szTemp);
} else {
hres = E_OUTOFMEMORY;
}
#else
wsprintf(lpDetails->str.cStr, TEXT("%ld"), pData->cJobs);
#endif
break;
case PRINTERS_ICOL_STATUS:
{
DWORD dwStatus = pData->Status;
// HACK: Use this free bit for "Work Offline"
if (pData->Attributes & PRINTER_ATTRIBUTE_WORK_OFFLINE)
dwStatus |= PRINTER_HACK_WORK_OFFLINE;
#ifdef UNICODE
szTemp[0] = TEXT('\0');
Printer_BitsToString(dwStatus, IDS_PRQSTATUS_SEPARATOR,
ssPrinterStatus, szTemp, ARRAYSIZE(szTemp));
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, szTemp);
} else {
hres = E_OUTOFMEMORY;
}
#else
Printer_BitsToString(dwStatus, IDS_PRQSTATUS_SEPARATOR,
ssPrinterStatus, lpDetails->str.cStr, ARRAYSIZE(lpDetails->str.cStr));
#endif
break;
}
case PRINTERS_ICOL_COMMENT:
if (pData->pComment)
{
LPTSTR pStr;
// pComment can have newlines in it because it comes from
// a multi-line edit box. BUT we display it here in a
// single line edit box. Strip out the newlines
// to avoid the ugly characters.
#ifdef UNICODE
lstrcpyn(szTemp, pData->pComment, ARRAYSIZE(szTemp));
pStr = szTemp;
while (*pStr)
{
if (*pStr == TEXT('\r') || *pStr == TEXT('\n'))
*pStr = TEXT(' ');
pStr = CharNext(pStr);
}
lpDetails->str.pOleStr = (LPOLESTR)SHAlloc((lstrlen(szTemp)+1)*SIZEOF(TCHAR));
if ( lpDetails->str.pOleStr != NULL ) {
lpDetails->str.uType = STRRET_OLESTR;
lstrcpy(lpDetails->str.pOleStr, szTemp);
} else {
hres = E_OUTOFMEMORY;
}
#else
lstrcpyn(lpDetails->str.cStr, pData->pComment, ARRAYSIZE(lpDetails->str.cStr));
pStr = lpDetails->str.cStr;
while (*pStr)
{
if (*pStr == TEXT('\r') || *pStr == TEXT('\n'))
*pStr = TEXT(' ');
pStr = CharNext(pStr);
}
#endif
}
break;
}
#ifdef PRN_FOLDERDATA
LocalFree((HLOCAL)pData);
#else
CPrinters_SF_FreePrinterInfo2(that);
#endif // ndef PRN_FOLDERDATA
}
}
return(hres);
}
STDMETHODIMP CPrinters_SD_ColumnClick(IShellDetails * psd, UINT iColumn)
{
// we don't change the sorting order
return(NOERROR);
}
#pragma data_seg(DATASEG_READONLY)
IShellDetailsVtbl c_PrintersSDVtbl =
{
SH32Unknown_QueryInterface,
SH32Unknown_AddRef,
CPrinters_SD_Release,
CPrinters_SD_GetDetailsOf,
CPrinters_SD_ColumnClick,
};
#pragma data_seg()
HRESULT CPrinters_SD_Create(LPSHELLFOLDER psf, HWND hwndMain, LPVOID * ppvOut)
{
CPrintersSD *psd = (CPrintersSD *)LocalAlloc(LPTR, SIZEOF(CPrintersSD));
if (!psd)
{
*ppvOut = NULL;
return E_OUTOFMEMORY;
}
psd->SH32Unk.unk.lpVtbl = (IUnknownVtbl *)&c_PrintersSDVtbl;
psd->SH32Unk.cRef = 1;
psd->SH32Unk.riid = &IID_IShellDetails;
psd->hwndMain = hwndMain;
psd->psf = psf;
psf->lpVtbl->AddRef(psf);
*ppvOut = psd;
return NOERROR;
}
#ifdef WINNT
//---------------------------------------------------------------------------
//
// IRemoteComputer stuff
//
ULONG CPrinters_RC_Release(IUnknown * punk);
STDMETHODIMP CPrinters_RC_Initialize(LPREMOTECOMPUTER prc, LPTSTR pszMachine, BOOL bEnumerating);
HRESULT Printers_CreateRC(IUnknown *punkOuter, LPCOMMINFO lpcinfo,
REFIID riid, IUnknown **punkAgg);
typedef struct _PrintersRemoteComputer
{
WCommonUnknown cunk;
LPCOMMINFO lpcinfo;
} CPrintersRemoteComputer, *PPrintersRemoteComputer;
#pragma data_seg(".text", "CODE")
IUnknownVtbl s_PrintersAggRCVtbl =
{
WCommonUnknown_QueryInterface,
WCommonUnknown_AddRef,
CPrinters_RC_Release,
};
IRemoteComputerVtbl s_PrintersRCVtbl =
{
WCommonKnown_QueryInterface,
WCommonKnown_AddRef,
WCommonKnown_Release,
CPrinters_RC_Initialize,
};
#pragma data_seg()
ULONG CPrinters_RC_Release(IUnknown * punk)
{
PPrintersRemoteComputer this = IToClass(CPrintersRemoteComputer, cunk.unk, punk);
this->cunk.cRef--;
if (this->cunk.cRef > 0)
{
return(this->cunk.cRef);
}
LocalFree((HLOCAL)this);
return(0);
}
STDMETHODIMP CPrinters_RC_Initialize(LPREMOTECOMPUTER prc, LPTSTR pszMachine, BOOL bEnumerating)
{
PPrintersRemoteComputer this = IToClass(CPrintersRemoteComputer, cunk.ck.unk, prc);
// Get the object-wide data
SPrintersObj* that = (SPrintersObj*)this->lpcinfo->lpData;
DWORD cch;
LPTSTR psz;
if (NULL == pszMachine)
{
return E_INVALIDARG;
}
//
// For NT servers, we want to show the remote printer folder. Only check
// during enumeration
//
if (bEnumerating)
{
if (!Printer_CheckShowFolder(pszMachine))
{
return E_FAIL;
}
}
cch = lstrlen(pszMachine);
psz = (LPTSTR)LocalAlloc(LPTR, (cch + 1) * sizeof(TCHAR));
if (!psz)
{
return E_OUTOFMEMORY;
}
lstrcpy(psz, pszMachine);
that->pszMachineName = psz;
return S_OK;
}
HRESULT Printers_CreateRC(IUnknown *punkOuter, LPCOMMINFO lpcinfo,
REFIID riid, IUnknown **punkAgg)
{
HRESULT hres;
hres = WU_CreateInterface(SIZEOF(CPrintersRemoteComputer), &IID_IRemoteComputer,
&s_PrintersAggRCVtbl, &s_PrintersRCVtbl, punkOuter, riid, punkAgg);
if (SUCCEEDED(hres))
{
PPrintersRemoteComputer this = IToClass(CPrintersRemoteComputer, cunk.unk, *punkAgg);
this->lpcinfo = lpcinfo;
}
return hres;
}
//---------------------------------------------------------------------------
// And a way to delete it
VOID Printers_Destroy(LPVOID lpData)
{
COMMINFO* pcinfo = (COMMINFO*)lpData;
SPrintersObj* ppo = (SPrintersObj*)pcinfo->lpData;
if (NULL != ppo)
{
if (NULL != ppo->pszMachineName)
{
LocalFree(ppo->pszMachineName);
}
LocalFree(ppo);
}
}
#endif // def WINNT
//---------------------------------------------------------------------------
//
// The IClassFactory callback for CLSID_CPrinters
//
HRESULT CALLBACK CPrinters_CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, LPVOID * ppvObject)
{
// The explorer gets an IShellFolder through an IPersistFolder
#pragma data_seg(".text", "CODE")
static const COMMOBJ_OBJDESC sPrintersDesc[] =
{
&IID_IPersistFolder, WU_CreatePF,
&IID_IShellFolder, Printers_CreateSF,
#ifdef WINNT
&IID_IRemoteComputer, Printers_CreateRC
#endif
};
#pragma data_seg()
COMMINFO cinfo = {NULL, NULL, NULL, &CLSID_CPrinters, NULL, NULL};
#ifdef WINNT
SPrintersObj *ppo = (SPrintersObj *)LocalAlloc(LPTR, SIZEOF(SPrintersObj));
if (!ppo)
{
*ppvObject = NULL;
return E_OUTOFMEMORY;
}
ppo->pszMachineName = NULL;
cinfo.lpData = (LPVOID)ppo;
#endif
// REVIEW: do we even need to check this? Most other _CreateInstance
// implementations simply Assert(!pUnkOuter).
if (pUnkOuter)
{
Assert(FALSE);
return E_NOTIMPL;
}
return(Common_CreateObject(&cinfo,
#ifdef WINNT
Printers_Destroy,
#else
WU_DecRef,
#endif
sPrintersDesc,
ARRAYSIZE(sPrintersDesc),
riid,
ppvObject));
}
#ifdef WINNT
// Now for the Load and Unload Functions
BOOL NetApi32DLL_Init()
{
HINSTANCE hmodNetApi32;
//
// First, check the global without entering the critical section.
//
if (s_hmodNetApi32 != NULL)
{
return(TRUE);
}
hmodNetApi32 = LoadLibrary(TEXT("NetApi32.dll"));
if ((UINT)hmodNetApi32 <= HINSTANCE_ERROR)
{
return(FALSE);
}
// Now get all of the procedure addresses we need
g_pfnNetServerGetInfo = (PFNNETSERVERGETINFO)GetProcAddress(hmodNetApi32, "NetServerGetInfo");
g_pfnNetApiBufferFree = (PFNNETAPIBUFFERFREE)GetProcAddress(hmodNetApi32, "NetApiBufferFree");
if (!g_pfnNetServerGetInfo || !g_pfnNetApiBufferFree)
{
Assert(FALSE);
FreeLibrary(hmodNetApi32);
return(FALSE);
}
ENTERCRITICAL;
if (!s_hmodNetApi32)
{
s_hmodNetApi32 = hmodNetApi32;
hmodNetApi32 = NULL;
}
LEAVECRITICAL;
if (hmodNetApi32)
{
FreeLibrary(hmodNetApi32);
}
return TRUE;
}
void NetApi32DLL_Term()
{
// If we loaded it for this app, we should now free it
if (ISVALIDHINSTANCE(s_hmodNetApi32))
{
FreeLibrary(s_hmodNetApi32);
s_hmodNetApi32 = NULL;
// We could also set all of the vars to NULL, but not needed
// as we always call through our wrappers
}
}
BOOL
Printer_CheckShowFolder(LPCTSTR pszMachine)
{
BOOL bShowIt = FALSE;
ASSERTNONCRITICAL
if (NetApi32DLL_Init())
{
PSERVER_INFO_101 pServerInfo = NULL;
NET_API_STATUS status;
LPCWSTR pszUnicodeMachine;
#ifdef UNICODE
pszUnicodeMachine = pszMachine;
#else // UNICODE
WCHAR szUnicodeBuf[MAX_PATH];
MultiByteToWideChar(CP_ACP, 0, pszMachine, -1, szUnicodeBuf, ARRAYSIZE(szUnicodeBuf));
pszUnicodeMachine = szUnicodeBuf;
#endif // UNICODE
status = (*g_pfnNetServerGetInfo)((LPWSTR)pszUnicodeMachine,
101,
(PBYTE*)&pServerInfo);
if (status == NERR_Success)
{
//
// If it's any flavor of NT, show the print folder.
//
if (pServerInfo->sv101_type & SV_TYPE_NT)
{
bShowIt = TRUE;
}
(*g_pfnNetApiBufferFree)( pServerInfo );
}
}
return bShowIt;
}
#endif // def WINNT