Source code of Windows XP (NT5)
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.

564 lines
18 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1990 - 1998
  3. All rights reserved
  4. Module Name:
  5. thread.cxx
  6. Abstract:
  7. Contains the worker thread for the Connect To browsing dialog.
  8. This is a secondary thread spun off to call the spooler
  9. enumerate-printer APIs.
  10. EnumPrinters frequently takes a long time to return, especially
  11. across the network, so calling it on a separate thread enables
  12. the window to be painted without delays.
  13. There is a common data structure between the main thread and this
  14. worker thread, pBrowseDlgData, defined in browse.h.
  15. Author:
  16. Created by AndrewBe on 1 Dec 1992
  17. Steve Kiraly (SteveKi) 1 May 1998
  18. Lazar Ivanov (LazarI) Jun-2000 (Win64 fixes)
  19. Environment:
  20. User Mode -Win32
  21. Revision History:
  22. 1 May 1998 moved from winspool.drv to printui.dll
  23. --*/
  24. #include "precomp.hxx"
  25. #pragma hdrstop
  26. #include "browse.hxx"
  27. #include "thread.hxx"
  28. #if DBG
  29. //#define DBG_THREADINFO DBG_INFO
  30. #define DBG_THREADINFO DBG_NONE
  31. #endif
  32. PSAVED_BUFFER_SIZE pFirstSavedBufferSize = NULL;
  33. VOID BrowseThread( PBROWSE_DLG_DATA pBrowseDlgData )
  34. {
  35. DWORD RequestId;
  36. LPTSTR pEnumerateName; /* These may get overwritten before we return */
  37. PVOID pEnumerateObject; /* in the case of BROWSE_THREAD_GET_PRINTER, */
  38. /* so make sure we take a copy. */
  39. for( ; ; )
  40. {
  41. RECEIVE_BROWSE_THREAD_REQUEST( pBrowseDlgData, RequestId,
  42. pEnumerateName, pEnumerateObject );
  43. DBGMSG( DBG_THREADINFO, ( "BrowseThread: Request ID = %d\n", RequestId ) );
  44. switch( pBrowseDlgData->RequestId )
  45. {
  46. case BROWSE_THREAD_ENUM_OBJECTS:
  47. BrowseThreadEnumerate( pBrowseDlgData, (PCONNECTTO_OBJECT )pEnumerateObject, pEnumerateName );
  48. break;
  49. case BROWSE_THREAD_GET_PRINTER:
  50. BrowseThreadGetPrinter( pBrowseDlgData, pEnumerateName, (PPRINTER_INFO_2)pEnumerateObject );
  51. break;
  52. case BROWSE_THREAD_TERMINATE:
  53. BrowseThreadDelete( pBrowseDlgData );
  54. BrowseThreadTerminate( pBrowseDlgData );
  55. ExitThread( 0 );
  56. }
  57. }
  58. }
  59. /* BrowseThreadEnumerate
  60. *
  61. * Called to enumerate objects on a given node.
  62. *
  63. */
  64. VOID BrowseThreadEnumerate( PBROWSE_DLG_DATA pBrowseDlgData, PCONNECTTO_OBJECT pConnectToParent, LPTSTR pParentName )
  65. {
  66. DWORD cEnum;
  67. DWORD Result;
  68. Result = EnumConnectToObjects( pBrowseDlgData, pConnectToParent, pParentName );
  69. DBGMSG( DBG_TRACE, ( "Sending WM_ENUM_OBJECTS_COMPLETE; cEnum == %d\n",
  70. pConnectToParent->cSubObjects ) );
  71. ENTER_CRITICAL( pBrowseDlgData );
  72. cEnum = pConnectToParent->cSubObjects;
  73. SEND_BROWSE_THREAD_REQUEST_COMPLETE( pBrowseDlgData,
  74. WM_ENUM_OBJECTS_COMPLETE,
  75. pConnectToParent,
  76. cEnum );
  77. LEAVE_CRITICAL( pBrowseDlgData );
  78. DBGMSG( DBG_TRACE, ( "Sent WM_ENUM_OBJECTS_COMPLETE; cEnum == %d\n",
  79. pConnectToParent->cSubObjects ) );
  80. }
  81. /*
  82. *
  83. */
  84. VOID BrowseThreadGetPrinter( PBROWSE_DLG_DATA pBrowseDlgData,
  85. LPTSTR pPrinterName,
  86. LPPRINTER_INFO_2 pPrinterInfo )
  87. {
  88. HANDLE hPrinter = NULL;
  89. LPPRINTER_INFO_2 pPrinter = NULL;
  90. DWORD cbPrinter = 0;
  91. DWORD cbNeeded = 0;
  92. BOOL OK = FALSE;
  93. DBGMSG( DBG_TRACE, ( "BrowseThreadGetPrinter %s\n", pPrinterName ) );
  94. if( OpenPrinter( pPrinterName, &hPrinter, NULL ) )
  95. {
  96. /* We don't free up this memory until the thread terminates.
  97. * Just leave it so we can use it the next time we're called,
  98. * increasing the size when necessary:
  99. */
  100. if( cbPrinter = pBrowseDlgData->cbPrinterInfo )
  101. pPrinter = (PPRINTER_INFO_2)AllocSplMem( cbPrinter );
  102. if( pPrinter || !cbPrinter )
  103. {
  104. DBGMSG( DBG_TRACE, ( "GetPrinter( %x, %d, %08x, %x )\n",
  105. hPrinter, 2, pPrinter, cbPrinter ) );
  106. OK = GetPrinter( hPrinter, 2, (LPBYTE)pPrinter,
  107. cbPrinter, &cbNeeded );
  108. DBGMSG( DBG_TRACE, ( "GetPrinter( %x, %d, %08x, %x ) returned %d; cbNeeded = %x\n",
  109. hPrinter, 2, pPrinter, cbPrinter, OK, cbNeeded ) );
  110. if( !OK )
  111. {
  112. if( GetLastError( ) == ERROR_INSUFFICIENT_BUFFER )
  113. {
  114. DBGMSG( DBG_TRACE, ( "ReallocSplMem( %08x, %x, %x )\n",
  115. pPrinter, cbPrinter, cbNeeded ) );
  116. if( pPrinter )
  117. pPrinter = (PPRINTER_INFO_2)ReallocSplMem( pPrinter, cbPrinter, cbNeeded );
  118. else
  119. pPrinter = (PPRINTER_INFO_2)AllocSplMem( cbNeeded );
  120. if( pPrinter )
  121. {
  122. cbPrinter = cbNeeded;
  123. DBGMSG( DBG_TRACE, ( "GetPrinter( %x, %d, %08x, %x )\n",
  124. hPrinter, 2, pPrinter, cbPrinter ) );
  125. OK = GetPrinter( hPrinter, 2, (LPBYTE)pPrinter,
  126. cbPrinter, &cbNeeded );
  127. DBGMSG( DBG_TRACE, ( "GetPrinter( %x, %d, %08x, %x ) returned %d; cbNeeded = %x\n",
  128. hPrinter, 2, pPrinter, cbPrinter, OK, cbNeeded ) );
  129. }
  130. }
  131. }
  132. }
  133. ClosePrinter(hPrinter);
  134. }
  135. else
  136. {
  137. DBGMSG( DBG_WARNING, ( "Couldn't open "TSTR"\n", pPrinterName ) );
  138. }
  139. ENTER_CRITICAL( pBrowseDlgData );
  140. if( pBrowseDlgData->pPrinterInfo )
  141. FreeSplMem( pBrowseDlgData->pPrinterInfo );
  142. pBrowseDlgData->pPrinterInfo = pPrinter;
  143. pBrowseDlgData->cbPrinterInfo = cbPrinter;
  144. LEAVE_CRITICAL( pBrowseDlgData );
  145. SEND_BROWSE_THREAD_REQUEST_COMPLETE( pBrowseDlgData,
  146. ( OK ? WM_GET_PRINTER_COMPLETE
  147. : WM_GET_PRINTER_ERROR ),
  148. pPrinterName,
  149. ( OK ? (ULONG_PTR)pPrinter
  150. : GetLastError( ) ) );
  151. }
  152. /* BrowseThreadDelete
  153. *
  154. * Called to delete objects on a given node.
  155. *
  156. */
  157. VOID BrowseThreadDelete( PBROWSE_DLG_DATA pBrowseDlgData )
  158. {
  159. PCONNECTTO_OBJECT pConnectToParent;
  160. DWORD ObjectsDeleted;
  161. DBGMSG( DBG_TRACE, ( "BrowseThreadDelete\n" ) );
  162. pConnectToParent = pBrowseDlgData->pConnectToData;
  163. if( pConnectToParent )
  164. {
  165. ENTER_CRITICAL( pBrowseDlgData );
  166. ObjectsDeleted = FreeConnectToObjects( &pConnectToParent->pSubObject[0],
  167. pConnectToParent->cSubObjects,
  168. pConnectToParent->cbPrinterInfo );
  169. pConnectToParent->pSubObject = NULL;
  170. pConnectToParent->cSubObjects = 0;
  171. pConnectToParent->cbPrinterInfo = 0;
  172. LEAVE_CRITICAL( pBrowseDlgData );
  173. }
  174. }
  175. /* BrowseThreadTerminate
  176. *
  177. * Frees up the top-level connect-to object, deletes the critical section,
  178. * and closes the semaphore, then frees the dialog data.
  179. * Note, if there are remaining enumerated objects below the top-level
  180. * object, they should have been freed by BrowseThreadDelete.
  181. */
  182. VOID BrowseThreadTerminate( PBROWSE_DLG_DATA pBrowseDlgData )
  183. {
  184. DBGMSG( DBG_TRACE, ( "BrowseThreadTerminate\n" ) );
  185. pBrowseDlgData->cDecRef();
  186. }
  187. /* EnumConnectToObjects
  188. *
  189. * Calls GetPrinterInfo (which in turn calls EnumPrinters) on the requested
  190. * parent node. It allocates an initial buffer and sets up the subobject
  191. * fields in the supplied CONNECTTO_OBJECT structure.
  192. *
  193. * Arguments:
  194. *
  195. * pParentName - The object on which the enumeration is to be performed.
  196. * This may be a domain or server name, depending upon the proprietory
  197. * network implementation. If this value is NULL, this indicates
  198. * that the top-level objects should be enumerated.
  199. *
  200. * pConnectToParent - A pointer to a CONNECTTO_OBJECT for the parent node.
  201. * The subobject and cbPrinterInfo fields will be filled in
  202. * by this function.
  203. *
  204. * Return:
  205. *
  206. * FALSE if an error occurred, otherwise TRUE.
  207. *
  208. * Author:
  209. *
  210. * andrewbe, July 1992
  211. *
  212. */
  213. DWORD EnumConnectToObjects( PBROWSE_DLG_DATA pBrowseDlgData, PCONNECTTO_OBJECT pConnectToParent, LPTSTR pParentName )
  214. {
  215. DWORD i, j, cReturned;
  216. DWORD cbNeeded;
  217. DWORD cbPrinter;
  218. LPPRINTER_INFO_1 pPrinter;
  219. LPPRINTER_INFO_1 pOrderPrinter;
  220. PCONNECTTO_OBJECT pConnectToChildren;
  221. BOOL Success = FALSE;
  222. DWORD Error = 0;
  223. cbPrinter = GetSavedBufferSize( pParentName, NULL );
  224. /* Allocate a buffer that will probably be big enough to hold
  225. * all the information we'll need.
  226. * This is so that GetPrinterInfo doesn't have to call EnumPrinters twice.
  227. */
  228. pPrinter = (PPRINTER_INFO_1)AllocSplMem( cbPrinter );
  229. if( pPrinter )
  230. {
  231. pPrinter = (LPPRINTER_INFO_1)GetPrinterInfo( PRINTER_ENUM_NAME | PRINTER_ENUM_REMOTE,
  232. pParentName, 1,
  233. (LPBYTE)pPrinter, &cbPrinter,
  234. &cReturned, &cbNeeded, &Error );
  235. if( pPrinter )
  236. {
  237. /* Allocate an array of CONNECTTO_OBJECTs, one for each object returned:
  238. */
  239. if( cReturned > 0 )
  240. {
  241. pConnectToChildren = (PCONNECTTO_OBJECT)AllocSplMem( cReturned * sizeof( CONNECTTO_OBJECT ) );
  242. SaveBufferSize( pParentName, cbPrinter );
  243. }
  244. else
  245. {
  246. FreeSplMem( pPrinter );
  247. cbPrinter = 0;
  248. pConnectToChildren = EMPTY_CONTAINER;
  249. }
  250. if( pConnectToChildren && ( pConnectToChildren != EMPTY_CONTAINER ) )
  251. {
  252. /*
  253. * Allocate a buffer to sort the printer info entries. Once ordered
  254. * copy the entries back to original area and free the buffer.
  255. * *
  256. */
  257. pOrderPrinter = (PPRINTER_INFO_1)AllocSplMem( cReturned * sizeof( PRINTER_INFO_1 ) );
  258. if( pOrderPrinter ) {
  259. pOrderPrinter[0] = pPrinter[0]; /* Copy 1st printer info */
  260. for( i = 1; i < cReturned; i++ )
  261. {
  262. for ( j = 0; j< i; j++ )
  263. {
  264. if ( _wcsicmp(pPrinter[i].pDescription, pOrderPrinter[j].pDescription) < 0 )
  265. {
  266. memmove(&pOrderPrinter[j+1], &pOrderPrinter[j], sizeof( PRINTER_INFO_1 ) * (i-j));
  267. break;
  268. }
  269. }
  270. pOrderPrinter[j] = pPrinter[i];
  271. }
  272. memmove(pPrinter, pOrderPrinter, cReturned * sizeof( PRINTER_INFO_1 ));
  273. FreeSplMem( pOrderPrinter );
  274. }
  275. /*
  276. * Build ConnectTo array
  277. */
  278. for( i = 0; i < cReturned; i++ )
  279. {
  280. pConnectToChildren[i].pPrinterInfo = &pPrinter[i];
  281. pConnectToChildren[i].pSubObject = NULL;
  282. pConnectToChildren[i].cSubObjects = 0;
  283. pConnectToChildren[i].cbPrinterInfo = 0;
  284. }
  285. ENTER_CRITICAL( pBrowseDlgData );
  286. pConnectToParent->pSubObject = pConnectToChildren;
  287. pConnectToParent->cSubObjects = cReturned;
  288. pConnectToParent->cbPrinterInfo = cbPrinter;
  289. LEAVE_CRITICAL( pBrowseDlgData );
  290. Success = TRUE;
  291. }
  292. }
  293. }
  294. SetCursor( pBrowseDlgData->hcursorWait );
  295. return Error;
  296. }
  297. /* GetPrinterInfo
  298. *
  299. * Calls EnumPrinters using the supplied parameters.
  300. * If the buffer is not big enough, it is reallocated,
  301. * and a second attempt is made.
  302. *
  303. * Returns a pointer to the buffer of printer info.
  304. *
  305. * pPrinters may be NULL, in which case *pcbPrinters must equal 0.
  306. *
  307. * andrewbe, April 1992
  308. */
  309. #define MAX_RETRIES 5 /* How many times we retry if we get
  310. ERROR_INSUFFICIENT_BUFFER */
  311. LPBYTE GetPrinterInfo( IN DWORD Flags,
  312. IN LPTSTR Name,
  313. IN DWORD Level,
  314. IN LPBYTE pPrinters,
  315. OUT LPDWORD pcbPrinters,
  316. OUT LPDWORD pcReturned,
  317. OUT LPDWORD pcbNeeded OPTIONAL,
  318. OUT LPDWORD pError OPTIONAL )
  319. {
  320. DWORD cbCurrent;
  321. BOOL rc;
  322. DWORD cbNeeded;
  323. DWORD Error = 0;
  324. DWORD Retry;
  325. /* cbCurrent holds our current buffer size.
  326. * This will change if we have to realloc:
  327. */
  328. cbCurrent = *pcbPrinters;
  329. DBGMSG( DBG_TRACE, ( "Calling EnumPrinters( %08x, "TSTR", %d, %08x, 0x%x )\n",
  330. Flags, ( Name ? Name : TEXT("NULL") ), Level, pPrinters, cbCurrent ) );
  331. rc = EnumPrinters( Flags, Name, Level, pPrinters, cbCurrent,
  332. &cbNeeded, pcReturned );
  333. DBGMSG( DBG_TRACE, ( "EnumPrinters( %08x, "TSTR", %d, %08x, 0x%x ) returned %d; cbNeeded 0x%x; cReturned 0x%x\n",
  334. Flags, ( Name ? Name : TEXT("NULL") ), Level, pPrinters, cbCurrent,
  335. rc, cbNeeded, *pcReturned ) );
  336. Retry = 1;
  337. while (!rc) {
  338. Error = GetLastError( );
  339. if ( Error != ERROR_INSUFFICIENT_BUFFER ||
  340. Retry > MAX_RETRIES ) {
  341. break;
  342. }
  343. /* Hopefully the error will be buffer not big enough.
  344. * If not, we'll have to bomb out:
  345. */
  346. /* The problem here is that, the second time we call EnumPrinters,
  347. * the size of buffer we need may have increased because new devices
  348. * have come on line, or the server that provided the list of objects
  349. * has updated itself. In this case, we should try again,
  350. * but don't keep trying indefinitely, because something might be amiss.
  351. */
  352. DBGMSG( DBG_THREADINFO, ( "EnumPrinters failed with ERROR_INSUFFICIENT_BUFFER; buffer size = %d\nRetry #%d with buffer size = %d\n",
  353. cbCurrent, Retry, cbNeeded ) );
  354. if( cbCurrent != 0 )
  355. FreeSplMem(pPrinters);
  356. pPrinters = (PBYTE)AllocSplMem( cbNeeded );
  357. if( pPrinters )
  358. {
  359. cbCurrent = cbNeeded;
  360. DBGMSG( DBG_THREADINFO, ( "Calling EnumPrinters( %08x, "TSTR", %d, %08x, 0x%x )\n",
  361. Flags, ( Name ? Name : TEXT("NULL") ), Level, pPrinters, cbCurrent ) );
  362. rc = EnumPrinters( Flags, Name, Level, pPrinters, cbCurrent,
  363. &cbNeeded, pcReturned );
  364. DBGMSG( DBG_THREADINFO, ( "EnumPrinters( %08x, "TSTR", %d, %08x, 0x%x )\nreturned %d; cbNeeded 0x%x; cReturned 0x%x\n",
  365. Flags, ( Name ? Name : TEXT("NULL") ), Level, pPrinters, cbCurrent,
  366. rc, cbNeeded, *pcReturned ) );
  367. }
  368. Retry++;
  369. }
  370. if( !rc )
  371. {
  372. DBGMSG( DBG_WARNING, ( "EnumPrinters failed: Error %d\n", Error ) );
  373. if( cbCurrent != 0 )
  374. FreeSplMem(pPrinters);
  375. pPrinters = NULL;
  376. cbCurrent = 0;
  377. *pcReturned = 0;
  378. Error = GetLastError( );
  379. }
  380. *pcbPrinters = cbCurrent;
  381. if( pError )
  382. *pError = Error;
  383. return pPrinters;
  384. }
  385. DWORD GetSavedBufferSize( LPTSTR pName,
  386. PSAVED_BUFFER_SIZE *ppSavedBufferSize OPTIONAL )
  387. {
  388. PSAVED_BUFFER_SIZE pSavedBufferSize;
  389. if( !pName )
  390. pName = TEXT("");
  391. pSavedBufferSize = pFirstSavedBufferSize;
  392. while( pSavedBufferSize )
  393. {
  394. if( !_tcscmp( pSavedBufferSize->pName, pName ) )
  395. {
  396. if( ppSavedBufferSize )
  397. *ppSavedBufferSize = pSavedBufferSize;
  398. return pSavedBufferSize->Size;
  399. }
  400. pSavedBufferSize = pSavedBufferSize->pNext;
  401. }
  402. if( ppSavedBufferSize )
  403. *ppSavedBufferSize = NULL;
  404. return 0;
  405. }
  406. VOID SaveBufferSize( LPTSTR pName, DWORD Size )
  407. {
  408. PSAVED_BUFFER_SIZE pSavedBufferSize;
  409. if( !pName )
  410. pName = TEXT("");
  411. if( GetSavedBufferSize( pName, &pSavedBufferSize ) )
  412. {
  413. if( pSavedBufferSize->Size < Size )
  414. {
  415. DBGMSG( DBG_TRACE, ( "Updating buffer size for "TSTR" from %d (0x%x) to %d (0x%x)\n",
  416. ( pName ? pName : TEXT("NULL") ), pSavedBufferSize->Size,
  417. pSavedBufferSize->Size, Size, Size ) );
  418. pSavedBufferSize->Size = Size;
  419. }
  420. }
  421. else
  422. {
  423. DBGMSG( DBG_TRACE, ( "Saving buffer size %d (0x%x) for "TSTR"\n",
  424. Size, Size, ( pName ? pName : TEXT("NULL") ) ) );
  425. if( pSavedBufferSize = (PSAVED_BUFFER_SIZE)AllocSplMem( sizeof( SAVED_BUFFER_SIZE ) ) )
  426. {
  427. pSavedBufferSize->pName = AllocSplStr( pName );
  428. pSavedBufferSize->Size = Size;
  429. pSavedBufferSize->pNext = pFirstSavedBufferSize;
  430. pFirstSavedBufferSize = pSavedBufferSize;
  431. }
  432. }
  433. }