Leaked source code of windows server 2003
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.

456 lines
16 KiB

  1. //
  2. // PrnUtil.cpp
  3. //
  4. #include "stdafx.h"
  5. #include "PrnUtil.h"
  6. #include "Sharing.h"
  7. #include "msprintx.h"
  8. #include "NetUtil.h"
  9. #include "TheApp.h"
  10. #include "cwnd.h"
  11. /////////////////////////////////////////////////////////////////////////////
  12. // static data
  13. static BOOL _bInit = FALSE;
  14. static HMODULE _hShell32 = NULL;
  15. static HMODULE _hMSPrint2 = NULL;
  16. static BOOL (STDAPICALLTYPE *_pfnSHInvokePrinterCommand)(HWND, UINT, LPCTSTR, LPCTSTR, BOOL) = NULL;
  17. static BOOL (STDAPICALLTYPE *_pfnSHHelpShortcuts)(HWND, HINSTANCE, LPSTR, int) = NULL;
  18. static BOOL (STDAPICALLTYPE *_pfnPrinterSetup32)(HWND, WORD, WORD, LPBYTE, LPWORD) = NULL;
  19. /////////////////////////////////////////////////////////////////////////////
  20. // Initialization of function thunks
  21. void InitPrinterFunctions()
  22. {
  23. if (!_bInit)
  24. {
  25. _bInit = TRUE;
  26. _hShell32 = LoadLibrary(TEXT("shell32.dll"));
  27. if (_hShell32 != NULL)
  28. {
  29. *(FARPROC*)&_pfnSHInvokePrinterCommand = GetProcAddress(_hShell32, "SHInvokePrinterCommandA");
  30. *(FARPROC*)&_pfnSHHelpShortcuts = GetProcAddress(_hShell32, "SHHelpShortcuts_RunDLL");
  31. }
  32. if (theApp.IsWindows9x())
  33. {
  34. _hMSPrint2 = LoadLibrary(TEXT("msprint2.dll"));
  35. if (_hMSPrint2 != NULL)
  36. {
  37. *(FARPROC*)&_pfnPrinterSetup32 = GetProcAddress(_hMSPrint2, MSPRINT2_PRINTERSETUP32);
  38. }
  39. }
  40. else
  41. {
  42. // NTs version of this function moved to a new dll and a different name
  43. _hMSPrint2 = LoadLibrary(TEXT("printui.dll"));
  44. if (_hMSPrint2 != NULL)
  45. {
  46. *(FARPROC*)&_pfnPrinterSetup32 = GetProcAddress(_hMSPrint2, "bPrinterSetup");
  47. }
  48. }
  49. }
  50. }
  51. /////////////////////////////////////////////////////////////////////////////
  52. // MyEnumPrinters
  53. //
  54. // Enumerates local or remote connected printers, allocates an array
  55. // of PRINTER_ENUM structs for the result, and returns the number of
  56. // printers found.
  57. //
  58. // pprgPrinters - gets filled with an array of PRINTER_ENUM structs
  59. // allocated via malloc().
  60. //
  61. // dwEnumFlags - one or more of:
  62. // MY_PRINTER_ENUM_REMOTE
  63. // MY_PRINTER_ENUM_LOCAL
  64. // MY_PRINTER_ENUM_LOCAL
  65. //
  66. int MyEnumPrinters(PRINTER_ENUM** pprgPrinters, DWORD dwEnumFlags)
  67. {
  68. PRINTER_ENUM* prgPrinters = NULL;
  69. int cMatchingPrinters = 0;
  70. ASSERT(sizeof(PRINTER_INFO_5A) == sizeof(PRINTER_INFO_5W)); // to handle thunking
  71. DWORD cb = 0;
  72. DWORD cAllPrinters = 0;
  73. EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, NULL, 0, &cb, &cAllPrinters);
  74. if (cb > 0)
  75. {
  76. PRINTER_INFO_5* prgPrinterInfo5 = (PRINTER_INFO_5*)malloc(cb);
  77. if (prgPrinterInfo5)
  78. {
  79. if (EnumPrinters(PRINTER_ENUM_LOCAL, NULL, 5, (LPBYTE)prgPrinterInfo5, cb, &cb, &cAllPrinters))
  80. {
  81. ASSERT(cAllPrinters > 0);
  82. // How much space will the strings take?
  83. DWORD cbArray = cAllPrinters * sizeof(PRINTER_INFO_5);
  84. DWORD cbStrings = cb - cbArray;
  85. // Allocate out [OUT] buffer
  86. prgPrinters = (PRINTER_ENUM*)malloc(cAllPrinters*sizeof(PRINTER_ENUM) + cbStrings);
  87. if (prgPrinters)
  88. {
  89. // set up text portion of output buffer and thunking/copying function
  90. //
  91. LPTSTR pszPrinterText = (LPTSTR)(prgPrinters + cAllPrinters);
  92. UINT cchStrings = cbStrings/sizeof(WCHAR);
  93. // NT and 9X do defaultness differently...
  94. TCHAR szDefaultPrinter[MAX_PATH];
  95. szDefaultPrinter[0]=TEXT('\0');
  96. if (!theApp.IsWindows9x())
  97. {
  98. DWORD cch = ARRAYSIZE(szDefaultPrinter);
  99. GetDefaultPrinter(szDefaultPrinter, &cch);
  100. }
  101. // Fill in the output buffer
  102. for (DWORD i = 0; i < cAllPrinters; i++)
  103. {
  104. BOOL bKeepThisPrinter = FALSE;
  105. DWORD dwFlags = 0;
  106. if (theApp.IsWindows9x())
  107. {
  108. PRINTER_INFO_5* pPrinterInfo5 = (PRINTER_INFO_5*)&prgPrinterInfo5[i];
  109. if (pPrinterInfo5->pPortName[0] == L'\\' && pPrinterInfo5->pPortName[1] == L'\\')
  110. {
  111. // Found a remote, connected printer
  112. if (dwEnumFlags & MY_PRINTER_ENUM_REMOTE)
  113. {
  114. bKeepThisPrinter = TRUE;
  115. dwFlags |= PRF_REMOTE;
  116. }
  117. }
  118. else if (0 == StrCmpI(pPrinterInfo5->pPortName, L"FILE:"))
  119. {
  120. // Found a pseudo printer
  121. if (dwEnumFlags & MY_PRINTER_ENUM_VIRTUAL)
  122. {
  123. bKeepThisPrinter = TRUE;
  124. dwFlags |= PRF_VIRTUAL;
  125. }
  126. }
  127. else if (StrStr(pPrinterInfo5->pPortName, L"FAX"))
  128. {
  129. // Found a pseudo printer
  130. if (dwEnumFlags & MY_PRINTER_ENUM_VIRTUAL)
  131. {
  132. bKeepThisPrinter = TRUE;
  133. dwFlags |= PRF_VIRTUAL;
  134. }
  135. }
  136. else
  137. {
  138. // Found a local printer
  139. if (dwEnumFlags & MY_PRINTER_ENUM_LOCAL)
  140. {
  141. bKeepThisPrinter = TRUE;
  142. dwFlags |= PRF_LOCAL;
  143. }
  144. }
  145. }
  146. else // handle NT
  147. {
  148. PRINTER_INFO_5* pPrinterInfo5 = (PRINTER_INFO_5*)&prgPrinterInfo5[i];
  149. if (pPrinterInfo5->pPortName[0] == _T('\\') && pPrinterInfo5->pPortName[1] == _T('\\'))
  150. {
  151. // Found a remote, connected printer
  152. if (dwEnumFlags & MY_PRINTER_ENUM_REMOTE)
  153. {
  154. bKeepThisPrinter = TRUE;
  155. dwFlags |= PRF_REMOTE;
  156. }
  157. }
  158. else if (0 == StrCmpI(pPrinterInfo5->pPortName, _T("FILE:")))
  159. {
  160. // Found a pseudo printer
  161. if (dwEnumFlags & MY_PRINTER_ENUM_VIRTUAL)
  162. {
  163. bKeepThisPrinter = TRUE;
  164. dwFlags |= PRF_VIRTUAL;
  165. }
  166. }
  167. else if (StrStr(pPrinterInfo5->pPortName, _T("FAX")))
  168. {
  169. // Found a pseudo printer
  170. if (dwEnumFlags & MY_PRINTER_ENUM_VIRTUAL)
  171. {
  172. bKeepThisPrinter = TRUE;
  173. dwFlags |= PRF_VIRTUAL;
  174. }
  175. }
  176. else
  177. {
  178. // Found a local printer
  179. if (dwEnumFlags & MY_PRINTER_ENUM_LOCAL)
  180. {
  181. bKeepThisPrinter = TRUE;
  182. dwFlags |= PRF_LOCAL;
  183. }
  184. }
  185. }
  186. if (bKeepThisPrinter)
  187. {
  188. PRINTER_INFO_5* pPrinterInfo5 = (PRINTER_INFO_5*)&prgPrinterInfo5[i];
  189. PRINTER_ENUM* pPrinter = &prgPrinters[cMatchingPrinters++];
  190. int cch;
  191. StrCpyNW(pszPrinterText, pPrinterInfo5->pPrinterName, cchStrings);
  192. cch = lstrlenW(pszPrinterText) + 1;
  193. pPrinter->pszPrinterName = pszPrinterText;
  194. pszPrinterText += cch;
  195. cchStrings -= cch;
  196. StrCpyNW(pszPrinterText, pPrinterInfo5->pPortName, cchStrings);
  197. cch = lstrlenW(pszPrinterText) + 1;
  198. pPrinter->pszPortName = pszPrinterText;
  199. pszPrinterText += cch;
  200. cchStrings -= cch;
  201. // update some flags before we cache them away
  202. //
  203. if (!(dwFlags&PRF_REMOTE) && IsPrinterShared(pPrinter->pszPrinterName))
  204. {
  205. dwFlags |= PRF_SHARED;
  206. }
  207. if ((pPrinterInfo5->Attributes & PRINTER_ATTRIBUTE_DEFAULT)
  208. || (0 == StrCmpI(szDefaultPrinter, pPrinterInfo5->pPrinterName)))
  209. {
  210. dwFlags |= PRF_DEFAULT;
  211. }
  212. pPrinter->dwFlags = dwFlags;
  213. }
  214. }
  215. // didn't find anything, throw away our output buffer
  216. if (cMatchingPrinters == 0 && prgPrinters != NULL)
  217. {
  218. free(prgPrinters);
  219. prgPrinters = NULL;
  220. }
  221. }
  222. }
  223. free(prgPrinterInfo5);
  224. }
  225. }
  226. *pprgPrinters = prgPrinters;
  227. return cMatchingPrinters;
  228. }
  229. int MyEnumLocalPrinters(PRINTER_ENUM** prgPrinters)
  230. {
  231. return MyEnumPrinters(prgPrinters, MY_PRINTER_ENUM_LOCAL);
  232. }
  233. int MyEnumRemotePrinters(PRINTER_ENUM** prgPrinters)
  234. {
  235. return MyEnumPrinters(prgPrinters, MY_PRINTER_ENUM_REMOTE);
  236. }
  237. /////////////////////////////////////////////////////////////////////////////
  238. // AddPrinterHookProc
  239. class CAddPrinterHook : public CWnd
  240. {
  241. public:
  242. CAddPrinterHook(LPCTSTR pszAppendWindowTitle, HWND hwndOwner);
  243. void Release() { CWnd::Release(); };
  244. void Done(BOOL bResult);
  245. protected:
  246. static LRESULT CALLBACK AddPrinterHookProcStatic(int nCode, WPARAM wParam, LPARAM lParam);
  247. ~CAddPrinterHook();
  248. LRESULT WindowProc(UINT message, WPARAM wParam, LPARAM lParam);
  249. LRESULT AddPrinterHookProc(int nCode, WPARAM wParam, LPARAM lParam);
  250. HHOOK m_hAddPrinterHook;
  251. HWND m_hWndAddPrinterParent;
  252. LPTSTR m_pszAppendWindowTitle;
  253. };
  254. // global hooks have no state, must use global to get back to our data
  255. static CAddPrinterHook * g_pCAddPrinterHook = NULL;
  256. CAddPrinterHook::CAddPrinterHook(LPCTSTR pszAppendWindowTitle, HWND hwndOwner)
  257. {
  258. ASSERT(NULL == g_pCAddPrinterHook);
  259. g_pCAddPrinterHook = this;
  260. m_pszAppendWindowTitle = lstrdup(pszAppendWindowTitle);
  261. m_hWndAddPrinterParent = hwndOwner;
  262. // Set a hook so we can modify the title of the add printer wizard when it pops up
  263. m_hAddPrinterHook = SetWindowsHookEx(WH_CBT, AddPrinterHookProcStatic, NULL, GetCurrentThreadId());
  264. }
  265. CAddPrinterHook::~CAddPrinterHook()
  266. {
  267. ASSERT(this == g_pCAddPrinterHook);
  268. g_pCAddPrinterHook = NULL;
  269. if (m_pszAppendWindowTitle)
  270. free(m_pszAppendWindowTitle);
  271. CWnd::~CWnd();
  272. }
  273. void CAddPrinterHook::Done(BOOL bResult)
  274. {
  275. // TRUE==bResult if the window was launched.
  276. //
  277. // FALSE => no window to watch, so remove our hook as it'll never come up
  278. // TRUE => if the window is on the same thread, we've already seen it and unhooked
  279. // but if the window is on another thread, it may not come up it so don't unhook.
  280. // EXCEPT, we may never see it. So be safe and always unhook...
  281. //
  282. if (m_hAddPrinterHook != NULL)
  283. {
  284. if (bResult)
  285. {
  286. TraceMsg(TF_WARNING, "CAddPrinterHook::Done(TRUE) called but m_hAddPrinterHook still exists...");
  287. }
  288. UnhookWindowsHookEx(m_hAddPrinterHook);
  289. m_hAddPrinterHook = NULL;
  290. }
  291. }
  292. LRESULT CAddPrinterHook::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
  293. {
  294. LPTSTR pszTempText = NULL;
  295. switch (message)
  296. {
  297. case WM_SETTEXT:
  298. if (m_pszAppendWindowTitle)
  299. {
  300. pszTempText = new TCHAR [lstrlen(m_pszAppendWindowTitle) + lstrlen((LPCTSTR)lParam) + 1];
  301. if (pszTempText)
  302. {
  303. StrCpy(pszTempText, (LPCTSTR)lParam);
  304. StrCat(pszTempText, m_pszAppendWindowTitle);
  305. lParam = (LPARAM)pszTempText;
  306. }
  307. }
  308. break;
  309. }
  310. LRESULT lResult = Default(message, wParam, lParam);
  311. delete [] pszTempText;
  312. return lResult;
  313. }
  314. LRESULT CALLBACK CAddPrinterHook::AddPrinterHookProcStatic(int nCode, WPARAM wParam, LPARAM lParam)
  315. {
  316. CAddPrinterHook* pThis = g_pCAddPrinterHook; // global hook -- we have no associated state!
  317. if (pThis)
  318. return pThis->AddPrinterHookProc(nCode, wParam, lParam);
  319. else
  320. return 0;
  321. }
  322. LRESULT CAddPrinterHook::AddPrinterHookProc(int nCode, WPARAM wParam, LPARAM lParam)
  323. {
  324. LRESULT lResult = CallNextHookEx(m_hAddPrinterHook, nCode, wParam, lParam);
  325. if (nCode == HCBT_CREATEWND)
  326. {
  327. HWND hwndNew = (HWND)wParam;
  328. CBT_CREATEWND* pCreateWnd = (CBT_CREATEWND*)lParam;
  329. if (pCreateWnd->lpcs->hwndParent == m_hWndAddPrinterParent &&
  330. (pCreateWnd->lpcs->style & WS_POPUP) != 0)
  331. {
  332. UnhookWindowsHookEx(m_hAddPrinterHook);
  333. m_hAddPrinterHook = NULL;
  334. Attach(hwndNew);
  335. }
  336. }
  337. return lResult;
  338. }
  339. /////////////////////////////////////////////////////////////////////////////
  340. // ConnectToNetworkPrinter
  341. BOOL ConnectToNetworkPrinter(HWND hWndOwner, LPCTSTR pszPrinterShare)
  342. {
  343. InitPrinterFunctions();
  344. BOOL bResult;
  345. LPTSTR pszAppendWindowTitle = NULL;
  346. LPTSTR pszPrettyName = FormatShareNameAlloc(pszPrinterShare);
  347. if (pszPrettyName)
  348. {
  349. pszAppendWindowTitle = theApp.FormatStringAlloc(IDS_ADDPRINTER_APPEND, pszPrettyName);
  350. free(pszPrettyName);
  351. }
  352. CAddPrinterHook * paph = new CAddPrinterHook(pszAppendWindowTitle, hWndOwner);
  353. if (pszAppendWindowTitle)
  354. free(pszAppendWindowTitle);
  355. if (_pfnSHInvokePrinterCommand != NULL)
  356. {
  357. // First: Try to call SHInvokePrinterCommand, if available.
  358. // This only works on systems with the IE4 desktop enhancements installed.
  359. bResult = (*_pfnSHInvokePrinterCommand)(hWndOwner, PRINTACTION_NETINSTALL, pszPrinterShare, NULL, TRUE);
  360. }
  361. else if (_pfnPrinterSetup32 != NULL)
  362. {
  363. // Next: Try to call PrinterSetup32, if available.
  364. WORD cch = lstrlen(pszPrinterShare) + 1;
  365. BYTE* pPrinterShare = (BYTE*)malloc(cch);
  366. StrCpy((LPTSTR)pPrinterShare, pszPrinterShare);
  367. bResult = (*_pfnPrinterSetup32)(hWndOwner, MSP_NETPRINTER, cch, pPrinterShare, &cch);
  368. free(pPrinterShare);
  369. }
  370. else if (_pfnSHHelpShortcuts != NULL)
  371. {
  372. // Neither of the above APIs was available.
  373. // Instead, just launch the Add Printer Wizard.
  374. bResult = (*_pfnSHHelpShortcuts)(hWndOwner, _hShell32, "AddPrinter", SW_SHOW);
  375. }
  376. else
  377. {
  378. // Yikes, we can't even launch the Add Printer Wizard!
  379. bResult = FALSE;
  380. }
  381. if (paph)
  382. {
  383. paph->Done(bResult);
  384. paph->Release();
  385. }
  386. return bResult;
  387. }