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.

2531 lines
55 KiB

  1. #include "stdafx.h"
  2. #include "resource.h"
  3. #include <process.h>
  4. #include "bar.h"
  5. #include "pbrush.h"
  6. #include "imaging.h"
  7. //////////////////////////////////////////////////////////////////////////
  8. //
  9. // Trace
  10. //
  11. #ifdef DBG
  12. void AFX_CDECL Trace(PCTSTR pszFormat, ...)
  13. {
  14. va_list argList;
  15. va_start(argList, pszFormat);
  16. CString strMessage;
  17. strMessage.FormatV(pszFormat, argList);
  18. OutputDebugString(strMessage);
  19. va_end(argList);
  20. }
  21. #else //DBG
  22. inline void AFX_CDECL Trace(PCTSTR pszFormat, ...)
  23. {
  24. }
  25. #endif DBG
  26. //////////////////////////////////////////////////////////////////////////
  27. //
  28. //
  29. //
  30. CImagingMgr::~CImagingMgr()
  31. {
  32. }
  33. //////////////////////////////////////////////////////////////////////////
  34. //
  35. //
  36. //
  37. CWIAMgr::CWIAMgr()
  38. {
  39. HRESULT hr;
  40. m_pEventCallback = new CEventCallback();
  41. if (m_pEventCallback)
  42. {
  43. hr = m_pEventCallback->Register();
  44. if (hr != S_OK)
  45. {
  46. m_pEventCallback.Release();
  47. }
  48. }
  49. }
  50. //////////////////////////////////////////////////////////////////////////
  51. //
  52. //
  53. //
  54. HRESULT
  55. CWIAMgr::SelectSource(
  56. HWND hWndParent,
  57. LONG lFlags
  58. )
  59. {
  60. HRESULT hr = S_FALSE;
  61. // Create a connection to the local WIA device manager
  62. CComPtr<IWiaDevMgr> pWiaDevMgr;
  63. hr = pWiaDevMgr.CoCreateInstance(CLSID_WiaDevMgr);
  64. if (!SUCCEEDED(hr))
  65. {
  66. return hr;
  67. }
  68. // clear the current selection (if any)
  69. m_bstrDeviceID.Empty();
  70. // display the device selection dialog
  71. hr = pWiaDevMgr->SelectDeviceDlgID(
  72. hWndParent,
  73. StiDeviceTypeDefault,
  74. lFlags,
  75. &m_bstrDeviceID
  76. );
  77. theApp.RestoreWaitCursor();
  78. if (hr != S_OK)
  79. {
  80. Trace(_T("SelectDeviceDlgID HRESULT=%08x\n"), hr);
  81. return hr;
  82. }
  83. return S_OK;
  84. }
  85. //////////////////////////////////////////////////////////////////////////
  86. //
  87. //
  88. //
  89. HRESULT
  90. CWIAMgr::Select(
  91. LPCTSTR pDeviceId
  92. )
  93. {
  94. m_bstrDeviceID = pDeviceId;
  95. return S_OK;
  96. }
  97. //////////////////////////////////////////////////////////////////////////
  98. //
  99. //
  100. //
  101. HRESULT ReadPropertyLong(IWiaItem *pWiaItem, PROPID propid, LONG *lResult)
  102. {
  103. if (!lResult)
  104. {
  105. return E_POINTER;
  106. }
  107. HRESULT hr = S_FALSE;
  108. CComQIPtr<IWiaPropertyStorage> pWiaPropertyStorage(pWiaItem);
  109. if (pWiaPropertyStorage == 0)
  110. {
  111. return E_NOINTERFACE;
  112. }
  113. PROPSPEC PropSpec;
  114. PropSpec.ulKind = PRSPEC_PROPID;
  115. PropSpec.propid = propid;
  116. PROPVARIANT PropVariant;
  117. PropVariantInit(&PropVariant);
  118. hr = pWiaPropertyStorage->ReadMultiple(1, &PropSpec, &PropVariant);
  119. if (hr != S_OK)
  120. {
  121. Trace(_T("ReadMultiple HRESULT=%08x\n"), hr);
  122. return hr;
  123. }
  124. switch (PropVariant.vt)
  125. {
  126. case VT_I1: *lResult = (LONG) PropVariant.cVal; break;
  127. case VT_UI1: *lResult = (LONG) PropVariant.bVal; break;
  128. case VT_I2: *lResult = (LONG) PropVariant.iVal; break;
  129. case VT_UI2: *lResult = (LONG) PropVariant.uiVal; break;
  130. case VT_I4: *lResult = (LONG) PropVariant.lVal; break;
  131. case VT_UI4: *lResult = (LONG) PropVariant.ulVal; break;
  132. case VT_INT: *lResult = (LONG) PropVariant.intVal; break;
  133. case VT_UINT: *lResult = (LONG) PropVariant.uintVal; break;
  134. case VT_R4: *lResult = (LONG) (PropVariant.fltVal + 0.5); break;
  135. case VT_R8: *lResult = (LONG) (PropVariant.dblVal + 0.5); break;
  136. default: hr = S_FALSE; break;
  137. }
  138. PropVariantClear(&PropVariant);
  139. return hr;
  140. }
  141. //////////////////////////////////////////////////////////////////////////
  142. //
  143. //
  144. //
  145. HRESULT
  146. CWIAMgr::Acquire(
  147. HWND hWndParent,
  148. HGLOBAL *phDib
  149. )
  150. {
  151. ASSERT(phDib != 0);
  152. HRESULT hr;
  153. // Create a connection to the local WIA device manager
  154. CComPtr<IWiaDevMgr> pWiaDevMgr;
  155. hr = pWiaDevMgr.CoCreateInstance(CLSID_WiaDevMgr);
  156. if (!SUCCEEDED(hr))
  157. {
  158. return hr;
  159. }
  160. // Create the device object.
  161. // Select the device first if
  162. // no device has been selected before or
  163. // we fail to create the device with the selected ID
  164. CComPtr<IWiaItem> pRootItem;
  165. if (!m_bstrDeviceID ||
  166. !SUCCEEDED(pWiaDevMgr->CreateDevice(m_bstrDeviceID, &pRootItem)))
  167. {
  168. // clear the current selection (if any)
  169. m_bstrDeviceID.Empty();
  170. // display the device selection dialog
  171. hr = pWiaDevMgr->SelectDeviceDlg(
  172. hWndParent,
  173. StiDeviceTypeDefault,
  174. 0,
  175. &m_bstrDeviceID,
  176. &pRootItem
  177. );
  178. theApp.RestoreWaitCursor();
  179. if (hr != S_OK)
  180. {
  181. Trace(_T("SelectDeviceDlg HRESULT=%08x\n"), hr);
  182. return hr;
  183. }
  184. #ifndef USE_SELECTSOURCE_MENUITEM
  185. // forget the current selection
  186. m_bstrDeviceID.Empty();
  187. #endif //!USE_SELECTSOURCE_MENUITEM
  188. if (!SUCCEEDED(hr))
  189. {
  190. Trace(_T("CreateDevice HRESULT=%08x\n"), hr);
  191. return hr;
  192. }
  193. }
  194. // display the image selection dialog and let the user
  195. // select the item to be transferred
  196. CComPtrArray<IWiaItem> ppIWiaItem;
  197. hr = pRootItem->DeviceDlg(
  198. hWndParent,
  199. WIA_DEVICE_DIALOG_SINGLE_IMAGE,
  200. WIA_INTENT_NONE,
  201. &ppIWiaItem.ItemCount(),
  202. &ppIWiaItem
  203. );
  204. theApp.RestoreWaitCursor();
  205. if (hr != S_OK)
  206. {
  207. Trace(_T("DeviceDlg HRESULT=%08x\n"), hr);
  208. return hr;
  209. }
  210. if (ppIWiaItem.ItemCount() == 0)
  211. {
  212. Trace(_T("DeviceDlg returned 0 items\n"));
  213. return E_FAIL;
  214. }
  215. // set the image transfer properties; we want a DIB memory transfer
  216. TYMED tymed = (TYMED) TYMED_CALLBACK;
  217. GUID guidFormat = WiaImgFmt_MEMORYBMP;
  218. PROPSPEC PropSpec[2] = { 0 };
  219. PROPVARIANT PropVariant[2] = { 0 };
  220. PropSpec[0].ulKind = PRSPEC_PROPID;
  221. PropSpec[0].propid = WIA_IPA_TYMED;
  222. PropVariant[0].vt = VT_I4;
  223. PropVariant[0].lVal = tymed;
  224. PropSpec[1].ulKind = PRSPEC_PROPID;
  225. PropSpec[1].propid = WIA_IPA_FORMAT;
  226. PropVariant[1].vt = VT_CLSID;
  227. PropVariant[1].puuid = &guidFormat;
  228. CComQIPtr<IWiaPropertyStorage> pWiaPropertyStorage(ppIWiaItem[0]);
  229. if (pWiaPropertyStorage == 0)
  230. {
  231. return E_NOINTERFACE;
  232. }
  233. hr = pWiaPropertyStorage->WriteMultiple(
  234. 1,
  235. &(PropSpec[0]),
  236. &(PropVariant[0]),
  237. WIA_IPA_FIRST
  238. );
  239. if (hr != S_OK)
  240. {
  241. Trace(_T("WriteMultiple HRESULT=%08x\n"), hr);
  242. return hr;
  243. }
  244. hr = pWiaPropertyStorage->WriteMultiple(
  245. 1,
  246. &(PropSpec[1]),
  247. &(PropVariant[1]),
  248. WIA_IPA_FIRST
  249. );
  250. if (hr != S_OK)
  251. {
  252. Trace(_T("WriteMultiple HRESULT=%08x\n"), hr);
  253. return hr;
  254. }
  255. // now, determine the transfer buffer size
  256. // 64k transfer size and double buffering seem to work fine;
  257. // a smaller buffer considerably slows down the memory transfer
  258. // and a larger buffer doesn't give much speed increase.
  259. // If the device minimum is larger than 64k though, use that size...
  260. LONG lBufferSize;
  261. hr = ReadPropertyLong(ppIWiaItem[0], WIA_IPA_MIN_BUFFER_SIZE, &lBufferSize);
  262. if (hr != S_OK || lBufferSize < 64*1024)
  263. {
  264. lBufferSize = 64*1024;
  265. }
  266. // setup the progress dialog
  267. CComPtr<IWiaProgressDialog> pProgress;
  268. hr = CoCreateInstance(
  269. CLSID_WiaDefaultUi,
  270. 0,
  271. CLSCTX_INPROC_SERVER,
  272. IID_IWiaProgressDialog,
  273. (void**) &pProgress
  274. );
  275. if (hr != S_OK)
  276. {
  277. pProgress = new CProgressDialog;
  278. }
  279. LONG nDeviceType;
  280. hr = ReadPropertyLong(pRootItem, WIA_DIP_DEV_TYPE, &nDeviceType);
  281. if (hr != S_OK)
  282. {
  283. nDeviceType = 0;
  284. }
  285. LONG lAnimFlag;
  286. switch (GET_STIDEVICE_TYPE(nDeviceType))
  287. {
  288. case StiDeviceTypeScanner:
  289. lAnimFlag = WIA_PROGRESSDLG_ANIM_SCANNER_ACQUIRE;
  290. break;
  291. case StiDeviceTypeDigitalCamera:
  292. lAnimFlag = WIA_PROGRESSDLG_ANIM_CAMERA_ACQUIRE;
  293. break;
  294. case StiDeviceTypeStreamingVideo:
  295. lAnimFlag = WIA_PROGRESSDLG_ANIM_VIDEO_ACQUIRE;
  296. break;
  297. default:
  298. lAnimFlag = WIA_PROGRESSDLG_NO_ANIM;
  299. break;
  300. }
  301. pProgress->Create(hWndParent, lAnimFlag);
  302. CString strDownloading;
  303. strDownloading.LoadString(IDS_DOWNLOAD_IMAGE);
  304. USES_CONVERSION;
  305. pProgress->SetTitle(T2CW(strDownloading));
  306. pProgress->SetMessage(L"");
  307. pProgress->Show();
  308. // init the data callback interface
  309. CDataCallback *pDataCallback = new CDataCallback(pProgress);
  310. if (!pDataCallback)
  311. {
  312. theApp.SetMemoryEmergency(TRUE);
  313. return E_OUTOFMEMORY;
  314. }
  315. CComQIPtr<IWiaDataCallback> pIWiaDataCallback(pDataCallback);
  316. ASSERT(pIWiaDataCallback != 0);
  317. // initiate the transfer
  318. CComQIPtr<IWiaDataTransfer> pIWiaDataTransfer(ppIWiaItem[0]);
  319. if (pIWiaDataTransfer == 0)
  320. {
  321. return E_NOINTERFACE;
  322. }
  323. WIA_DATA_TRANSFER_INFO WiaDataTransferInfo = { 0 };
  324. WiaDataTransferInfo.ulSize = sizeof(WIA_DATA_TRANSFER_INFO);
  325. WiaDataTransferInfo.ulBufferSize = 2 * lBufferSize;
  326. WiaDataTransferInfo.bDoubleBuffer = TRUE;
  327. // This *easy* solution will cause the mspaint UI to freeze during
  328. // image transfer; this is possibly too long time to remain frozen.
  329. // So we will create a worker thread to do the data transfer.
  330. //
  331. //hr = pIWiaDataTransfer->idtGetBandedData(
  332. // &WiaDataTransferInfo,
  333. // pIWiaDataCallback
  334. //);
  335. EnableWindow(hWndParent, FALSE);
  336. hr = GetBandedData(CGetBandedDataThreadData(
  337. pIWiaDataTransfer,
  338. &WiaDataTransferInfo,
  339. pIWiaDataCallback
  340. ));
  341. EnableWindow(hWndParent, TRUE);
  342. // check if the user has pressed cancel
  343. if (pProgress)
  344. {
  345. BOOL bCancelled;
  346. if (pProgress->Cancelled(&bCancelled) == S_OK && bCancelled)
  347. {
  348. hr = S_FALSE;
  349. }
  350. pProgress->Destroy();
  351. }
  352. if (hr != S_OK)
  353. {
  354. Trace(_T("idtGetBandedData HRESULT=%08x\n"), hr);
  355. return hr;
  356. }
  357. // return the results
  358. pDataCallback->PrintTimes();
  359. *phDib = pDataCallback->GetBuffer();
  360. return S_OK;
  361. }
  362. //////////////////////////////////////////////////////////////////////////
  363. //
  364. //
  365. //
  366. CWIAMgr::CGetBandedDataThreadData::CGetBandedDataThreadData(
  367. IWiaDataTransfer *pIWiaDataTransfer,
  368. WIA_DATA_TRANSFER_INFO *pWiaDataTransferInfo,
  369. IWiaDataCallback *pIWiaDataCallback
  370. ) :
  371. m_pIWiaDataTransfer(pIWiaDataTransfer),
  372. m_pWiaDataTransferInfo(pWiaDataTransferInfo),
  373. m_pIWiaDataCallback(pIWiaDataCallback)
  374. {
  375. }
  376. //////////////////////////////////////////////////////////////////////////
  377. //
  378. //
  379. //
  380. HRESULT CWIAMgr::CGetBandedDataThreadData::Marshal()
  381. {
  382. HRESULT hr;
  383. // marshal the IWiaDataTransfer interface
  384. ASSERT(m_pIWiaDataTransfer != 0);
  385. hr = CoMarshalInterThreadInterfaceInStream(
  386. IID_IWiaDataTransfer,
  387. m_pIWiaDataTransfer,
  388. &m_pIWiaDataTransferStream
  389. );
  390. if (hr != S_OK)
  391. {
  392. Trace(_T("CoMarshalInterThreadInterfaceInStream HRESULT=%08x\n"), hr);
  393. return hr;
  394. }
  395. m_pIWiaDataTransfer.Release();
  396. // marshal the IWiaDataCallback interface
  397. ASSERT(m_pIWiaDataCallback != 0);
  398. hr = CoMarshalInterThreadInterfaceInStream(
  399. IID_IWiaDataCallback,
  400. m_pIWiaDataCallback,
  401. &m_pIWiaDataCallbackStream
  402. );
  403. if (hr != S_OK)
  404. {
  405. Trace(_T("CoMarshalInterThreadInterfaceInStream HRESULT=%08x\n"), hr);
  406. return hr;
  407. }
  408. m_pIWiaDataCallback.Release();
  409. return hr;
  410. }
  411. //////////////////////////////////////////////////////////////////////////
  412. //
  413. //
  414. //
  415. HRESULT CWIAMgr::CGetBandedDataThreadData::Unmarshal()
  416. {
  417. HRESULT hr;
  418. // unmarshal the IWiaDataTransfer interface
  419. ASSERT(m_pIWiaDataTransferStream != 0);
  420. hr = CoGetInterfaceAndReleaseStream(
  421. m_pIWiaDataTransferStream,
  422. IID_IWiaDataTransfer,
  423. (void **) &m_pIWiaDataTransfer
  424. );
  425. // CoGetInterfaceAndReleaseStream should already have
  426. // released the stream pointer, so set it to zero so that
  427. // ~CGetBandedDataThreadData will not try to release it again
  428. m_pIWiaDataTransferStream.Detach();
  429. if (hr != S_OK)
  430. {
  431. Trace(_T("CoGetInterfaceAndReleaseStream HRESULT=%08x\n"), hr);
  432. return hr;
  433. }
  434. // unmarshal the IWiaDataCallback interface
  435. ASSERT(m_pIWiaDataCallbackStream != 0);
  436. hr = CoGetInterfaceAndReleaseStream(
  437. m_pIWiaDataCallbackStream,
  438. IID_IWiaDataCallback,
  439. (void **) &m_pIWiaDataCallback
  440. );
  441. m_pIWiaDataCallbackStream.Detach();
  442. if (hr != S_OK)
  443. {
  444. Trace(_T("CoGetInterfaceAndReleaseStream HRESULT=%08x\n"), hr);
  445. return hr;
  446. }
  447. return hr;
  448. }
  449. //////////////////////////////////////////////////////////////////////////
  450. //
  451. //
  452. //
  453. HRESULT CWIAMgr::GetBandedData(CGetBandedDataThreadData &ThreadData)
  454. {
  455. // marshal the interface pointers before passing them to another thread
  456. HRESULT hr = ThreadData.Marshal();
  457. if (hr != S_OK)
  458. {
  459. return hr;
  460. }
  461. // fire up the new thread
  462. unsigned nThreadId;
  463. HANDLE hThread = (HANDLE) _beginthreadex(
  464. 0,
  465. 0,
  466. GetBandedDataThread,
  467. &ThreadData,
  468. 0,
  469. &nThreadId
  470. );
  471. if (hThread == 0)
  472. {
  473. Trace(_T("CreateThread LastError=%08x\n"), GetLastError());
  474. return HRESULT_FROM_WIN32(GetLastError());
  475. }
  476. // enter a msg loop while waiting for the thread to complete;
  477. // this will keep the mspaint UI alive
  478. while (MsgWaitForMultipleObjects(1, &hThread, FALSE, INFINITE, QS_ALLINPUT) == WAIT_OBJECT_0+1)
  479. {
  480. MSG msg;
  481. while (PeekMessage(&msg, 0, 0, 0, PM_REMOVE))
  482. {
  483. TranslateMessage(&msg);
  484. DispatchMessage(&msg);
  485. }
  486. theApp.RestoreWaitCursor();
  487. }
  488. // if we reach here, the thread must have ended; get the result
  489. DWORD dwExitCode = S_FALSE;
  490. GetExitCodeThread(hThread, &dwExitCode);
  491. // in case the thread is still alive (shouldn't happen), kill it
  492. if (dwExitCode == STILL_ACTIVE)
  493. {
  494. TerminateThread(hThread, 0);
  495. dwExitCode = E_FAIL;
  496. }
  497. CloseHandle(hThread);
  498. ASSERT(sizeof(DWORD) >= sizeof(HRESULT));
  499. return (HRESULT) dwExitCode;
  500. }
  501. //////////////////////////////////////////////////////////////////////////
  502. //
  503. //
  504. //
  505. unsigned WINAPI CWIAMgr::GetBandedDataThread(PVOID pVoid)
  506. {
  507. // Init COM for this thread
  508. HRESULT hr = CoInitialize(0);
  509. if (hr != S_OK)
  510. {
  511. Trace(_T("CoInitialize HRESULT=%08x\n"), hr);
  512. return (unsigned) hr;
  513. }
  514. CGetBandedDataThreadData *pThreadData = (CGetBandedDataThreadData *) pVoid;
  515. ASSERT(pThreadData != 0);
  516. if (pThreadData != 0)
  517. {
  518. // unmarshal the interface pointers before calling idtGetBandedData
  519. hr = pThreadData->Unmarshal();
  520. if (hr == S_OK)
  521. {
  522. hr = pThreadData->m_pIWiaDataTransfer->idtGetBandedData(
  523. pThreadData->m_pWiaDataTransferInfo,
  524. pThreadData->m_pIWiaDataCallback
  525. );
  526. }
  527. }
  528. CoUninitialize();
  529. ASSERT(sizeof(unsigned) >= sizeof(HRESULT));
  530. return (unsigned) hr;
  531. }
  532. //////////////////////////////////////////////////////////////////////////
  533. //
  534. //
  535. //
  536. int CWIAMgr::NumDevices(HWND /*hWndParent*/)
  537. {
  538. return m_pEventCallback ? m_pEventCallback->GetNumDevices() : 0;
  539. }
  540. #ifdef USE_TWAIN
  541. //////////////////////////////////////////////////////////////////////////
  542. //
  543. //
  544. //
  545. CTwainMgr::CTwainMgr()
  546. {
  547. m_TwainState = State_1_Pre_Session;
  548. // fill in the m_AppId struct with defaults
  549. m_AppId.Id = 0;
  550. m_AppId.Version.MajorNum = 1;
  551. m_AppId.Version.MinorNum = 0;
  552. m_AppId.Version.Language = TWLG_USA;
  553. m_AppId.Version.Country = TWCY_USA;
  554. strcpy(m_AppId.Version.Info, "FileDescription");
  555. m_AppId.ProtocolMajor = TWON_PROTOCOLMAJOR;
  556. m_AppId.ProtocolMinor = TWON_PROTOCOLMINOR;
  557. m_AppId.SupportedGroups = DG_IMAGE | DG_CONTROL;
  558. strcpy(m_AppId.Manufacturer, "CompanyName");
  559. strcpy(m_AppId.ProductFamily, "ProductVersion");
  560. strcpy(m_AppId.ProductName, "ProductName");
  561. // reset m_SrcId
  562. m_SrcId.Id = 0;
  563. m_SrcId.ProductName[0] = '\0';
  564. // Load TWAIN DLL
  565. m_hTwainDll = LoadLibrary(_T("TWAIN_32.DLL"));
  566. if (m_hTwainDll)
  567. {
  568. // Get the entry point
  569. m_DSM_Entry = (DSMENTRYPROC) GetProcAddress(m_hTwainDll, "DSM_Entry");
  570. if (m_DSM_Entry)
  571. {
  572. m_TwainState = State_2_Source_Manager_Loaded;
  573. }
  574. }
  575. }
  576. //////////////////////////////////////////////////////////////////////////
  577. //
  578. //
  579. //
  580. CTwainMgr::~CTwainMgr()
  581. {
  582. // Free the library if loaded
  583. if (m_TwainState >= State_1_Pre_Session)
  584. {
  585. FreeLibrary(m_hTwainDll);
  586. }
  587. }
  588. //////////////////////////////////////////////////////////////////////////
  589. //
  590. //
  591. //
  592. HRESULT
  593. CTwainMgr::SelectSource(
  594. HWND hWndParent,
  595. LONG /*lFlags*/
  596. )
  597. {
  598. HRESULT hr = S_FALSE;
  599. TW_UINT16 rc = TWRC_FAILURE;
  600. if (m_TwainState >= State_2_Source_Manager_Loaded)
  601. {
  602. __try
  603. {
  604. if (m_TwainState == State_2_Source_Manager_Loaded)
  605. {
  606. // Open the data source manager
  607. rc = m_DSM_Entry(
  608. &m_AppId,
  609. 0,
  610. DG_CONTROL,
  611. DAT_PARENT,
  612. MSG_OPENDSM,
  613. (TW_MEMREF) &hWndParent
  614. );
  615. if (rc != TWRC_SUCCESS)
  616. {
  617. __leave;
  618. }
  619. m_TwainState = State_3_Source_Manager_Open;
  620. }
  621. // pop up the selection dialog
  622. rc = m_DSM_Entry(
  623. &m_AppId,
  624. 0,
  625. DG_CONTROL,
  626. DAT_IDENTITY,
  627. MSG_USERSELECT,
  628. (TW_MEMREF) &m_SrcId
  629. );
  630. ASSERT(rc == TWRC_SUCCESS || rc == TWRC_CANCEL);
  631. if (rc == TWRC_SUCCESS)
  632. {
  633. hr = S_OK;
  634. }
  635. }
  636. __finally
  637. {
  638. if (m_TwainState == State_3_Source_Manager_Open)
  639. {
  640. // Close the data source manager
  641. rc = m_DSM_Entry(
  642. &m_AppId,
  643. 0,
  644. DG_CONTROL,
  645. DAT_PARENT,
  646. MSG_CLOSEDSM,
  647. (TW_MEMREF) &hWndParent
  648. );
  649. ASSERT(rc == TWRC_SUCCESS);
  650. m_TwainState = State_2_Source_Manager_Loaded;
  651. }
  652. }
  653. }
  654. return hr;
  655. }
  656. //////////////////////////////////////////////////////////////////////////
  657. //
  658. //
  659. //
  660. HRESULT
  661. CTwainMgr::Select(
  662. LPCTSTR pDeviceId
  663. )
  664. {
  665. #ifdef UNICODE
  666. WideCharToMultiByte(CP_ACP, 0, pDeviceId, -1,
  667. m_SrcId.ProductName, sizeof(m_SrcId.ProductName), 0, 0);
  668. #else //UNICODE
  669. lstrcpyn(m_SrcId.ProductName, pDeviceId, sizeof(m_SrcId.ProductName));
  670. #endif //UNICODE
  671. return S_OK;
  672. }
  673. //////////////////////////////////////////////////////////////////////////
  674. //
  675. //
  676. //
  677. HRESULT
  678. CTwainMgr::Acquire(
  679. HWND hWndParent,
  680. HGLOBAL *phDib
  681. )
  682. {
  683. ASSERT(phDib);
  684. HRESULT hr = S_FALSE;
  685. TW_UINT16 rc = TWRC_FAILURE;
  686. if (m_TwainState >= State_2_Source_Manager_Loaded)
  687. {
  688. __try
  689. {
  690. if (m_TwainState == State_2_Source_Manager_Loaded)
  691. {
  692. // Open the data source manager
  693. rc = m_DSM_Entry(
  694. &m_AppId,
  695. 0,
  696. DG_CONTROL,
  697. DAT_PARENT,
  698. MSG_OPENDSM,
  699. (TW_MEMREF) &hWndParent
  700. );
  701. if (rc != TWRC_SUCCESS)
  702. {
  703. __leave;
  704. }
  705. m_TwainState = State_3_Source_Manager_Open;
  706. }
  707. #ifdef USE_SELECTSOURCE_MENUITEM
  708. if (m_SrcId.ProductName[0] == '\0')
  709. {
  710. // if no data source is selected yet, get the default
  711. rc = m_DSM_Entry(
  712. &m_AppId,
  713. 0,
  714. DG_CONTROL,
  715. DAT_IDENTITY,
  716. MSG_GETDEFAULT,
  717. (TW_MEMREF) &m_SrcId
  718. );
  719. if (rc != TWRC_SUCCESS)
  720. {
  721. __leave;
  722. }
  723. }
  724. #else //USE_SELECTSOURCE_MENUITEM
  725. rc = m_DSM_Entry(
  726. &m_AppId,
  727. 0,
  728. DG_CONTROL,
  729. DAT_IDENTITY,
  730. MSG_USERSELECT,
  731. (TW_MEMREF) &m_SrcId
  732. );
  733. ASSERT(rc == TWRC_SUCCESS || rc == TWRC_CANCEL);
  734. if (rc != TWRC_SUCCESS)
  735. {
  736. __leave;
  737. }
  738. #endif //USE_SELECTSOURCE_MENUITEM
  739. if (m_TwainState == State_3_Source_Manager_Open)
  740. {
  741. // open the data source
  742. rc = m_DSM_Entry(
  743. &m_AppId,
  744. 0,
  745. DG_CONTROL,
  746. DAT_IDENTITY,
  747. MSG_OPENDS,
  748. (TW_MEMREF) &m_SrcId
  749. );
  750. if (rc != TWRC_SUCCESS)
  751. {
  752. __leave;
  753. }
  754. m_TwainState = State_4_Source_Open;
  755. }
  756. // set the desired transfer options;
  757. // we want to transfer a single 8-bit RGB image
  758. SetCapability(CAP_XFERCOUNT, TWTY_INT16, 1);
  759. SetCapability(ICAP_PIXELTYPE, TWTY_UINT32, TWPT_RGB);
  760. SetCapability(ICAP_BITDEPTH, TWTY_UINT32, 8);
  761. if (m_TwainState == State_4_Source_Open)
  762. {
  763. // enable the data source
  764. TW_USERINTERFACE twUI;
  765. twUI.ShowUI = TRUE;
  766. twUI.hParent = hWndParent;
  767. rc = m_DSM_Entry(
  768. &m_AppId,
  769. &m_SrcId,
  770. DG_CONTROL,
  771. DAT_USERINTERFACE,
  772. MSG_ENABLEDS,
  773. (TW_MEMREF) &twUI
  774. );
  775. theApp.RestoreWaitCursor();
  776. if (rc != TWRC_SUCCESS)
  777. {
  778. __leave;
  779. }
  780. m_TwainState = State_5_Source_Enabled;
  781. }
  782. if (m_TwainState == State_5_Source_Enabled)
  783. {
  784. // Disable the parent window
  785. EnableWindow(hWndParent, FALSE);
  786. // Enter the message loop to transfer the image
  787. MSG msg;
  788. BOOL bDone = FALSE;
  789. while (!bDone && GetMessage(&msg, 0, 0, 0))
  790. {
  791. // process the event through TWAIN
  792. TW_EVENT twEvent;
  793. twEvent.pEvent = &msg;
  794. twEvent.TWMessage = MSG_NULL;
  795. rc = m_DSM_Entry(
  796. &m_AppId,
  797. &m_SrcId,
  798. DG_CONTROL,
  799. DAT_EVENT,
  800. MSG_PROCESSEVENT,
  801. (TW_MEMREF) &twEvent
  802. );
  803. if (twEvent.TWMessage == MSG_CLOSEDSREQ)
  804. {
  805. bDone = TRUE;
  806. hr = S_FALSE;
  807. }
  808. else if (twEvent.TWMessage == MSG_XFERREADY)
  809. {
  810. m_TwainState = State_6_Transfer_Ready;
  811. TW_PENDINGXFERS twPendingXfers;
  812. do
  813. {
  814. m_TwainState = State_7_Transferring;
  815. rc = m_DSM_Entry(
  816. &m_AppId,
  817. &m_SrcId,
  818. DG_IMAGE,
  819. DAT_IMAGENATIVEXFER,
  820. MSG_GET,
  821. (TW_MEMREF) phDib
  822. );
  823. if (rc != TWRC_XFERDONE)
  824. {
  825. if (*phDib)
  826. {
  827. GlobalFree(*phDib);
  828. }
  829. __leave;
  830. }
  831. hr = S_OK;
  832. // End the transfer
  833. rc = m_DSM_Entry(
  834. &m_AppId,
  835. &m_SrcId,
  836. DG_CONTROL,
  837. DAT_PENDINGXFERS,
  838. MSG_ENDXFER,
  839. (TW_MEMREF) &twPendingXfers
  840. );
  841. if (rc != TWRC_SUCCESS)
  842. {
  843. __leave;
  844. }
  845. m_TwainState = State_6_Transfer_Ready;
  846. } while (twPendingXfers.Count != 0);
  847. m_TwainState = State_5_Source_Enabled;
  848. //exit after a single image transfer
  849. bDone = TRUE;
  850. }
  851. if (rc == TWRC_NOTDSEVENT)
  852. {
  853. TranslateMessage(&msg);
  854. DispatchMessage(&msg);
  855. }
  856. }
  857. }
  858. }
  859. __finally
  860. {
  861. // enable the parent window upon exiting the message loop
  862. EnableWindow(hWndParent, TRUE);
  863. ASSERT(m_TwainState <= State_6_Transfer_Ready);
  864. if (m_TwainState == State_6_Transfer_Ready)
  865. {
  866. TW_PENDINGXFERS twPendingXfers;
  867. rc = m_DSM_Entry(
  868. &m_AppId,
  869. &m_SrcId,
  870. DG_CONTROL,
  871. DAT_PENDINGXFERS,
  872. MSG_RESET,
  873. (TW_MEMREF) &twPendingXfers
  874. );
  875. ASSERT(rc == TWRC_SUCCESS);
  876. m_TwainState = State_5_Source_Enabled;
  877. }
  878. if (m_TwainState == State_5_Source_Enabled)
  879. {
  880. TW_USERINTERFACE twUI;
  881. rc = m_DSM_Entry(
  882. &m_AppId,
  883. &m_SrcId,
  884. DG_CONTROL,
  885. DAT_USERINTERFACE,
  886. MSG_DISABLEDS,
  887. (TW_MEMREF) &twUI
  888. );
  889. ASSERT(rc == TWRC_SUCCESS);
  890. m_TwainState = State_4_Source_Open;
  891. }
  892. if (m_TwainState == State_4_Source_Open)
  893. {
  894. rc = m_DSM_Entry(
  895. &m_AppId,
  896. 0,
  897. DG_CONTROL,
  898. DAT_IDENTITY,
  899. MSG_CLOSEDS,
  900. (TW_MEMREF) &m_SrcId
  901. );
  902. ASSERT(rc == TWRC_SUCCESS);
  903. m_TwainState = State_3_Source_Manager_Open;
  904. }
  905. if (m_TwainState == State_3_Source_Manager_Open)
  906. {
  907. rc = m_DSM_Entry(
  908. &m_AppId,
  909. 0,
  910. DG_CONTROL,
  911. DAT_PARENT,
  912. MSG_CLOSEDSM,
  913. (TW_MEMREF) &hWndParent
  914. );
  915. ASSERT(rc == TWRC_SUCCESS);
  916. m_TwainState = State_2_Source_Manager_Loaded;
  917. }
  918. }
  919. }
  920. return hr;
  921. }
  922. //////////////////////////////////////////////////////////////////////////
  923. //
  924. //
  925. //
  926. int CTwainMgr::NumDevices(HWND hWndParent)
  927. {
  928. return 1; // this is too slow, better lie...
  929. int nNumDevices = 0;
  930. TW_UINT16 rc = TWRC_FAILURE;
  931. // m_TwainState >= State_2 guarantees m_DSM_Entry != 0
  932. if (m_TwainState >= State_2_Source_Manager_Loaded)
  933. {
  934. __try
  935. {
  936. if (m_TwainState == State_2_Source_Manager_Loaded)
  937. {
  938. // Open the data source manager
  939. rc = m_DSM_Entry(
  940. &m_AppId,
  941. 0,
  942. DG_CONTROL,
  943. DAT_PARENT,
  944. MSG_OPENDSM,
  945. (TW_MEMREF) &hWndParent
  946. );
  947. if (rc != TWRC_SUCCESS)
  948. {
  949. __leave;
  950. }
  951. m_TwainState = State_3_Source_Manager_Open;
  952. }
  953. // Enumerate the devices one by one
  954. TW_IDENTITY SrcId;
  955. rc = m_DSM_Entry(
  956. &m_AppId,
  957. 0,
  958. DG_CONTROL,
  959. DAT_IDENTITY,
  960. MSG_GETFIRST,
  961. (TW_MEMREF) &SrcId
  962. );
  963. while (rc == TWRC_SUCCESS)
  964. {
  965. ++nNumDevices;
  966. rc = m_DSM_Entry(
  967. &m_AppId,
  968. 0,
  969. DG_CONTROL,
  970. DAT_IDENTITY,
  971. MSG_GETNEXT,
  972. (TW_MEMREF) &SrcId
  973. );
  974. }
  975. }
  976. __finally
  977. {
  978. if (m_TwainState == State_3_Source_Manager_Open)
  979. {
  980. // Close the data source manager
  981. rc = m_DSM_Entry(
  982. &m_AppId,
  983. 0,
  984. DG_CONTROL,
  985. DAT_PARENT,
  986. MSG_CLOSEDSM,
  987. (TW_MEMREF) &hWndParent
  988. );
  989. ASSERT(rc == TWRC_SUCCESS);
  990. m_TwainState = State_2_Source_Manager_Loaded;
  991. }
  992. }
  993. }
  994. return nNumDevices;
  995. }
  996. //////////////////////////////////////////////////////////////////////////
  997. //
  998. //
  999. //
  1000. TW_UINT16
  1001. CTwainMgr::SetCapability(
  1002. TW_UINT16 Cap,
  1003. TW_UINT16 ItemType,
  1004. TW_UINT32 Item
  1005. )
  1006. {
  1007. TW_UINT16 rc = TWRC_FAILURE;
  1008. TW_CAPABILITY twCapability;
  1009. twCapability.Cap = Cap;
  1010. twCapability.ConType = TWON_ONEVALUE;
  1011. twCapability.hContainer = 0;
  1012. twCapability.hContainer = GlobalAlloc(
  1013. GMEM_MOVEABLE | GMEM_ZEROINIT,
  1014. sizeof(TW_ONEVALUE)
  1015. );
  1016. if (twCapability.hContainer)
  1017. {
  1018. pTW_ONEVALUE pVal = (pTW_ONEVALUE) GlobalLock(twCapability.hContainer);
  1019. if (pVal)
  1020. {
  1021. pVal->ItemType = ItemType;
  1022. pVal->Item = Item;
  1023. GlobalUnlock(twCapability.hContainer);
  1024. rc = m_DSM_Entry(
  1025. &m_AppId,
  1026. &m_SrcId,
  1027. DG_CONTROL,
  1028. DAT_CAPABILITY,
  1029. MSG_SET,
  1030. (TW_MEMREF) &twCapability
  1031. );
  1032. }
  1033. GlobalFree(twCapability.hContainer);
  1034. }
  1035. return rc;
  1036. }
  1037. //////////////////////////////////////////////////////////////////////////
  1038. //
  1039. //
  1040. //
  1041. TW_UINT16
  1042. CTwainMgr::GetCapability(
  1043. TW_UINT16 Cap,
  1044. pTW_UINT16 pItemType,
  1045. pTW_UINT32 pItem
  1046. )
  1047. {
  1048. TW_CAPABILITY twCapability;
  1049. twCapability.Cap = Cap;
  1050. twCapability.ConType = TWON_DONTCARE16;
  1051. twCapability.hContainer = 0;
  1052. TW_UINT16 rc = m_DSM_Entry(
  1053. &m_AppId,
  1054. &m_SrcId,
  1055. DG_CONTROL,
  1056. DAT_CAPABILITY,
  1057. MSG_GET,
  1058. (TW_MEMREF) &twCapability
  1059. );
  1060. if (twCapability.hContainer)
  1061. {
  1062. pTW_ONEVALUE pVal = (pTW_ONEVALUE) GlobalLock(twCapability.hContainer);
  1063. if (pVal)
  1064. {
  1065. if (pItemType)
  1066. {
  1067. *pItemType = pVal->ItemType;
  1068. }
  1069. if (pItem)
  1070. {
  1071. *pItem = pVal->Item;
  1072. }
  1073. }
  1074. GlobalFree(twCapability.hContainer);
  1075. }
  1076. return rc;
  1077. }
  1078. #endif //USE_TWAIN
  1079. //////////////////////////////////////////////////////////////////////////
  1080. //
  1081. //
  1082. //
  1083. HRESULT
  1084. WiaGetNumDevices(
  1085. IWiaDevMgr *_pWiaDevMgr,
  1086. ULONG *pulNumDevices
  1087. )
  1088. {
  1089. HRESULT hr;
  1090. // Validate and initialize output parameters
  1091. if (pulNumDevices == 0)
  1092. {
  1093. return E_POINTER;
  1094. }
  1095. *pulNumDevices = 0;
  1096. // Create a connection to the local WIA device manager
  1097. CComPtr<IWiaDevMgr> pWiaDevMgr = _pWiaDevMgr;
  1098. if (pWiaDevMgr == 0)
  1099. {
  1100. hr = pWiaDevMgr.CoCreateInstance(CLSID_WiaDevMgr);
  1101. if (!SUCCEEDED(hr))
  1102. {
  1103. return hr;
  1104. }
  1105. }
  1106. // Get a list of all the WIA devices on the system
  1107. CComPtr<IEnumWIA_DEV_INFO> pIEnumWIA_DEV_INFO;
  1108. hr = pWiaDevMgr->EnumDeviceInfo(
  1109. 0,
  1110. &pIEnumWIA_DEV_INFO
  1111. );
  1112. if (!SUCCEEDED(hr))
  1113. {
  1114. return hr;
  1115. }
  1116. // Get the number of WIA devices
  1117. ULONG celt;
  1118. hr = pIEnumWIA_DEV_INFO->GetCount(&celt);
  1119. if (!SUCCEEDED(hr))
  1120. {
  1121. return hr;
  1122. }
  1123. *pulNumDevices = celt;
  1124. return S_OK;
  1125. }
  1126. //////////////////////////////////////////////////////////////////////////
  1127. //
  1128. //
  1129. //
  1130. CEventCallback::CEventCallback()
  1131. {
  1132. m_cRef = 0;
  1133. m_nNumDevices = 0;
  1134. }
  1135. //////////////////////////////////////////////////////////////////////////
  1136. //
  1137. //
  1138. //
  1139. HRESULT CEventCallback::Register()
  1140. {
  1141. HRESULT hr;
  1142. // Create a connection to the local WIA device manager
  1143. CComPtr<IWiaDevMgr> pWiaDevMgr;
  1144. hr = pWiaDevMgr.CoCreateInstance(CLSID_WiaDevMgr);
  1145. if (!SUCCEEDED(hr))
  1146. {
  1147. return hr;
  1148. }
  1149. // Get the count of all the WIA devices on the system
  1150. hr = WiaGetNumDevices(pWiaDevMgr, &m_nNumDevices);
  1151. if (!SUCCEEDED(hr))
  1152. {
  1153. return hr;
  1154. }
  1155. // Register the callback interface
  1156. hr = pWiaDevMgr->RegisterEventCallbackInterface(
  1157. 0,
  1158. 0,
  1159. &WIA_EVENT_DEVICE_CONNECTED,
  1160. this,
  1161. &m_pConnectEventObject
  1162. );
  1163. if (!SUCCEEDED(hr))
  1164. {
  1165. return hr;
  1166. }
  1167. hr = pWiaDevMgr->RegisterEventCallbackInterface(
  1168. 0,
  1169. 0,
  1170. &WIA_EVENT_DEVICE_DISCONNECTED,
  1171. this,
  1172. &m_pDisconnectEventObject
  1173. );
  1174. if (!SUCCEEDED(hr))
  1175. {
  1176. return hr;
  1177. }
  1178. return S_OK;
  1179. }
  1180. //////////////////////////////////////////////////////////////////////////
  1181. //
  1182. //
  1183. //
  1184. ULONG CEventCallback::GetNumDevices() const
  1185. {
  1186. return m_nNumDevices;
  1187. }
  1188. //////////////////////////////////////////////////////////////////////////
  1189. //
  1190. //
  1191. //
  1192. STDMETHODIMP CEventCallback::QueryInterface(REFIID iid, LPVOID *ppvObj)
  1193. {
  1194. if (ppvObj == 0)
  1195. {
  1196. return E_POINTER;
  1197. }
  1198. if (iid == IID_IUnknown)
  1199. {
  1200. AddRef();
  1201. *ppvObj = (IUnknown *) this;
  1202. return S_OK;
  1203. }
  1204. if (iid == IID_IWiaEventCallback)
  1205. {
  1206. AddRef();
  1207. *ppvObj = (IWiaEventCallback *) this;
  1208. return S_OK;
  1209. }
  1210. *ppvObj = 0;
  1211. return E_NOINTERFACE;
  1212. }
  1213. //////////////////////////////////////////////////////////////////////////
  1214. //
  1215. //
  1216. //
  1217. STDMETHODIMP_(ULONG) CEventCallback::AddRef()
  1218. {
  1219. return InterlockedIncrement(&m_cRef);
  1220. }
  1221. //////////////////////////////////////////////////////////////////////////
  1222. //
  1223. //
  1224. //
  1225. STDMETHODIMP_(ULONG) CEventCallback::Release()
  1226. {
  1227. LONG cRef = InterlockedDecrement(&m_cRef);
  1228. if (cRef == 0)
  1229. {
  1230. delete this;
  1231. }
  1232. return cRef;
  1233. }
  1234. //////////////////////////////////////////////////////////////////////////
  1235. //
  1236. //
  1237. //
  1238. STDMETHODIMP CEventCallback::ImageEventCallback(
  1239. LPCGUID pEventGuid,
  1240. BSTR bstrEventDescription,
  1241. BSTR bstrDeviceID,
  1242. BSTR bstrDeviceDescription,
  1243. DWORD dwDeviceType,
  1244. BSTR bstrFullItemName,
  1245. ULONG *pulEventType,
  1246. ULONG ulReserved
  1247. )
  1248. {
  1249. return WiaGetNumDevices(0, &m_nNumDevices);
  1250. }
  1251. //////////////////////////////////////////////////////////////////////////
  1252. //
  1253. //
  1254. //
  1255. CDataCallback::CDataCallback(IWiaProgressDialog *pProgress)
  1256. {
  1257. m_cRef = 0;
  1258. m_hBuffer = 0;
  1259. m_lBufferSize = 0;
  1260. m_lDataSize = 0;
  1261. m_pProgress = pProgress;
  1262. #ifdef DBG
  1263. m_hDumpFile = CreateFile(_T("wiadump.bin"), GENERIC_WRITE,
  1264. FILE_SHARE_READ, 0, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
  1265. m_TimeDeviceBegin.QuadPart = 0;
  1266. m_TimeDeviceEnd.QuadPart = 0;
  1267. m_TimeProcessBegin.QuadPart = 0;
  1268. m_TimeProcessEnd.QuadPart = 0;
  1269. m_TimeClientBegin.QuadPart = 0;
  1270. m_TimeClientEnd.QuadPart = 0;
  1271. #endif //DBG
  1272. }
  1273. //////////////////////////////////////////////////////////////////////////
  1274. //
  1275. //
  1276. //
  1277. CDataCallback::~CDataCallback()
  1278. {
  1279. if (m_hBuffer)
  1280. {
  1281. GlobalFree(m_hBuffer);
  1282. }
  1283. #ifdef DBG
  1284. CloseHandle(m_hDumpFile);
  1285. #endif //DBG
  1286. }
  1287. //////////////////////////////////////////////////////////////////////////
  1288. //
  1289. //
  1290. //
  1291. HGLOBAL CDataCallback::GetBuffer()
  1292. {
  1293. HGLOBAL hBuffer = m_hBuffer;
  1294. m_hBuffer = 0;
  1295. m_lBufferSize = 0;
  1296. m_lDataSize = 0;
  1297. return hBuffer;
  1298. }
  1299. //////////////////////////////////////////////////////////////////////////
  1300. //
  1301. //
  1302. //
  1303. STDMETHODIMP CDataCallback::QueryInterface(REFIID iid, LPVOID *ppvObj)
  1304. {
  1305. if (ppvObj == 0)
  1306. {
  1307. return E_POINTER;
  1308. }
  1309. if (iid == IID_IUnknown)
  1310. {
  1311. AddRef();
  1312. *ppvObj = (IUnknown*) this;
  1313. return S_OK;
  1314. }
  1315. if (iid == IID_IWiaDataCallback)
  1316. {
  1317. AddRef();
  1318. *ppvObj = (IWiaDataCallback *) this;
  1319. return S_OK;
  1320. }
  1321. *ppvObj = 0;
  1322. return E_NOINTERFACE;
  1323. }
  1324. //////////////////////////////////////////////////////////////////////////
  1325. //
  1326. //
  1327. //
  1328. STDMETHODIMP_(ULONG) CDataCallback::AddRef()
  1329. {
  1330. return InterlockedIncrement(&m_cRef);
  1331. }
  1332. //////////////////////////////////////////////////////////////////////////
  1333. //
  1334. //
  1335. //
  1336. STDMETHODIMP_(ULONG) CDataCallback::Release()
  1337. {
  1338. LONG cRef = InterlockedDecrement(&m_cRef);
  1339. if (cRef == 0)
  1340. {
  1341. delete this;
  1342. }
  1343. return cRef;
  1344. }
  1345. //////////////////////////////////////////////////////////////////////////
  1346. //
  1347. //
  1348. //
  1349. STDMETHODIMP CDataCallback::BandedDataCallback(
  1350. LONG lReason,
  1351. LONG lStatus,
  1352. LONG lPercentComplete,
  1353. LONG lOffset,
  1354. LONG lLength,
  1355. LONG lReserved,
  1356. LONG lResLength,
  1357. PBYTE pbBuffer
  1358. )
  1359. {
  1360. HRESULT hr;
  1361. Trace(
  1362. _T("DataCallback: Reason=%d Stat=%d %%=%d Offset=%d Length=%d Buf=%p\n"),
  1363. lReason, lStatus, lPercentComplete, lOffset, lLength, pbBuffer
  1364. );
  1365. // check if the user has pressed cancel
  1366. BOOL bCancelled;
  1367. if (m_pProgress && m_pProgress->Cancelled(&bCancelled) == S_OK && bCancelled)
  1368. {
  1369. return S_FALSE;
  1370. }
  1371. switch (lReason)
  1372. {
  1373. case IT_MSG_DATA_HEADER:
  1374. {
  1375. // allocate memory for the image if the size is given in the header
  1376. PWIA_DATA_CALLBACK_HEADER pHeader = (PWIA_DATA_CALLBACK_HEADER) pbBuffer;
  1377. if (pHeader && pHeader->lBufferSize)
  1378. {
  1379. hr = ReAllocBuffer(pHeader->lBufferSize);
  1380. if (hr != S_OK)
  1381. {
  1382. return S_FALSE;
  1383. }
  1384. }
  1385. break;
  1386. }
  1387. case IT_MSG_DATA:
  1388. {
  1389. QueryStartTimes(lStatus, lPercentComplete);
  1390. UpdateStatus(lStatus, lPercentComplete);
  1391. // if the buffer is not allocated yet and this is the first block,
  1392. // try to allocate a buffer according to the bitmap header info
  1393. if (m_lBufferSize == 0 && lOffset == 0)
  1394. {
  1395. LONG lBufferSize = FindDibSize(pbBuffer);
  1396. if (lBufferSize)
  1397. {
  1398. hr = ReAllocBuffer(lBufferSize);
  1399. if (hr != S_OK)
  1400. {
  1401. return S_FALSE;
  1402. }
  1403. }
  1404. }
  1405. // if the transfer goes past the buffer, try to expand it
  1406. if (lOffset + lLength > m_lBufferSize)
  1407. {
  1408. hr = ReAllocBuffer(lOffset + 2*lLength);
  1409. if (hr != S_OK)
  1410. {
  1411. return S_FALSE;
  1412. }
  1413. }
  1414. // keep track of data size
  1415. if (lOffset + lLength > m_lDataSize)
  1416. {
  1417. m_lDataSize = lOffset + lLength;
  1418. }
  1419. // copy the transfer buffer
  1420. PBYTE pBuffer = (PBYTE) GlobalLock(m_hBuffer);
  1421. if (pBuffer)
  1422. {
  1423. CopyMemory(pBuffer + lOffset, pbBuffer, lLength);
  1424. GlobalUnlock(m_hBuffer);
  1425. }
  1426. #ifdef DBG
  1427. DWORD nWritten;
  1428. SetFilePointer(m_hDumpFile, lOffset, 0, FILE_BEGIN);
  1429. WriteFile(m_hDumpFile, pbBuffer, lLength, &nWritten, 0);
  1430. #endif //DBG
  1431. QueryStopTimes(lStatus, lPercentComplete);
  1432. break;
  1433. }
  1434. case IT_MSG_STATUS:
  1435. {
  1436. // update the progress bar position
  1437. QueryStartTimes(lStatus, lPercentComplete);
  1438. UpdateStatus(lStatus, lPercentComplete);
  1439. QueryStopTimes(lStatus, lPercentComplete);
  1440. break;
  1441. }
  1442. case IT_MSG_TERMINATION:
  1443. {
  1444. PVOID pBuffer = GlobalLock(m_hBuffer);
  1445. if (pBuffer)
  1446. {
  1447. FixDibHeader(pBuffer, m_lDataSize);
  1448. GlobalUnlock(m_hBuffer);
  1449. }
  1450. break;
  1451. }
  1452. case IT_MSG_NEW_PAGE:
  1453. {
  1454. // mspaint should not get this message, but...
  1455. PVOID pBuffer = GlobalLock(m_hBuffer);
  1456. if (pBuffer)
  1457. {
  1458. FixDibHeader(pBuffer, m_lDataSize);
  1459. GlobalUnlock(m_hBuffer);
  1460. }
  1461. break;
  1462. }
  1463. }
  1464. return S_OK;
  1465. }
  1466. //////////////////////////////////////////////////////////////////////////
  1467. //
  1468. //
  1469. //
  1470. HRESULT CDataCallback::ReAllocBuffer(LONG lBufferSize)
  1471. {
  1472. // try to allocate the new buffer
  1473. Trace(_T("Allocating %d bytes for image data\n"), lBufferSize);
  1474. HGLOBAL hBuffer;
  1475. if (m_hBuffer == 0)
  1476. {
  1477. hBuffer = (PBYTE) GlobalAlloc(GMEM_MOVEABLE, lBufferSize);
  1478. }
  1479. else
  1480. {
  1481. hBuffer = (PBYTE) GlobalReAlloc(m_hBuffer, lBufferSize, 0);
  1482. }
  1483. if (hBuffer == 0)
  1484. {
  1485. theApp.SetMemoryEmergency(TRUE);
  1486. return S_FALSE;
  1487. }
  1488. // store this new buffer
  1489. m_hBuffer = hBuffer;
  1490. m_lBufferSize = lBufferSize;
  1491. return S_OK;
  1492. }
  1493. //////////////////////////////////////////////////////////////////////////
  1494. //
  1495. //
  1496. //
  1497. inline ULONG LineWidth(ULONG nWidth, ULONG nBitCount)
  1498. {
  1499. return (((nWidth * nBitCount) + 31) & ~31) >> 3;
  1500. }
  1501. //////////////////////////////////////////////////////////////////////////
  1502. //
  1503. //
  1504. //
  1505. ULONG FindDibSize(LPCVOID pDib)
  1506. {
  1507. ULONG nHeaderSize = *(PDWORD)pDib;
  1508. // Do we recognize the header?
  1509. if (nHeaderSize != sizeof(BITMAPCOREHEADER) &&
  1510. nHeaderSize != sizeof(BITMAPINFOHEADER) &&
  1511. nHeaderSize != sizeof(BITMAPV4HEADER) &&
  1512. nHeaderSize != sizeof(BITMAPV5HEADER))
  1513. {
  1514. return 0;
  1515. }
  1516. // Start the calculation with the header size
  1517. ULONG nDibSize = nHeaderSize;
  1518. // is this an old style BITMAPCOREHEADER?
  1519. if (nHeaderSize == sizeof(BITMAPCOREHEADER))
  1520. {
  1521. PBITMAPCOREHEADER pbmch = (PBITMAPCOREHEADER) pDib;
  1522. // Add the color table size
  1523. if (pbmch->bcBitCount <= 8)
  1524. {
  1525. nDibSize += sizeof(RGBTRIPLE) * (1 << pbmch->bcBitCount);
  1526. }
  1527. // Add the bitmap size
  1528. nDibSize += LineWidth(pbmch->bcWidth, pbmch->bcBitCount) * pbmch->bcHeight;
  1529. }
  1530. else
  1531. {
  1532. // this is at least a BITMAPINFOHEADER
  1533. PBITMAPINFOHEADER pbmih = (PBITMAPINFOHEADER) pDib;
  1534. // Add the color table size
  1535. if (pbmih->biClrUsed != 0)
  1536. {
  1537. nDibSize += sizeof(RGBQUAD) * pbmih->biClrUsed;
  1538. }
  1539. else if (pbmih->biBitCount <= 8)
  1540. {
  1541. nDibSize += sizeof(RGBQUAD) * (1 << pbmih->biBitCount);
  1542. }
  1543. // Add the bitmap size
  1544. if (pbmih->biSizeImage != 0)
  1545. {
  1546. nDibSize += pbmih->biSizeImage;
  1547. }
  1548. else
  1549. {
  1550. // biSizeImage must be specified for compressed bitmaps
  1551. if (pbmih->biCompression != BI_RGB &&
  1552. pbmih->biCompression != BI_BITFIELDS)
  1553. {
  1554. return 0;
  1555. }
  1556. nDibSize += LineWidth(pbmih->biWidth, pbmih->biBitCount) * abs(pbmih->biHeight);
  1557. }
  1558. // Consider special cases
  1559. if (nHeaderSize == sizeof(BITMAPINFOHEADER))
  1560. {
  1561. // If this is a 16 or 32 bit bitmap and BI_BITFIELDS is used,
  1562. // bmiColors member contains three DWORD color masks.
  1563. // For V4 or V5 headers, this info is included the header
  1564. if (pbmih->biCompression == BI_BITFIELDS)
  1565. {
  1566. nDibSize += 3 * sizeof(DWORD);
  1567. }
  1568. }
  1569. else if (nHeaderSize >= sizeof(BITMAPV5HEADER))
  1570. {
  1571. // If this is a V5 header and an ICM profile is specified,
  1572. // we need to consider the profile data size
  1573. PBITMAPV5HEADER pbV5h = (PBITMAPV5HEADER) pDib;
  1574. // if there is some padding before the profile data, add it
  1575. if (pbV5h->bV5ProfileData > nDibSize)
  1576. {
  1577. nDibSize = pbV5h->bV5ProfileData;
  1578. }
  1579. // add the profile data size
  1580. nDibSize += pbV5h->bV5ProfileSize;
  1581. }
  1582. }
  1583. return nDibSize;
  1584. }
  1585. //////////////////////////////////////////////////////////////////////////
  1586. //
  1587. //
  1588. //
  1589. ULONG FindDibOffBits(LPCVOID pDib)
  1590. {
  1591. ULONG nHeaderSize = *(PDWORD)pDib;
  1592. // Do we recognize the header?
  1593. if (nHeaderSize != sizeof(BITMAPCOREHEADER) &&
  1594. nHeaderSize != sizeof(BITMAPINFOHEADER) &&
  1595. nHeaderSize != sizeof(BITMAPV4HEADER) &&
  1596. nHeaderSize != sizeof(BITMAPV5HEADER))
  1597. {
  1598. return 0;
  1599. }
  1600. // Start the calculation with the header size
  1601. ULONG nOffBits = nHeaderSize;
  1602. // is this an old style BITMAPCOREHEADER?
  1603. if (nHeaderSize == sizeof(BITMAPCOREHEADER))
  1604. {
  1605. PBITMAPCOREHEADER pbmch = (PBITMAPCOREHEADER) pDib;
  1606. // Add the color table size
  1607. if (pbmch->bcBitCount <= 8)
  1608. {
  1609. nOffBits += sizeof(RGBTRIPLE) * (1 << pbmch->bcBitCount);
  1610. }
  1611. }
  1612. else
  1613. {
  1614. // this is at least a BITMAPINFOHEADER
  1615. PBITMAPINFOHEADER pbmih = (PBITMAPINFOHEADER) pDib;
  1616. // Add the color table size
  1617. if (pbmih->biClrUsed != 0)
  1618. {
  1619. nOffBits += sizeof(RGBQUAD) * pbmih->biClrUsed;
  1620. }
  1621. else if (pbmih->biBitCount <= 8)
  1622. {
  1623. nOffBits += sizeof(RGBQUAD) * (1 << pbmih->biBitCount);
  1624. }
  1625. // Consider special cases
  1626. if (nHeaderSize == sizeof(BITMAPINFOHEADER))
  1627. {
  1628. // If this is a 16 or 32 bit bitmap and BI_BITFIELDS is used,
  1629. // bmiColors member contains three DWORD color masks.
  1630. // For V4 or V5 headers, this info is included the header
  1631. if (pbmih->biCompression == BI_BITFIELDS)
  1632. {
  1633. nOffBits += 3 * sizeof(DWORD);
  1634. }
  1635. }
  1636. else if (nHeaderSize >= sizeof(BITMAPV5HEADER))
  1637. {
  1638. // If this is a V5 header and an ICM profile is specified,
  1639. // we need to consider the profile data size
  1640. PBITMAPV5HEADER pbV5h = (PBITMAPV5HEADER) pDib;
  1641. // if the profile data comes before the pixel data, add it
  1642. if (pbV5h->bV5ProfileData <= nOffBits)
  1643. {
  1644. nOffBits += pbV5h->bV5ProfileSize;
  1645. }
  1646. }
  1647. }
  1648. return nOffBits;
  1649. }
  1650. //////////////////////////////////////////////////////////////////////////
  1651. //
  1652. //
  1653. //
  1654. void FixDibHeader(LPVOID pDib, DWORD dwSize)
  1655. {
  1656. ULONG nHeaderSize = *(PDWORD)pDib;
  1657. // Do we recognize the header?
  1658. if (nHeaderSize != sizeof(BITMAPCOREHEADER) &&
  1659. nHeaderSize != sizeof(BITMAPINFOHEADER) &&
  1660. nHeaderSize != sizeof(BITMAPV4HEADER) &&
  1661. nHeaderSize != sizeof(BITMAPV5HEADER))
  1662. {
  1663. return;
  1664. }
  1665. // is this an old style BITMAPCOREHEADER?
  1666. if (nHeaderSize == sizeof(BITMAPCOREHEADER))
  1667. {
  1668. PBITMAPCOREHEADER pbmch = (PBITMAPCOREHEADER) pDib;
  1669. // fix the height value if necessary
  1670. if (pbmch->bcHeight == 0)
  1671. {
  1672. // start the calculation with the header size
  1673. DWORD dwSizeImage = dwSize - nHeaderSize;
  1674. // subtract the color table size
  1675. if (pbmch->bcBitCount <= 8)
  1676. {
  1677. dwSizeImage -= sizeof(RGBTRIPLE) * (1 << pbmch->bcBitCount);
  1678. }
  1679. // calculate the height
  1680. pbmch->bcHeight = (WORD) (dwSizeImage / LineWidth(pbmch->bcWidth, pbmch->bcBitCount));
  1681. }
  1682. }
  1683. else
  1684. {
  1685. // this is at least a BITMAPINFOHEADER
  1686. PBITMAPINFOHEADER pbmih = (PBITMAPINFOHEADER) pDib;
  1687. // fix the height value if necessary
  1688. if (pbmih->biHeight == 0)
  1689. {
  1690. // find the size of the image data
  1691. DWORD dwSizeImage;
  1692. if (pbmih->biSizeImage != 0)
  1693. {
  1694. // if the size is specified in the header, take it
  1695. dwSizeImage = pbmih->biSizeImage;
  1696. }
  1697. else
  1698. {
  1699. // start the calculation with the header size
  1700. dwSizeImage = dwSize - nHeaderSize;
  1701. // subtract the color table size
  1702. if (pbmih->biClrUsed != 0)
  1703. {
  1704. dwSizeImage -= sizeof(RGBQUAD) * pbmih->biClrUsed;
  1705. }
  1706. else if (pbmih->biBitCount <= 8)
  1707. {
  1708. dwSizeImage -= sizeof(RGBQUAD) * (1 << pbmih->biBitCount);
  1709. }
  1710. // Consider special cases
  1711. if (nHeaderSize == sizeof(BITMAPINFOHEADER))
  1712. {
  1713. // If this is a 16 or 32 bit bitmap and BI_BITFIELDS is used,
  1714. // bmiColors member contains three DWORD color masks.
  1715. // For V4 or V5 headers, this info is included the header
  1716. if (pbmih->biCompression == BI_BITFIELDS)
  1717. {
  1718. dwSizeImage -= 3 * sizeof(DWORD);
  1719. }
  1720. }
  1721. else if (nHeaderSize >= sizeof(BITMAPV5HEADER))
  1722. {
  1723. // If this is a V5 header and an ICM profile is specified,
  1724. // we need to consider the profile data size
  1725. PBITMAPV5HEADER pbV5h = (PBITMAPV5HEADER) pDib;
  1726. // add the profile data size
  1727. dwSizeImage -= pbV5h->bV5ProfileSize;
  1728. }
  1729. // store the image size
  1730. pbmih->biSizeImage = dwSizeImage;
  1731. }
  1732. // finally, calculate the height
  1733. pbmih->biHeight = -(LONG) (dwSizeImage / LineWidth(pbmih->biWidth, pbmih->biBitCount));
  1734. }
  1735. }
  1736. }
  1737. //////////////////////////////////////////////////////////////////////////
  1738. //
  1739. //
  1740. //
  1741. void CDataCallback::UpdateStatus(LONG lStatus, LONG lPercentComplete)
  1742. {
  1743. if (m_pProgress)
  1744. {
  1745. m_pProgress->SetPercentComplete(lPercentComplete);
  1746. CString strFormat;
  1747. switch (lStatus)
  1748. {
  1749. case IT_STATUS_TRANSFER_FROM_DEVICE:
  1750. strFormat.LoadString(IDS_STATUS_TRANSFER_FROM_DEVICE);
  1751. break;
  1752. case IT_STATUS_PROCESSING_DATA:
  1753. strFormat.LoadString(IDS_STATUS_PROCESSING_DATA);
  1754. break;
  1755. case IT_STATUS_TRANSFER_TO_CLIENT:
  1756. strFormat.LoadString(IDS_STATUS_TRANSFER_TO_CLIENT);
  1757. break;
  1758. }
  1759. CString strStatusText;
  1760. strStatusText.Format(strFormat, lPercentComplete);
  1761. USES_CONVERSION;
  1762. m_pProgress->SetMessage(T2CW(strStatusText));
  1763. }
  1764. }
  1765. //////////////////////////////////////////////////////////////////////////
  1766. //
  1767. //
  1768. //
  1769. #ifdef DBG
  1770. void CDataCallback::QueryStartTimes(LONG lStatus, LONG lPercentComplete)
  1771. {
  1772. if (lStatus & IT_STATUS_TRANSFER_FROM_DEVICE &&
  1773. (lPercentComplete == 0 || m_TimeDeviceBegin.QuadPart == 0))
  1774. {
  1775. QueryPerformanceCounter(&m_TimeDeviceBegin);
  1776. }
  1777. if (lStatus & IT_STATUS_PROCESSING_DATA &&
  1778. (lPercentComplete == 0 || m_TimeProcessBegin.QuadPart == 0))
  1779. {
  1780. QueryPerformanceCounter(&m_TimeProcessBegin);
  1781. }
  1782. if (lStatus & IT_STATUS_TRANSFER_TO_CLIENT &&
  1783. (lPercentComplete == 0 || m_TimeClientBegin.QuadPart == 0))
  1784. {
  1785. QueryPerformanceCounter(&m_TimeClientBegin);
  1786. }
  1787. }
  1788. void CDataCallback::QueryStopTimes(LONG lStatus, LONG lPercentComplete)
  1789. {
  1790. if (lStatus & IT_STATUS_TRANSFER_FROM_DEVICE && lPercentComplete == 100)
  1791. {
  1792. QueryPerformanceCounter(&m_TimeDeviceEnd);
  1793. }
  1794. if (lStatus & IT_STATUS_PROCESSING_DATA && lPercentComplete == 100)
  1795. {
  1796. QueryPerformanceCounter(&m_TimeProcessEnd);
  1797. }
  1798. if (lStatus & IT_STATUS_TRANSFER_TO_CLIENT && lPercentComplete == 100)
  1799. {
  1800. QueryPerformanceCounter(&m_TimeClientEnd);
  1801. }
  1802. }
  1803. void CDataCallback::PrintTimes()
  1804. {
  1805. LARGE_INTEGER Freq;
  1806. QueryPerformanceFrequency(&Freq);
  1807. double nTimeDevice =
  1808. (double) (m_TimeDeviceEnd.QuadPart - m_TimeDeviceBegin.QuadPart) /
  1809. (double) Freq.QuadPart;
  1810. double nTimeProcess =
  1811. (double) (m_TimeProcessEnd.QuadPart - m_TimeProcessBegin.QuadPart) /
  1812. (double) Freq.QuadPart;
  1813. double nTimeClient =
  1814. (double) (m_TimeClientEnd.QuadPart - m_TimeClientBegin.QuadPart) /
  1815. (double) Freq.QuadPart;
  1816. Trace(
  1817. _T("TRANSFER_FROM_DEVICE = %.02lf secs\n")
  1818. _T("PROCESSING_DATA = %.02lf secs\n")
  1819. _T("TRANSFER_TO_CLIENT = %.02lf secs\n")
  1820. _T("\n"),
  1821. nTimeDevice,
  1822. nTimeProcess,
  1823. nTimeClient
  1824. );
  1825. }
  1826. #else //DBG
  1827. inline void CDataCallback::QueryStartTimes(LONG, LONG)
  1828. {
  1829. }
  1830. inline void CDataCallback::QueryStopTimes(LONG, LONG)
  1831. {
  1832. }
  1833. inline void CDataCallback::PrintTimes()
  1834. {
  1835. }
  1836. #endif //DBG
  1837. //////////////////////////////////////////////////////////////////////////
  1838. //
  1839. //
  1840. //
  1841. CProgressDialog::CProgressDialog()
  1842. {
  1843. m_cRef = 0;
  1844. }
  1845. CProgressDialog::~CProgressDialog()
  1846. {
  1847. // remove the "downloading..." message from the status bar
  1848. if (g_pStatBarWnd)
  1849. {
  1850. g_pStatBarWnd->SetPaneText(0, _T(""));
  1851. }
  1852. }
  1853. STDMETHODIMP CProgressDialog::QueryInterface(REFIID iid, LPVOID *ppvObj)
  1854. {
  1855. if (ppvObj == 0)
  1856. {
  1857. return E_POINTER;
  1858. }
  1859. if (iid == IID_IUnknown)
  1860. {
  1861. AddRef();
  1862. *ppvObj = (IUnknown*) this;
  1863. return S_OK;
  1864. }
  1865. if (iid == IID_IWiaProgressDialog)
  1866. {
  1867. AddRef();
  1868. *ppvObj = (IWiaProgressDialog *) this;
  1869. return S_OK;
  1870. }
  1871. *ppvObj = 0;
  1872. return E_NOINTERFACE;
  1873. }
  1874. STDMETHODIMP_(ULONG) CProgressDialog::AddRef()
  1875. {
  1876. return InterlockedIncrement(&m_cRef);
  1877. }
  1878. STDMETHODIMP_(ULONG) CProgressDialog::Release()
  1879. {
  1880. LONG cRef = InterlockedDecrement(&m_cRef);
  1881. if (cRef == 0)
  1882. {
  1883. delete this;
  1884. }
  1885. return cRef;
  1886. }
  1887. STDMETHODIMP CProgressDialog::Create(HWND hwndParent, LONG lFlags)
  1888. {
  1889. if (g_pStatBarWnd)
  1890. {
  1891. RECT r;
  1892. g_pStatBarWnd->GetItemRect(1, &r);
  1893. m_ProgressCtrl.Create(WS_CHILD | WS_VISIBLE, r, g_pStatBarWnd, 1);
  1894. m_ProgressCtrl.SetRange(0, 100);
  1895. }
  1896. return S_OK;
  1897. }
  1898. STDMETHODIMP CProgressDialog::Show()
  1899. {
  1900. m_ProgressCtrl.UpdateWindow();
  1901. return S_OK;
  1902. }
  1903. STDMETHODIMP CProgressDialog::Hide()
  1904. {
  1905. return S_OK;
  1906. }
  1907. STDMETHODIMP CProgressDialog::Cancelled(BOOL *pbCancelled)
  1908. {
  1909. *pbCancelled = FALSE;
  1910. return S_OK;
  1911. }
  1912. STDMETHODIMP CProgressDialog::SetTitle(LPCWSTR pszMessage)
  1913. {
  1914. return S_OK;
  1915. }
  1916. STDMETHODIMP CProgressDialog::SetMessage(LPCWSTR pszTitle)
  1917. {
  1918. if (g_pStatBarWnd)
  1919. {
  1920. USES_CONVERSION;
  1921. g_pStatBarWnd->SetPaneText(0, W2CT(pszTitle));
  1922. }
  1923. return S_OK;
  1924. }
  1925. STDMETHODIMP CProgressDialog::SetPercentComplete(UINT nPercent)
  1926. {
  1927. m_ProgressCtrl.SetPos(nPercent);
  1928. return S_OK;
  1929. }
  1930. STDMETHODIMP CProgressDialog::Destroy()
  1931. {
  1932. return S_OK;
  1933. }