// // 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; }