windows-nt/Source/XPSP1/NT/shell/ext/hnw/wizard/prnutil.cpp
2020-09-26 16:20:57 +08:00

457 lines
16 KiB
C++

//
// PrnUtil.cpp
//
#include "stdafx.h"
#include "PrnUtil.h"
#include "Sharing.h"
#include "msprintx.h"
#include "NetUtil.h"
#include "TheApp.h"
#include "cwnd.h"
/////////////////////////////////////////////////////////////////////////////
// static data
static BOOL _bInit = FALSE;
static HMODULE _hShell32 = NULL;
static HMODULE _hMSPrint2 = NULL;
static BOOL (STDAPICALLTYPE *_pfnSHInvokePrinterCommand)(HWND, UINT, LPCTSTR, LPCTSTR, BOOL) = NULL;
static BOOL (STDAPICALLTYPE *_pfnSHHelpShortcuts)(HWND, HINSTANCE, LPSTR, int) = NULL;
static BOOL (STDAPICALLTYPE *_pfnPrinterSetup32)(HWND, WORD, WORD, LPBYTE, LPWORD) = NULL;
/////////////////////////////////////////////////////////////////////////////
// Initialization of function thunks
void InitPrinterFunctions()
{
if (!_bInit)
{
_bInit = TRUE;
_hShell32 = LoadLibrary(TEXT("shell32.dll"));
if (_hShell32 != NULL)
{
*(FARPROC*)&_pfnSHInvokePrinterCommand = GetProcAddress(_hShell32, "SHInvokePrinterCommandA");
*(FARPROC*)&_pfnSHHelpShortcuts = GetProcAddress(_hShell32, "SHHelpShortcuts_RunDLL");
}
if (theApp.IsWindows9x())
{
_hMSPrint2 = LoadLibrary(TEXT("msprint2.dll"));
if (_hMSPrint2 != NULL)
{
*(FARPROC*)&_pfnPrinterSetup32 = GetProcAddress(_hMSPrint2, MSPRINT2_PRINTERSETUP32);
}
}
else
{
// NTs version of this function moved to a new dll and a different name
_hMSPrint2 = LoadLibrary(TEXT("printui.dll"));
if (_hMSPrint2 != NULL)
{
*(FARPROC*)&_pfnPrinterSetup32 = GetProcAddress(_hMSPrint2, "bPrinterSetup");
}
}
}
}
/////////////////////////////////////////////////////////////////////////////
// MyEnumPrinters
//
// Enumerates local or remote connected printers, allocates an array
// of PRINTER_ENUM structs for the result, and returns the number of
// printers found.
//
// pprgPrinters - gets filled with an array of PRINTER_ENUM structs
// allocated via malloc().
//
// dwEnumFlags - one or more of:
// MY_PRINTER_ENUM_REMOTE
// MY_PRINTER_ENUM_LOCAL
// MY_PRINTER_ENUM_LOCAL
//
int MyEnumPrinters(PRINTER_ENUM** pprgPrinters, DWORD dwEnumFlags)
{
PRINTER_ENUM* prgPrinters = NULL;
int cMatchingPrinters = 0;
ASSERT(sizeof(PRINTER_INFO_5A) == sizeof(PRINTER_INFO_5W)); // to handle thunking
DWORD cb = 0;
DWORD cAllPrinters = 0;
EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &cb, &cAllPrinters);
if (cb > 0)
{
PRINTER_INFO_5* prgPrinterInfo5 = (PRINTER_INFO_5*)malloc(cb);
if (prgPrinterInfo5)
{
if (EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)prgPrinterInfo5, cb, &cb, &cAllPrinters))
{
ASSERT(cAllPrinters > 0);
// How much space will the strings take?
DWORD cbArray = cAllPrinters * sizeof(PRINTER_INFO_5);
DWORD cbStrings = cb - cbArray;
// Allocate out [OUT] buffer
prgPrinters = (PRINTER_ENUM*)malloc(cAllPrinters*sizeof(PRINTER_ENUM) + cbStrings);
if (prgPrinters)
{
// set up text portion of output buffer and thunking/copying function
//
LPTSTR pszPrinterText = (LPTSTR)(prgPrinters + cAllPrinters);
UINT cchStrings = cbStrings/sizeof(WCHAR);
// NT and 9X do defaultness differently...
TCHAR szDefaultPrinter[MAX_PATH];
szDefaultPrinter[0]=TEXT('\0');
if (!theApp.IsWindows9x())
{
DWORD cch = ARRAYSIZE(szDefaultPrinter);
GetDefaultPrinter(szDefaultPrinter, &cch);
}
// Fill in the output buffer
for (DWORD i = 0; i < cAllPrinters; i++)
{
BOOL bKeepThisPrinter = FALSE;
DWORD dwFlags = 0;
if (theApp.IsWindows9x())
{
PRINTER_INFO_5* pPrinterInfo5 = (PRINTER_INFO_5*)&prgPrinterInfo5[i];
if (pPrinterInfo5->pPortName[0] == L'\\' && pPrinterInfo5->pPortName[1] == L'\\')
{
// Found a remote, connected printer
if (dwEnumFlags & MY_PRINTER_ENUM_REMOTE)
{
bKeepThisPrinter = TRUE;
dwFlags |= PRF_REMOTE;
}
}
else if (0 == StrCmpI(pPrinterInfo5->pPortName, L"FILE:"))
{
// Found a pseudo printer
if (dwEnumFlags & MY_PRINTER_ENUM_VIRTUAL)
{
bKeepThisPrinter = TRUE;
dwFlags |= PRF_VIRTUAL;
}
}
else if (StrStr(pPrinterInfo5->pPortName, L"FAX"))
{
// Found a pseudo printer
if (dwEnumFlags & MY_PRINTER_ENUM_VIRTUAL)
{
bKeepThisPrinter = TRUE;
dwFlags |= PRF_VIRTUAL;
}
}
else
{
// Found a local printer
if (dwEnumFlags & MY_PRINTER_ENUM_LOCAL)
{
bKeepThisPrinter = TRUE;
dwFlags |= PRF_LOCAL;
}
}
}
else // handle NT
{
PRINTER_INFO_5* pPrinterInfo5 = (PRINTER_INFO_5*)&prgPrinterInfo5[i];
if (pPrinterInfo5->pPortName[0] == _T('\\') && pPrinterInfo5->pPortName[1] == _T('\\'))
{
// Found a remote, connected printer
if (dwEnumFlags & MY_PRINTER_ENUM_REMOTE)
{
bKeepThisPrinter = TRUE;
dwFlags |= PRF_REMOTE;
}
}
else if (0 == StrCmpI(pPrinterInfo5->pPortName, _T("FILE:")))
{
// Found a pseudo printer
if (dwEnumFlags & MY_PRINTER_ENUM_VIRTUAL)
{
bKeepThisPrinter = TRUE;
dwFlags |= PRF_VIRTUAL;
}
}
else if (StrStr(pPrinterInfo5->pPortName, _T("FAX")))
{
// Found a pseudo printer
if (dwEnumFlags & MY_PRINTER_ENUM_VIRTUAL)
{
bKeepThisPrinter = TRUE;
dwFlags |= PRF_VIRTUAL;
}
}
else
{
// Found a local printer
if (dwEnumFlags & MY_PRINTER_ENUM_LOCAL)
{
bKeepThisPrinter = TRUE;
dwFlags |= PRF_LOCAL;
}
}
}
if (bKeepThisPrinter)
{
PRINTER_INFO_5* pPrinterInfo5 = (PRINTER_INFO_5*)&prgPrinterInfo5[i];
PRINTER_ENUM* pPrinter = &prgPrinters[cMatchingPrinters++];
int cch;
StrCpyNW(pszPrinterText, pPrinterInfo5->pPrinterName, cchStrings);
cch = lstrlenW(pszPrinterText) + 1;
pPrinter->pszPrinterName = pszPrinterText;
pszPrinterText += cch;
cchStrings -= cch;
StrCpyNW(pszPrinterText, pPrinterInfo5->pPortName, cchStrings);
cch = lstrlenW(pszPrinterText) + 1;
pPrinter->pszPortName = pszPrinterText;
pszPrinterText += cch;
cchStrings -= cch;
// update some flags before we cache them away
//
if (!(dwFlags&PRF_REMOTE) && IsPrinterShared(pPrinter->pszPrinterName))
{
dwFlags |= PRF_SHARED;
}
if ((pPrinterInfo5->Attributes & PRINTER_ATTRIBUTE_DEFAULT)
|| (0 == StrCmpI(szDefaultPrinter, pPrinterInfo5->pPrinterName)))
{
dwFlags |= PRF_DEFAULT;
}
pPrinter->dwFlags = dwFlags;
}
}
// didn't find anything, throw away our output buffer
if (cMatchingPrinters == 0 && prgPrinters != NULL)
{
free(prgPrinters);
prgPrinters = NULL;
}
}
}
free(prgPrinterInfo5);
}
}
*pprgPrinters = prgPrinters;
return cMatchingPrinters;
}
int MyEnumLocalPrinters(PRINTER_ENUM** prgPrinters)
{
return MyEnumPrinters(prgPrinters, MY_PRINTER_ENUM_LOCAL);
}
int MyEnumRemotePrinters(PRINTER_ENUM** prgPrinters)
{
return MyEnumPrinters(prgPrinters, MY_PRINTER_ENUM_REMOTE);
}
/////////////////////////////////////////////////////////////////////////////
// AddPrinterHookProc
class CAddPrinterHook : public CWnd
{
public:
CAddPrinterHook(LPCTSTR pszAppendWindowTitle, HWND hwndOwner);
void Release() { CWnd::Release(); };
void Done(BOOL bResult);
protected:
static LRESULT CALLBACK AddPrinterHookProcStatic(int nCode, WPARAM wParam, LPARAM lParam);
~CAddPrinterHook();
LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
LRESULT AddPrinterHookProc(int nCode, WPARAM wParam, LPARAM lParam);
HHOOK m_hAddPrinterHook;
HWND m_hWndAddPrinterParent;
LPTSTR m_pszAppendWindowTitle;
};
// global hooks have no state, must use global to get back to our data
static CAddPrinterHook * g_pCAddPrinterHook = NULL;
CAddPrinterHook::CAddPrinterHook(LPCTSTR pszAppendWindowTitle, HWND hwndOwner)
{
ASSERT(NULL == g_pCAddPrinterHook);
g_pCAddPrinterHook = this;
m_pszAppendWindowTitle = lstrdup(pszAppendWindowTitle);
m_hWndAddPrinterParent = hwndOwner;
// Set a hook so we can modify the title of the add printer wizard when it pops up
m_hAddPrinterHook = SetWindowsHookEx(WH_CBT, AddPrinterHookProcStatic, NULL, GetCurrentThreadId());
}
CAddPrinterHook::~CAddPrinterHook()
{
ASSERT(this == g_pCAddPrinterHook);
g_pCAddPrinterHook = NULL;
if (m_pszAppendWindowTitle)
free(m_pszAppendWindowTitle);
CWnd::~CWnd();
}
void CAddPrinterHook::Done(BOOL bResult)
{
// TRUE==bResult if the window was launched.
//
// FALSE => no window to watch, so remove our hook as it'll never come up
// TRUE => if the window is on the same thread, we've already seen it and unhooked
// but if the window is on another thread, it may not come up it so don't unhook.
// EXCEPT, we may never see it. So be safe and always unhook...
//
if (m_hAddPrinterHook != NULL)
{
if (bResult)
{
TraceMsg(TF_WARNING, "CAddPrinterHook::Done(TRUE) called but m_hAddPrinterHook still exists...");
}
UnhookWindowsHookEx(m_hAddPrinterHook);
m_hAddPrinterHook = NULL;
}
}
LRESULT CAddPrinterHook::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
LPTSTR pszTempText = NULL;
switch (message)
{
case WM_SETTEXT:
if (m_pszAppendWindowTitle)
{
pszTempText = new TCHAR [lstrlen(m_pszAppendWindowTitle) + lstrlen((LPCTSTR)lParam) + 1];
if (pszTempText)
{
StrCpy(pszTempText, (LPCTSTR)lParam);
StrCat(pszTempText, m_pszAppendWindowTitle);
lParam = (LPARAM)pszTempText;
}
}
break;
}
LRESULT lResult = Default(message, wParam, lParam);
delete [] pszTempText;
return lResult;
}
LRESULT CALLBACK CAddPrinterHook::AddPrinterHookProcStatic(int nCode, WPARAM wParam, LPARAM lParam)
{
CAddPrinterHook* pThis = g_pCAddPrinterHook; // global hook -- we have no associated state!
if (pThis)
return pThis->AddPrinterHookProc(nCode, wParam, lParam);
else
return 0;
}
LRESULT CAddPrinterHook::AddPrinterHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{
LRESULT lResult = CallNextHookEx(m_hAddPrinterHook, nCode, wParam, lParam);
if (nCode == HCBT_CREATEWND)
{
HWND hwndNew = (HWND)wParam;
CBT_CREATEWND* pCreateWnd = (CBT_CREATEWND*)lParam;
if (pCreateWnd->lpcs->hwndParent == m_hWndAddPrinterParent &&
(pCreateWnd->lpcs->style & WS_POPUP) != 0)
{
UnhookWindowsHookEx(m_hAddPrinterHook);
m_hAddPrinterHook = NULL;
Attach(hwndNew);
}
}
return lResult;
}
/////////////////////////////////////////////////////////////////////////////
// ConnectToNetworkPrinter
BOOL ConnectToNetworkPrinter(HWND hWndOwner, LPCTSTR pszPrinterShare)
{
InitPrinterFunctions();
BOOL bResult;
LPTSTR pszAppendWindowTitle = NULL;
LPTSTR pszPrettyName = FormatShareNameAlloc(pszPrinterShare);
if (pszPrettyName)
{
pszAppendWindowTitle = theApp.FormatStringAlloc(IDS_ADDPRINTER_APPEND, pszPrettyName);
free(pszPrettyName);
}
CAddPrinterHook * paph = new CAddPrinterHook(pszAppendWindowTitle, hWndOwner);
if (pszAppendWindowTitle)
free(pszAppendWindowTitle);
if (_pfnSHInvokePrinterCommand != NULL)
{
// First: Try to call SHInvokePrinterCommand, if available.
// This only works on systems with the IE4 desktop enhancements installed.
bResult = (*_pfnSHInvokePrinterCommand)(hWndOwner, PRINTACTION_NETINSTALL, pszPrinterShare, NULL, TRUE);
}
else if (_pfnPrinterSetup32 != NULL)
{
// Next: Try to call PrinterSetup32, if available.
WORD cch = lstrlen(pszPrinterShare) + 1;
BYTE* pPrinterShare = (BYTE*)malloc(cch);
StrCpy((LPTSTR)pPrinterShare, pszPrinterShare);
bResult = (*_pfnPrinterSetup32)(hWndOwner, MSP_NETPRINTER, cch, pPrinterShare, &cch);
free(pPrinterShare);
}
else if (_pfnSHHelpShortcuts != NULL)
{
// Neither of the above APIs was available.
// Instead, just launch the Add Printer Wizard.
bResult = (*_pfnSHHelpShortcuts)(hWndOwner, _hShell32, "AddPrinter", SW_SHOW);
}
else
{
// Yikes, we can't even launch the Add Printer Wizard!
bResult = FALSE;
}
if (paph)
{
paph->Done(bResult);
paph->Release();
}
return bResult;
}