457 lines
16 KiB
C++
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;
|
||
|
}
|
||
|
|