windows-nt/Source/XPSP1/NT/printscan/lib/psutil/tmplutil.cpp
2020-09-26 16:20:57 +08:00

1361 lines
38 KiB
C++

/*****************************************************************************
*
* (C) COPYRIGHT MICROSOFT CORPORATION, 2000
*
* TITLE: tmplutil.cpp
*
* VERSION: 1.0
*
* AUTHOR: LazarI
*
* DATE: 10-Mar-2000
*
* DESCRIPTION: Smart pointers, utility templates, etc...
*
*****************************************************************************/
#include "precomp.h"
#pragma hdrstop
/*****************************************************************************
IMPORTANT!
all those headers should be included in the pch file before including tmplutil.h
(in the same order!) in order to be able to compile this file.
// some common headers
#include <shlobj.h> // shell OM interfaces
#include <shlwapi.h> // shell common API
#include <winspool.h> // spooler
#include <assert.h> // assert
#include <commctrl.h> // common controls
#include <lm.h> // Lan manager (netapi32.dll)
#include <wininet.h> // inet core - necessary for INTERNET_MAX_HOST_NAME_LENGTH
// some private shell headers
#include <shlwapip.h> // private shell common API
#include <shpriv.h> // private shell interfaces
#include <comctrlp.h> // private common controls
*****************************************************************************/
#include "tmplutil.h"
#define gszBackwardSlash TEXT('\\')
#define gszLeadingSlashes TEXT("\\\\")
/*****************************************************************************
COMObjects_GetCount
*****************************************************************************/
static LONG g_lCOMObjectsCount = 0;
LONG COMObjects_GetCount()
{
return g_lCOMObjectsCount;
}
HRESULT PinCurrentDLL()
{
HRESULT hr = S_OK;
HINSTANCE hModuleSelf = NULL;
// Let's get the handle of the current module -
// the one this function belongs to.
MEMORY_BASIC_INFORMATION mbi;
if (VirtualQuery(reinterpret_cast<LPCVOID>(PinCurrentDLL), &mbi, sizeof(mbi)))
{
hModuleSelf = reinterpret_cast<HINSTANCE>(mbi.AllocationBase);
}
else
{
// VirtualQuery failed.
hr = CreateHRFromWin32();
}
if (SUCCEEDED(hr))
{
// Get the module name and call LoadLibrary on it.
TCHAR szModuleName[MAX_PATH];
if (GetModuleFileName(hModuleSelf, szModuleName, ARRAYSIZE(szModuleName)))
{
if (NULL == LoadLibrary(szModuleName))
{
// LoadLibrary failed.
hr = CreateHRFromWin32();
}
}
else
{
// GetModuleFileName failed.
hr = CreateHRFromWin32();
}
}
return hr;
}
/*****************************************************************************
PrinterSplitFullName
Routine Description:
splits a fully qualified printer connection name into server and printer name parts.
Arguments:
pszFullName - full qualifier printer name ('printer' or '\\server\printer')
pszBuffer - scratch buffer used to store output strings.
nMaxLength - the size of the scratch buffer in characters
ppszServer - receives pointer to the server string. If it is a
local printer, empty string is returned.
ppszPrinter - receives a pointer to the printer string. OPTIONAL
Return Value:
returns S_OK on sucess or COM error otherwise
*****************************************************************************/
HRESULT PrinterSplitFullName(LPCTSTR pszFullName, TCHAR szBuffer[], int nMaxLength, LPCTSTR *ppszServer,LPCTSTR *ppszPrinter)
{
HRESULT hr = S_OK;
lstrcpyn(szBuffer, pszFullName, nMaxLength);
LPTSTR pszPrinter = szBuffer;
if (pszFullName[0] != TEXT('\\') || pszFullName[1] != TEXT('\\'))
{
pszPrinter = szBuffer;
*ppszServer = TEXT("");
}
else
{
*ppszServer = szBuffer;
pszPrinter = _tcschr(*ppszServer + 2, TEXT('\\'));
if (NULL == 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 = szBuffer;
*ppszServer = TEXT("");
}
else
{
//
// We found the third backslash; null terminate our
// copy and set bRemote TRUE to format the string.
//
*pszPrinter++ = 0;
}
}
if (ppszPrinter)
{
*ppszPrinter = pszPrinter;
}
return hr;
}
////////////////////////////////////////////////
//
// class COleComInitializer
//
// smart OLE2, COM initializer - just declare
// an instance wherever need to use COM, OLE2
//
COleComInitializer::COleComInitializer(BOOL bOleInit)
: m_hr(E_FAIL),
m_bOleInit(bOleInit)
{
if( m_bOleInit )
{
m_hr = OleInitialize(NULL);
}
else
{
m_hr = CoInitialize(NULL);
}
}
COleComInitializer::~COleComInitializer()
{
if( SUCCEEDED(m_hr) )
{
if( m_bOleInit )
{
OleUninitialize();
}
else
{
CoUninitialize();
}
}
}
COleComInitializer::operator BOOL () const
{
if( FAILED(m_hr) )
{
return (RPC_E_CHANGED_MODE == m_hr);
}
else
{
return TRUE;
}
}
////////////////////////////////////////////////
//
// class CDllLoader
//
// smart DLL loader - calls LoadLibrary
// FreeLibrary for you.
//
CDllLoader::CDllLoader(LPCTSTR pszDllName)
: m_hLib(NULL)
{
m_hLib = LoadLibrary(pszDllName);
}
CDllLoader::~CDllLoader()
{
if( m_hLib )
{
FreeLibrary( m_hLib );
m_hLib = NULL;
}
}
CDllLoader::operator BOOL () const
{
return (NULL != m_hLib);
}
FARPROC CDllLoader::GetProcAddress( LPCSTR lpProcName )
{
if( m_hLib )
{
return ::GetProcAddress( m_hLib, lpProcName );
}
return NULL;
}
FARPROC CDllLoader::GetProcAddress( WORD wProcOrd )
{
if( m_hLib )
{
return ::GetProcAddress( m_hLib, (LPCSTR)MAKEINTRESOURCE(wProcOrd) );
}
return NULL;
}
////////////////////////////////////////////////
// class CCookiesHolder
//
// this a utility class which allows us to pass more
// than one pointer through a single cookie.
//
CCookiesHolder::CCookiesHolder()
: m_pCookies(NULL),
m_uCount(0)
{
}
CCookiesHolder::CCookiesHolder(UINT uCount)
: m_pCookies(NULL),
m_uCount(0)
{
SetCount(uCount);
}
CCookiesHolder::~CCookiesHolder()
{
SetCount(0);
}
BOOL CCookiesHolder::SetCount(UINT uCount)
{
BOOL bReturn = FALSE;
if( uCount )
{
// reset first
SetCount(0);
// attempt to allocate memory for the cookies
LPVOID *pCookies = new LPVOID[uCount];
if( pCookies )
{
m_uCount = uCount;
m_pCookies = pCookies;
bReturn = TRUE;
}
}
else
{
// zero means - reset
if( m_pCookies )
{
delete[] m_pCookies;
m_pCookies = NULL;
m_uCount = 0;
}
bReturn = TRUE;
}
return bReturn;
}
////////////////////////////////////////////////
// class CPrintersAutoCompleteSource
//
// printer's autocomplete source impl.
//
QITABLE_DECLARE(CPrintersAutoCompleteSource)
class CPrintersAutoCompleteSource: public CUnknownMT<QITABLE_GET(CPrintersAutoCompleteSource)>, // MT impl. of IUnknown
public IEnumString, // string enumerator
public IACList // autocomplete list generator
{
public:
CPrintersAutoCompleteSource();
~CPrintersAutoCompleteSource();
//////////////////
// IUnknown
//
IMPLEMENT_IUNKNOWN()
//////////////////
// IEnumString
//
STDMETHODIMP Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched);
STDMETHODIMP Skip(ULONG celt);
STDMETHODIMP Reset(void);
STDMETHODIMP Clone(IEnumString **ppenum) { return E_NOTIMPL; }
//////////////////
// IACList
//
STDMETHODIMP Expand(LPCOLESTR pszExpand);
private:
CAutoPtrArray<BYTE> m_spBufferPrinters;
CAutoPtrArray<BYTE> m_spBufferShares;
PRINTER_INFO_5 *m_pPI5;
SHARE_INFO_1 *m_pSI1;
ULONG m_ulCount;
ULONG m_ulPos;
TCHAR m_szServer[PRINTER_MAX_PATH];
CRefPtrCOM<IEnumString> m_spCustomMRUEnum;
BOOL _IsServerName(LPCTSTR psz, BOOL *pbPartial);
static BOOL _IsMasqPrinter(const PRINTER_INFO_5 &pi5);
static HRESULT _CreateCustomMRU(REFIID riid, void **ppv);
static HRESULT _AddCustomMRU(LPCTSTR psz);
};
// QueryInterface table
QITABLE_BEGIN(CPrintersAutoCompleteSource)
QITABENT(CPrintersAutoCompleteSource, IEnumString), // IID_IEnumString
QITABENT(CPrintersAutoCompleteSource, IACList), // IID_IACList
QITABLE_END()
#define SZ_REGKEY_PRNCONNECTMRU L"Printers\\Settings\\Wizard\\ConnectMRU"
// comctrlp.h defines this as AddMRUStringW preventing us from using the IACLCustomMRU interface
#undef AddMRUString
HRESULT CPrintersACS_CreateInstance(IUnknown **ppUnk)
{
HRESULT hr = E_INVALIDARG;
if( ppUnk )
{
hr = PinCurrentDLL();
if( SUCCEEDED(hr) )
{
CPrintersAutoCompleteSource *pObj = new CPrintersAutoCompleteSource();
hr = pObj ? S_OK : E_OUTOFMEMORY;
if( SUCCEEDED(hr) )
{
hr = pObj->QueryInterface(IID_IUnknown, (void**)ppUnk);
pObj->Release();
}
}
}
return hr;
}
CPrintersAutoCompleteSource::CPrintersAutoCompleteSource():
m_pPI5(NULL),
m_ulCount(0),
m_ulPos(0)
{
InterlockedIncrement(&g_lCOMObjectsCount);
}
CPrintersAutoCompleteSource::~CPrintersAutoCompleteSource()
{
InterlockedDecrement(&g_lCOMObjectsCount);
}
//////////////////
// IUnknown
//
STDMETHODIMP CPrintersAutoCompleteSource::Next(ULONG celt, LPOLESTR *rgelt, ULONG *pceltFetched)
{
HRESULT hr = S_FALSE;
if( pceltFetched )
{
*pceltFetched = 0;
}
if( m_ulCount && (m_pPI5 || m_pSI1) )
{
ULONG cFetched = 0;
if( m_pPI5 )
{
// printers enumerated
for( ; m_ulPos < m_ulCount && cFetched < celt; m_ulPos++ )
{
// if this is a valid (non-masq) printer just return it
if( m_pPI5[m_ulPos].pPrinterName[0] && SUCCEEDED(SHStrDup(m_pPI5[m_ulPos].pPrinterName, &rgelt[cFetched])) )
{
cFetched++;
}
}
}
else
{
// shares enumerated
TCHAR szBuffer[PRINTER_MAX_PATH];
for( ; m_ulPos < m_ulCount && cFetched < celt; m_ulPos++ )
{
// if this is a valid printer share name, just return it
if( m_pSI1[m_ulPos].shi1_netname[0] &&
-1 != wnsprintf(szBuffer, ARRAYSIZE(szBuffer), TEXT("%s\\%s"),
m_szServer, m_pSI1[m_ulPos].shi1_netname) &&
SUCCEEDED(SHStrDup(szBuffer, &rgelt[cFetched])) )
{
cFetched++;
}
}
}
if( pceltFetched )
{
*pceltFetched = cFetched;
}
hr = cFetched == celt ? S_OK : S_FALSE;
}
else
{
// use our custom MRU if any...
if( m_spCustomMRUEnum )
{
hr = m_spCustomMRUEnum->Next(celt, rgelt, pceltFetched);
}
}
return hr;
}
STDMETHODIMP CPrintersAutoCompleteSource::Skip(ULONG celt)
{
HRESULT hr = S_FALSE;
if( m_ulCount && (m_pPI5 || m_pSI1) )
{
hr = ((m_ulPos + celt) <= m_ulCount) ? S_OK : S_FALSE;
m_ulPos = min(m_ulPos + celt, m_ulCount);
}
else
{
// use our custom MRU if any...
if( m_spCustomMRUEnum )
{
hr = m_spCustomMRUEnum->Skip(celt);
}
}
return hr;
}
STDMETHODIMP CPrintersAutoCompleteSource::Reset(void)
{
HRESULT hr = S_OK;
if( m_ulCount && (m_pPI5 || m_pSI1) )
{
m_ulPos = 0;
}
else
{
// use our custom MRU if any...
if( m_spCustomMRUEnum )
{
hr = m_spCustomMRUEnum->Reset();
}
}
return hr;
}
typedef bool PI5_less_type(const PRINTER_INFO_5 &i1, const PRINTER_INFO_5 &i2);
static bool PI5_less(const PRINTER_INFO_5 &i1, const PRINTER_INFO_5 &i2)
{
return (lstrcmp(i1.pPrinterName, i2.pPrinterName) < 0);
}
typedef bool SI1_less_type(const SHARE_INFO_1 &i1, const SHARE_INFO_1 &i2);
static bool SI1_less(const SHARE_INFO_1 &i1, const SHARE_INFO_1 &i2)
{
return (lstrcmp(i1.shi1_netname, i2.shi1_netname) < 0);
}
//////////////////
// IACList
//
STDMETHODIMP CPrintersAutoCompleteSource::Expand(LPCOLESTR pszExpand)
{
HRESULT hr = E_FAIL;
DWORD cReturned = 0;
BOOL bPartial = FALSE;
// assume this is not a server name, so reset the list first
m_pPI5 = NULL;
m_pSI1 = NULL;
m_spBufferPrinters = NULL;
m_spBufferShares = NULL;
m_ulCount = m_ulPos = 0;
m_szServer[0] = 0;
m_spCustomMRUEnum = NULL;
if( _IsServerName(pszExpand, &bPartial) )
{
// make a copy of the print buffer & cut off the last slash
TCHAR szBuffer[PRINTER_MAX_PATH];
lstrcpyn(szBuffer, pszExpand, ARRAYSIZE(szBuffer));
szBuffer[lstrlen(szBuffer)-1] = 0;
// enum the printers on that server
if( SUCCEEDED(hr = ShellServices::EnumPrintersWrap(PRINTER_ENUM_NAME, 5, szBuffer, &m_spBufferPrinters, &cReturned)) && cReturned )
{
m_ulPos = 0;
m_ulCount = cReturned;
m_pPI5 = m_spBufferPrinters.GetPtrAs<PRINTER_INFO_5*>();
lstrcpyn(m_szServer, szBuffer, ARRAYSIZE(m_szServer));
// successful expand - remember the MRU string
_AddCustomMRU(szBuffer);
// traverse to check for masq printers
for( ULONG ulPos = 0; ulPos < m_ulCount; ulPos++ )
{
if( _IsMasqPrinter(m_pPI5[ulPos]) )
{
// we don't really care for masq printer's since they are
// an obsolete concept and can't be truly shared/connected to
m_pPI5[ulPos].pPrinterName = TEXT("");
}
}
// invoke STL to sort
std::sort<PRINTER_INFO_5*, PI5_less_type*>(m_pPI5, m_pPI5 + m_ulCount, PI5_less);
}
else
{
// enumeration of the printers failed, this could be because the remote spooler is down
// or it is a downlevel print provider (win9x, novell, linux, sun...) in this case we
// would like to try enumerating the shares as possible connection points.
if( SUCCEEDED(hr = ShellServices::NetAPI_EnumShares(szBuffer, 1, &m_spBufferShares, &cReturned)) && cReturned )
{
m_ulPos = 0;
m_ulCount = cReturned;
m_pSI1 = m_spBufferShares.GetPtrAs<SHARE_INFO_1*>();
lstrcpyn(m_szServer, szBuffer, ARRAYSIZE(m_szServer));
// successful expand - remember the MRU string
_AddCustomMRU(szBuffer);
// traverse to remove the non-printer shares
for( ULONG ulPos = 0; ulPos < m_ulCount; ulPos++ )
{
if( STYPE_PRINTQ != m_pSI1[ulPos].shi1_type )
{
// this is a non-printer share, remove
m_pSI1[ulPos].shi1_netname[0] = 0;
}
}
// invoke STL to sort
std::sort<SHARE_INFO_1*, SI1_less_type*>(m_pSI1, m_pSI1 + m_ulCount, SI1_less);
}
}
}
else
{
if( bPartial )
{
// use our custom MRU for autocomplete
hr = _CreateCustomMRU(IID_IEnumString, m_spCustomMRUEnum.GetPPV());
}
}
return hr;
}
BOOL CPrintersAutoCompleteSource::_IsServerName(LPCTSTR psz, BOOL *pbPartial)
{
ASSERT(pbPartial);
BOOL bRet = FALSE;
int i, iSepCount = 0, iLen = lstrlen(psz);
for( i=0; i<iLen; i++ )
{
if( psz[i] == gszBackwardSlash )
{
iSepCount++;
}
}
if( (1 == iSepCount && psz[0] == gszBackwardSlash) ||
(2 == iSepCount && psz[0] == gszBackwardSlash && psz[1] == gszBackwardSlash) )
{
*pbPartial = TRUE;
}
if( 3 < iLen &&
3 == iSepCount &&
psz[0] == gszBackwardSlash &&
psz[1] == gszBackwardSlash &&
psz[iLen-1] == gszBackwardSlash )
{
bRet = TRUE;
}
return bRet;
}
BOOL CPrintersAutoCompleteSource::_IsMasqPrinter(const PRINTER_INFO_5 &pi5)
{
// this is a little bit hacky, but there is no other way to tell the masq printer
// in the remote case. the spooler APIs suffer from some severe design flaws and
// we have to put up with that.
LPCTSTR pszServer;
LPCTSTR pszPrinter;
TCHAR szScratch[PRINTER_MAX_PATH];
// split the full printer name into its components.
if( SUCCEEDED(PrinterSplitFullName(pi5.pPrinterName,
szScratch, ARRAYSIZE(szScratch), &pszServer, &pszPrinter)) )
{
return (0 == _tcsnicmp(pszPrinter, gszLeadingSlashes, _tcslen(gszLeadingSlashes)));
}
else
{
return FALSE;
}
}
HRESULT CPrintersAutoCompleteSource::_CreateCustomMRU(REFIID riid, void **ppv)
{
HRESULT hr = E_INVALIDARG;
CRefPtrCOM<IACLCustomMRU> spCustomMRU;
if( ppv &&
SUCCEEDED(hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL,
CLSCTX_INPROC_SERVER, IID_IACLCustomMRU, spCustomMRU.GetPPV())) &&
SUCCEEDED(hr = spCustomMRU->Initialize(SZ_REGKEY_PRNCONNECTMRU, 26)) )
{
// query the specified interface
hr = spCustomMRU->QueryInterface(riid, ppv);
}
return hr;
}
HRESULT CPrintersAutoCompleteSource::_AddCustomMRU(LPCTSTR psz)
{
HRESULT hr = E_INVALIDARG;
CRefPtrCOM<IACLCustomMRU> spCustomMRU;
if( psz &&
SUCCEEDED(hr = _CreateCustomMRU(IID_IACLCustomMRU, spCustomMRU.GetPPV())) )
{
// just remember the MRU string
hr = spCustomMRU->AddMRUString(psz);
}
return hr;
}
////////////////////////////////////////////////
// shell related services
namespace ShellServices
{
// creates a PIDL to a printer in the local printers folder by using ParseDisplayName
// see the description of CreatePrinterPIDL below.
HRESULT CreatePrinterPIDL_Parse(HWND hwnd, LPCTSTR pszPrinterName, IShellFolder **ppLocalPrnFolder, LPITEMIDLIST *ppidlPrinter)
{
HRESULT hr = E_UNEXPECTED;
CRefPtrCOM<IShellFolder> spDesktopFolder;
CRefPtrCOM<IShellFolder> spPrnFolder;
CAutoPtrPIDL pidlPrinters;
CAutoPtrPIDL pidlPrinter;
// attempt to get the fully qualified name (for parsing) of the printers folder
if( SUCCEEDED(hr = SHGetDesktopFolder(&spDesktopFolder)) &&
SUCCEEDED(hr = SHGetSpecialFolderLocation(NULL, CSIDL_PRINTERS, &pidlPrinters)) &&
SUCCEEDED(hr = spDesktopFolder->BindToObject(pidlPrinters, 0, IID_IShellFolder, spPrnFolder.GetPPV())) )
{
ULONG uEaten = 0;
ULONG uAttributes = SFGAO_DROPTARGET;
// attempt parse the printer name into PIDL
hr = spPrnFolder->ParseDisplayName(hwnd, 0, (LPOLESTR )pszPrinterName,
&uEaten, &pidlPrinter, &uAttributes);
if( SUCCEEDED(hr) )
{
if( ppLocalPrnFolder )
{
// return the local printers folder
*ppLocalPrnFolder = spPrnFolder.Detach();
}
if( ppidlPrinter )
{
// return the printer PIDL
*ppidlPrinter = pidlPrinter.Detach();
}
}
}
return hr;
}
// creates a PIDL to a printer in the local printers folder by enumerating the printers
// see the description of CreatePrinterPIDL below.
HRESULT CreatePrinterPIDL_Enum(HWND hwnd, LPCTSTR pszPrinterName, IShellFolder **ppLocalPrnFolder, LPITEMIDLIST *ppidlPrinter)
{
HRESULT hr = E_UNEXPECTED;
CRefPtrCOM<IShellFolder> spDesktopFolder;
CRefPtrCOM<IShellFolder> spPrnFolder;
CRefPtrCOM<IEnumIDList> spPrnEnum;
CAutoPtrPIDL pidlPrinters;
STRRET str = {0};
// attempt to get the fully qualified name (for parsing) of the printers folder
if( SUCCEEDED(hr = SHGetDesktopFolder(&spDesktopFolder)) &&
SUCCEEDED(hr = SHGetSpecialFolderLocation(NULL, CSIDL_PRINTERS, &pidlPrinters)) &&
SUCCEEDED(hr = spDesktopFolder->BindToObject(pidlPrinters, 0, IID_IShellFolder, spPrnFolder.GetPPV())) &&
SUCCEEDED(hr = spPrnFolder->EnumObjects(hwnd, SHCONTF_NONFOLDERS, &spPrnEnum)) )
{
TCHAR szBuffer[PRINTER_MAX_PATH];
CAutoPtrPIDL pidlPrinter;
ULONG uFetched = 0;
for( ;; )
{
// get next printer
hr = spPrnEnum->Next(1, &pidlPrinter, &uFetched);
if( S_OK != hr )
{
// no more printers, or error
break;
}
if( SUCCEEDED(hr = spPrnFolder->GetDisplayNameOf(pidlPrinter, SHGDN_FORPARSING, &str)) &&
SUCCEEDED(hr = StrRetToBuf(&str, pidlPrinter, szBuffer, COUNTOF(szBuffer))) &&
!lstrcmp(szBuffer, pszPrinterName) )
{
// found!
if( ppLocalPrnFolder )
{
// return the local printers folder
*ppLocalPrnFolder = spPrnFolder.Detach();
}
if( ppidlPrinter )
{
// return the printer PIDL
*ppidlPrinter = pidlPrinter.Detach();
}
break;
}
// release the PIDL
pidlPrinter = NULL;
}
if( hr == S_FALSE )
{
// printer name not found. setup the correct HRESULT.
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PRINTER_NAME);
}
}
return hr;
}
// creates a PIDL to a printer in the local printers folder.
// args:
// [in] hwnd - window handle (in case we need to show UI - message box)
// [in] pszPrinterName - full printer name.
// [out] ppLocalPrnFolder - the printers folder (optional - may be NULL)
// [out] ppidlPrinter - the PIDL of the printer pointed by pszPrinterName (optional - may be NULL)
//
// remarks:
// pszPrinterName should be fully qualified printer name, i.e. if printer connection it should be
// like "\\server\printer", if local printer just the printer name.
//
// returns:
// S_OK on success, or OLE2 error otherwise
HRESULT CreatePrinterPIDL(HWND hwnd, LPCTSTR pszPrinterName, IShellFolder **ppLocalPrnFolder, LPITEMIDLIST *ppidlPrinter)
{
// attempt to obtain the printer PIDL by parsing first - it's much quicker.
HRESULT hr = CreatePrinterPIDL_Parse(hwnd, pszPrinterName, ppLocalPrnFolder, ppidlPrinter);
if( E_NOTIMPL == hr )
{
// if parsing is not implemented then go ahead and enum the printers - slower.
hr = CreatePrinterPIDL_Enum(hwnd, pszPrinterName, ppLocalPrnFolder, ppidlPrinter);
}
return hr;
}
// loads a popup menu
HMENU LoadPopupMenu(HINSTANCE hInstance, UINT id, UINT uSubOffset)
{
HMENU hMenuPopup = NULL;
CAutoHandleMenu shMenuParent = LoadMenu(hInstance, MAKEINTRESOURCE(id));
if( shMenuParent && (hMenuPopup = GetSubMenu(shMenuParent, uSubOffset)) )
{
// tear off our submenu before destroying the parent
RemoveMenu(shMenuParent, uSubOffset, MF_BYPOSITION);
}
return hMenuPopup;
}
// initializes enum printer's autocomplete
HRESULT InitPrintersAutoComplete(HWND hwndEdit)
{
HRESULT hr = E_INVALIDARG;
if( hwndEdit )
{
// create an autocomplete object
CRefPtrCOM<IAutoComplete> spAC; // auto complete interface
CRefPtrCOM<IAutoComplete2> spAC2; // auto complete 2 interface
CRefPtrCOM<IUnknown> spACS; // auto complete source (IEnumString & IACList)
// initialize all the objects & hook them up
if( SUCCEEDED(hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER,
IID_IAutoComplete, (void**)&spAC)) &&
SUCCEEDED(hr = CPrintersACS_CreateInstance(&spACS)) &&
SUCCEEDED(hr = spAC->Init(hwndEdit, spACS, NULL, NULL)) &&
SUCCEEDED(hr = spAC->QueryInterface(IID_IAutoComplete2, (void **)&spAC2)) &&
SUCCEEDED(hr = spAC2->SetOptions(ACO_AUTOSUGGEST)) )
{
hr = S_OK;
}
}
return hr;
}
// helpers for the Enum* idioms
HRESULT EnumPrintersWrap(DWORD dwFlags, DWORD dwLevel, LPCTSTR pszName, BYTE **ppBuffer, DWORD *pcReturned)
{
HRESULT hr = E_INVALIDARG;
if( ppBuffer && pcReturned )
{
int iTry = -1;
DWORD cbNeeded = 0;
DWORD cReturned = 0;
CAutoPtrArray<BYTE> pData;
BOOL bStatus = FALSE;
for( ;; )
{
if( iTry++ >= ENUM_MAX_RETRY )
{
// max retry count reached. this is also
// considered out of memory case
pData = NULL;
break;
}
// call EnumPrinters...
bStatus = EnumPrinters(dwFlags, const_cast<LPTSTR>(pszName), dwLevel,
pData, cbNeeded, &cbNeeded, &cReturned);
if( !bStatus && ERROR_INSUFFICIENT_BUFFER == GetLastError() && cbNeeded )
{
// buffer too small case
pData = new BYTE[cbNeeded];
continue;
}
break;
}
// setup the error code properly
hr = bStatus ? S_OK : GetLastError() != ERROR_SUCCESS ? HRESULT_FROM_WIN32(GetLastError()) :
!pData ? E_OUTOFMEMORY : E_FAIL;
// setup the out parameters
if( SUCCEEDED(hr) )
{
*ppBuffer = pData.Detach();
*pcReturned = cReturned;
}
else
{
*ppBuffer = NULL;
*pcReturned = 0;
}
}
return hr;
}
// helpers for the GetJob API - see the SDK for mor information.
HRESULT GetJobWrap(HANDLE hPrinter, DWORD JobId, DWORD dwLevel, BYTE **ppBuffer, DWORD *pcReturned)
{
HRESULT hr = E_INVALIDARG;
if( ppBuffer && pcReturned )
{
int iTry = -1;
DWORD cbNeeded = 0;
CAutoPtrArray<BYTE> pData;
BOOL bStatus = FALSE;
for( ;; )
{
if( iTry++ >= ENUM_MAX_RETRY )
{
// max retry count reached. this is also
// considered out of memory case
pData = NULL;
break;
}
// call GetJob...
bStatus = GetJob(hPrinter, JobId, dwLevel, pData, cbNeeded, &cbNeeded);
if( !bStatus && ERROR_INSUFFICIENT_BUFFER == GetLastError() && cbNeeded )
{
// buffer too small case
pData = new BYTE[cbNeeded];
continue;
}
break;
}
// setup the error code properly
hr = bStatus ? S_OK : GetLastError() != ERROR_SUCCESS ? HRESULT_FROM_WIN32(GetLastError()) :
!pData ? E_OUTOFMEMORY : E_FAIL;
// setup the out parameters
if( SUCCEEDED(hr) )
{
*ppBuffer = pData.Detach();
*pcReturned = cbNeeded;
}
else
{
*ppBuffer = NULL;
*pcReturned = 0;
}
}
return hr;
}
typedef NET_API_STATUS
type_NetAPI_NetShareEnum(
LPWSTR servername,
DWORD level,
LPBYTE *bufptr,
DWORD prefmaxlen,
LPDWORD entriesread,
LPDWORD totalentries,
LPDWORD resume_handle
);
typedef NET_API_STATUS
type_NetAPI_NetApiBufferFree(
LPVOID Buffer
);
typedef NET_API_STATUS
type_NetAPI_NetApiBufferSize(
LPVOID Buffer,
LPDWORD ByteCount
);
// enumerates the shared resources on a server, for more info see SDK for NetShareEnum API.
HRESULT NetAPI_EnumShares(LPCTSTR pszServer, DWORD dwLevel, BYTE **ppBuffer, DWORD *pcReturned)
{
HRESULT hr = E_INVALIDARG;
if( ppBuffer && pcReturned )
{
hr = E_FAIL;
*pcReturned = 0;
*ppBuffer = NULL;
LPBYTE pNetBuf = NULL;
DWORD dwRead, dwTemp;
CDllLoader dll(TEXT("netapi32.dll"));
if( dll )
{
// netapi32.dll loaded here...
type_NetAPI_NetShareEnum *pfnNetShareEnum = (type_NetAPI_NetShareEnum *)dll.GetProcAddress("NetShareEnum");
type_NetAPI_NetApiBufferSize *pfnNetApiBufferSize = (type_NetAPI_NetApiBufferSize *)dll.GetProcAddress("NetApiBufferSize");
type_NetAPI_NetApiBufferFree *pfnNetApiBufferFree = (type_NetAPI_NetApiBufferFree *)dll.GetProcAddress("NetApiBufferFree");
if( pfnNetShareEnum && pfnNetApiBufferSize && pfnNetApiBufferFree &&
NERR_Success == pfnNetShareEnum(const_cast<LPTSTR>(pszServer), dwLevel,
&pNetBuf, MAX_PREFERRED_LENGTH, &dwRead, &dwTemp, NULL) &&
dwRead && pNetBuf &&
NERR_Success == pfnNetApiBufferSize(pNetBuf, &dwTemp) )
{
*ppBuffer = new BYTE[dwTemp];
if( *ppBuffer )
{
// copy the bits first
memcpy(*ppBuffer, pNetBuf, dwTemp);
// adjust the pointers here - a little bit ugly, but works
for( DWORD dw = 0; dw < dwRead; dw++ )
{
// adjust shi1_netname
reinterpret_cast<SHARE_INFO_1*>(*ppBuffer)[dw].shi1_netname =
reinterpret_cast<LPWSTR>(
(*ppBuffer) +
(reinterpret_cast<BYTE*>(
reinterpret_cast<SHARE_INFO_1*>(pNetBuf)[dw].shi1_netname) -
pNetBuf));
// adjust shi1_remark
reinterpret_cast<SHARE_INFO_1*>(*ppBuffer)[dw].shi1_remark =
reinterpret_cast<LPWSTR>(
(*ppBuffer) +
(reinterpret_cast<BYTE*>(
reinterpret_cast<SHARE_INFO_1*>(pNetBuf)[dw].shi1_remark) -
pNetBuf));
}
// number of structures returned
*pcReturned = dwRead;
}
hr = ((*ppBuffer) ? S_OK : E_OUTOFMEMORY);
CHECK(NERR_Success == pfnNetApiBufferFree(pNetBuf));
}
}
}
if( E_FAIL == hr && ERROR_SUCCESS != GetLastError() )
{
// if failed, let's be more spcific about what the error is...
hr = HRESULT_FROM_WIN32(GetLastError());
}
return hr;
}
} // namespace ShellServices
// utility functions
HRESULT LoadXMLDOMDoc(LPCTSTR pszURL, IXMLDOMDocument **ppXMLDoc)
{
HRESULT hr = E_INVALIDARG;
CRefPtrCOM<IXMLDOMDocument> spXMLDoc;
if( pszURL && ppXMLDoc )
{
*ppXMLDoc = NULL;
// create an instance of XMLDOM
hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER, IID_IXMLDOMDocument, (void **)&spXMLDoc);
if( SUCCEEDED(hr) )
{
CComVariant xmlSource(pszURL);
if( VT_BSTR == xmlSource.vt )
{
// just load the XML document here
VARIANT_BOOL fIsSuccessful = VARIANT_TRUE;
hr = spXMLDoc->load(xmlSource, &fIsSuccessful);
if( S_FALSE == hr || VARIANT_FALSE == fIsSuccessful )
{
// this isn't a valid XML file.
hr = E_FAIL;
}
else
{
// everything looks successful here - just return the XML document
*ppXMLDoc = spXMLDoc.Detach();
}
}
else
{
// xmlSource failed to allocate the string
hr = E_OUTOFMEMORY;
}
}
}
return hr;
}
HRESULT CreateStreamFromURL(LPCTSTR pszURL, IStream **pps)
{
HRESULT hr = E_INVALIDARG;
if( pszURL && pps )
{
*pps = NULL;
TCHAR szBuf[INTERNET_MAX_SCHEME_LENGTH];
DWORD cch = ARRAYSIZE(szBuf);
if( SUCCEEDED(hr = CoInternetParseUrl(pszURL, PARSE_SCHEMA, 0, szBuf, cch, &cch, 0)) &&
0 == lstrcmp(szBuf, TEXT("res")) )
{
// check if this is a res:// URL to handle explicitly since
// this protocol doesn't report filename and therefore can't
// be used in conditions where caching is required - we can't
// call URLOpenBlockingStream - use alternatives.
// not impl. yet...
ASSERT(FALSE);
}
hr = URLOpenBlockingStream(NULL, pszURL, pps, 0, NULL);
}
return hr;
}
HRESULT CreateStreamFromResource(LPCTSTR pszModule, LPCTSTR pszResType, LPCTSTR pszResName, IStream **pps)
{
HRESULT hr = E_INVALIDARG;
if( pszResType && pszResName )
{
hr = E_FAIL;
*pps = NULL;
HINSTANCE hModule = NULL;
if( (NULL == pszModule) || (hModule = LoadLibrary(pszModule)) )
{
HRSRC hHint = NULL;
ULONG uSize = 0;
if( (hHint = FindResource(hModule, pszResName, pszResType)) &&
(uSize = SizeofResource(hModule, hHint)) )
{
HGLOBAL hResData = LoadResource(hModule, hHint);
if( hResData )
{
LPVOID lpResData = LockResource(hResData);
if( lpResData )
{
if( (*pps = SHCreateMemStream(reinterpret_cast<LPBYTE>(lpResData), uSize)) )
{
hr = S_OK;
}
UnlockResource(lpResData);
}
FreeResource(hResData);
}
}
}
if( hModule )
{
FreeLibrary(hModule);
}
}
return hr;
}
HRESULT Gdiplus2HRESULT(Gdiplus::Status status)
{
// can't think of a better way to do this now
HRESULT hr = E_FAIL;
switch( status )
{
case Gdiplus::Ok:
hr = S_OK;
break;
case Gdiplus::InvalidParameter:
hr = E_INVALIDARG;
break;
case Gdiplus::OutOfMemory:
hr = E_OUTOFMEMORY;
break;
case Gdiplus::InsufficientBuffer:
hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
break;
case Gdiplus::Aborted:
hr = E_ABORT;
break;
case Gdiplus::ObjectBusy:
hr = E_PENDING;
break;
case Gdiplus::FileNotFound:
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
break;
case Gdiplus::AccessDenied:
hr = E_ACCESSDENIED;
break;
case Gdiplus::UnknownImageFormat:
hr = HRESULT_FROM_WIN32(ERROR_INVALID_PIXEL_FORMAT);
break;
case Gdiplus::NotImplemented:
hr = E_NOTIMPL;
break;
case Gdiplus::Win32Error:
hr = HRESULT_FROM_WIN32(GetLastError());
break;
case Gdiplus::ValueOverflow:
case Gdiplus::FontFamilyNotFound:
case Gdiplus::FontStyleNotFound:
case Gdiplus::NotTrueTypeFont:
case Gdiplus::UnsupportedGdiplusVersion:
case Gdiplus::GdiplusNotInitialized:
case Gdiplus::WrongState:
break;
}
return hr;
}
HRESULT LoadAndScaleBmp(LPCTSTR pszURL, UINT nWidth, UINT nHeight, Gdiplus::Bitmap **ppBmp)
{
CRefPtrCOM<IStream> spStream;
HRESULT hr = CreateStreamFromURL(pszURL, &spStream);
if( SUCCEEDED(hr) )
{
hr = LoadAndScaleBmp(spStream, nWidth, nHeight, ppBmp);
}
return hr;
}
HRESULT LoadAndScaleBmp(IStream *pStream, UINT nWidth, UINT nHeight, Gdiplus::Bitmap **ppBmp)
{
HRESULT hr = E_INVALIDARG;
if( pStream && nWidth && nHeight && ppBmp )
{
hr = E_FAIL;
*ppBmp = NULL;
Gdiplus::Bitmap bmp(pStream);
if( SUCCEEDED(hr = Gdiplus2HRESULT(bmp.GetLastStatus())) )
{
hr = E_OUTOFMEMORY;
CAutoPtr<Gdiplus::Bitmap> spBmpNew = new Gdiplus::Bitmap(nWidth, nHeight);
if( spBmpNew && SUCCEEDED(hr = Gdiplus2HRESULT(spBmpNew->GetLastStatus())) )
{
Gdiplus::Graphics g(spBmpNew);
if( SUCCEEDED(hr = Gdiplus2HRESULT(g.GetLastStatus())) )
{
if( SUCCEEDED(hr = g.DrawImage(&bmp, 0, 0, nWidth, nHeight)) )
{
*ppBmp = spBmpNew.Detach();
hr = S_OK;
}
}
}
}
}
return hr;
}
//
// This function is trying to get the last active popup of the top
// level owner of the current thread active window.
//
HRESULT GetCurrentThreadLastPopup(HWND *phwnd)
{
HRESULT hr = E_INVALIDARG;
if( phwnd )
{
hr = E_FAIL;
if( NULL == *phwnd )
{
// if *phwnd is NULL then get the current thread active window
GUITHREADINFO ti = {0};
ti.cbSize = sizeof(ti);
if( GetGUIThreadInfo(0, &ti) && ti.hwndActive )
{
*phwnd = ti.hwndActive;
}
}
if( *phwnd )
{
HWND hwndOwner, hwndParent;
// climb up to the top parent in case it's a child window...
while( hwndParent = GetParent(*phwnd) )
{
*phwnd = hwndParent;
}
// get the owner in case the top parent is owned
hwndOwner = GetWindow(*phwnd, GW_OWNER);
if( hwndOwner )
{
*phwnd = hwndOwner;
}
// get the last popup of the owner of the top level parent window
*phwnd = GetLastActivePopup(*phwnd);
hr = (*phwnd) ? S_OK : E_FAIL;
}
}
return hr;
}