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.

662 lines
19 KiB

  1. #include <windows.h>
  2. #include "resource.h"
  3. #include "wia.h"
  4. #include <atlbase.h>
  5. #include "classes.h"
  6. #define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
  7. CComPtr<IWiaDevMgr> g_pDevMgr;
  8. LARGE_INTEGER liTimerFreq;
  9. struct TESTENTRY
  10. {
  11. LPCTSTR szName;
  12. TESTPROC pfnTest;
  13. bool bRunMe;
  14. } TESTLIST[] =
  15. {
  16. {TEXT("CreateDevice only"), CTest::TstCreateDevice, false},
  17. {TEXT("Display Thumbnails"), CTest::TstShowThumbs, false},
  18. {TEXT("Enum device commands"), CTest::TstEnumCmds, false},
  19. {TEXT("idtGetData for all images"), CTest::TstDownload, false},
  20. {TEXT("idtGetBandedData for all images"), CTest::TstBandedDownload, false},
  21. };
  22. CPerfTest::CPerfTest () : m_hwnd(NULL), m_hEdit(NULL)
  23. {
  24. ZeroMemory (&m_settings, sizeof(m_settings));
  25. }
  26. static TCHAR cszWndClass[] = TEXT("TestWindow");
  27. static TCHAR cszWndName[] = TEXT("WIA Perf Measurement");
  28. bool
  29. CPerfTest::Init (HINSTANCE hInst)
  30. {
  31. WNDCLASS wc;
  32. m_hInst = hInst;
  33. // make sure WIA is around
  34. if (FAILED(CoCreateInstance (CLSID_WiaDevMgr,
  35. NULL,
  36. CLSCTX_LOCAL_SERVER,
  37. IID_IWiaDevMgr,
  38. reinterpret_cast<LPVOID*>(&g_pDevMgr))))
  39. {
  40. MessageBox (NULL,
  41. TEXT("Unable to create WIA!"),
  42. TEXT("WiaPerf Error"),
  43. MB_OK | MB_ICONSTOP);
  44. return false;
  45. }
  46. // verify high resolution timer available
  47. if (!QueryPerformanceFrequency(&liTimerFreq))
  48. {
  49. MessageBox (NULL,
  50. TEXT("No performance counter available."),
  51. TEXT("WiaPerf Error"),
  52. MB_OK | MB_ICONSTOP);
  53. return false;
  54. }
  55. ZeroMemory (&wc, sizeof(wc));
  56. wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);
  57. wc.hCursor = LoadCursor (NULL, IDC_ARROW);
  58. wc.hInstance = m_hInst;
  59. wc.lpfnWndProc = WndProc;
  60. wc.hIcon = LoadIcon (m_hInst, MAKEINTRESOURCE(IDI_APPICON));
  61. wc.lpszClassName = cszWndClass;
  62. wc.lpszMenuName = MAKEINTRESOURCE(IDM_MAINMENU);
  63. RegisterClass (&wc);
  64. m_hwnd = CreateWindow (cszWndClass,
  65. cszWndName,
  66. WS_OVERLAPPEDWINDOW | WS_VISIBLE |WS_CLIPCHILDREN,
  67. CW_USEDEFAULT, 0,
  68. CW_USEDEFAULT, 0,
  69. NULL,
  70. NULL,
  71. m_hInst,
  72. reinterpret_cast<LPVOID>(this));
  73. if (IsWindow (m_hwnd))
  74. {
  75. ShowWindow (m_hwnd, SW_SHOW);
  76. UpdateWindow (m_hwnd);
  77. return true;
  78. }
  79. return false;
  80. }
  81. LRESULT CALLBACK
  82. CPerfTest::WndProc (HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
  83. {
  84. CPerfTest *pThis;
  85. if (WM_CREATE == msg)
  86. {
  87. // store our "this" pointer
  88. SetWindowLongPtr (hwnd,
  89. GWLP_USERDATA,
  90. reinterpret_cast<LONG_PTR>(reinterpret_cast<LPCREATESTRUCT>(lp)->lpCreateParams));
  91. }
  92. pThis = reinterpret_cast<CPerfTest*>(GetWindowLongPtr(hwnd, GWLP_USERDATA));
  93. return pThis->RealWndProc (hwnd, msg, wp, lp);
  94. }
  95. LRESULT
  96. CPerfTest::RealWndProc (HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
  97. {
  98. switch (msg)
  99. {
  100. case WM_CREATE:
  101. m_hwnd = hwnd;
  102. return OnCreate ();
  103. case WM_COMMAND:
  104. return OnCommand (wp, lp);
  105. case WM_DESTROY:
  106. PostQuitMessage (0);
  107. return 0;
  108. case WM_SIZE:
  109. // size the edit control to match
  110. MoveWindow (m_hEdit, 0,0,LOWORD(lp), HIWORD(lp), TRUE);
  111. break;
  112. default:
  113. break;
  114. }
  115. return CallWindowProc (DefWindowProc, hwnd, msg, wp, lp);
  116. }
  117. LRESULT
  118. CPerfTest::OnCreate ()
  119. {
  120. // Create our child edit control. Used to display logging output
  121. m_hEdit = CreateWindow (TEXT("edit"), TEXT(""),
  122. WS_BORDER | ES_READONLY | WS_CHILD|WS_VISIBLE|ES_MULTILINE | WS_VSCROLL | WS_HSCROLL,
  123. 0,0,0,0,m_hwnd,
  124. reinterpret_cast<HMENU>(1),
  125. m_hInst,NULL);
  126. GetSettings ();
  127. return 0;
  128. }
  129. LRESULT
  130. CPerfTest::OnCommand (WPARAM wp, LPARAM lp)
  131. {
  132. switch (LOWORD(wp))
  133. {
  134. case IDM_TESTS_OPTIONS:
  135. GetSettings ();
  136. return 0;
  137. case IDM_TESTS_EXECUTE:
  138. RunTests ();
  139. return 0;
  140. case IDM_EXIT:
  141. DestroyWindow (m_hwnd);
  142. return 0;
  143. }
  144. return 1;
  145. }
  146. VOID
  147. CPerfTest::RunTests ()
  148. {
  149. UINT nRun;
  150. size_t nTest;
  151. UINT nDevice;
  152. CTest TheTest(&m_settings);
  153. for (nDevice=0; m_settings.pstrDevices[nDevice];nDevice++)
  154. {
  155. for (nRun = 0;nRun < m_settings.nIter; nRun++)
  156. {
  157. for (nTest=0;nTest<ARRAYSIZE(TESTLIST);nTest++)
  158. {
  159. if (TESTLIST[nTest].bRunMe)
  160. {
  161. (TESTLIST[nTest].pfnTest)(&TheTest, m_settings.pstrDevices[nDevice]);
  162. }
  163. }
  164. }
  165. TheTest.LogDevInfo (m_settings.pstrDevices[nDevice]);
  166. }
  167. }
  168. VOID
  169. CPerfTest::GetSettings ()
  170. {
  171. m_settings.hEdit = m_hEdit;
  172. if (m_settings.pstrDevices)
  173. {
  174. delete [] m_settings.pstrDevices;
  175. m_settings.pstrDevices = NULL;
  176. }
  177. DialogBoxParam (m_hInst,
  178. MAKEINTRESOURCE(IDD_OPTIONS),
  179. m_hwnd,
  180. SettingsDlgProc,
  181. reinterpret_cast<LPARAM>(&m_settings));
  182. // hide or show "Execute!" depending on settings
  183. HMENU hmenu = GetSubMenu (GetMenu(m_hwnd), 0);
  184. if (m_settings.nIter)
  185. {
  186. EnableMenuItem (hmenu, IDM_TESTS_EXECUTE, MF_BYCOMMAND|MF_ENABLED);
  187. }
  188. else
  189. {
  190. EnableMenuItem (hmenu, IDM_TESTS_EXECUTE, MF_BYCOMMAND|MF_DISABLED);
  191. }
  192. }
  193. INT_PTR CALLBACK
  194. SettingsDlgProc (HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
  195. {
  196. TESTSETTINGS *pSettings;
  197. pSettings = reinterpret_cast<TESTSETTINGS*>(GetWindowLongPtr(hwnd, DWLP_USER));
  198. switch (msg)
  199. {
  200. case WM_INITDIALOG:
  201. SetWindowLongPtr (hwnd, DWLP_USER, lp);
  202. pSettings = reinterpret_cast<TESTSETTINGS*>(lp);
  203. InitControls (hwnd, pSettings);
  204. return TRUE;
  205. case WM_COMMAND:
  206. if (IDOK == LOWORD(wp))
  207. {
  208. FillSettings (hwnd, pSettings);
  209. }
  210. if (IDOK == LOWORD(wp) || IDCANCEL == LOWORD(wp))
  211. {
  212. FreeDialogData (hwnd);
  213. EndDialog (hwnd, 1);
  214. return TRUE;
  215. }
  216. if (IDC_GETFILE == LOWORD(wp))
  217. {
  218. TCHAR szFileName[MAX_PATH];
  219. OPENFILENAME ofn;
  220. ZeroMemory (&ofn, sizeof(ofn));
  221. ofn.hInstance = GetModuleHandle (NULL);
  222. ofn.hwndOwner = hwnd;
  223. ofn.lpstrFile = szFileName;
  224. ofn.lpstrFilter = TEXT("LOG file\0*.log\0Text file\0*.txt\0");
  225. ofn.lpstrDefExt = TEXT("log");
  226. ofn.lStructSize = sizeof(ofn);
  227. ofn.nMaxFile = MAX_PATH;
  228. if (GetOpenFileName(&ofn))
  229. {
  230. SetDlgItemText (hwnd, IDC_LOGFILENAME, szFileName);
  231. }
  232. return TRUE;
  233. }
  234. return FALSE;
  235. }
  236. return FALSE;
  237. }
  238. VOID
  239. InitControls (HWND hwnd, TESTSETTINGS *pSettings)
  240. {
  241. //
  242. // First, enum the WIA devices available and put their names in the listbox
  243. //
  244. CComPtr<IEnumWIA_DEV_INFO> pEnum;
  245. CComPtr<IWiaPropertyStorage> pStg;
  246. PROPVARIANT pv[2];
  247. PROPSPEC ps[2];
  248. ULONG ul;
  249. BSTR strID;
  250. TCHAR szName[MAX_PATH];
  251. LRESULT lItem;
  252. if (FAILED(g_pDevMgr->EnumDeviceInfo(0, &pEnum)))
  253. {
  254. MessageBox (hwnd,
  255. TEXT("Unable to enum WIA devices!"),
  256. TEXT("WiaPerf Error"),
  257. MB_OK | MB_ICONSTOP);
  258. return;
  259. }
  260. ps[0].ulKind = ps[1].ulKind = PRSPEC_PROPID;
  261. ps[0].propid = WIA_DIP_DEV_NAME;
  262. ps[1].propid = WIA_DIP_DEV_ID;
  263. while (NOERROR == pEnum->Next (1, &pStg, &ul))
  264. {
  265. if (NOERROR == pStg->ReadMultiple (2, ps, pv))
  266. {
  267. strID = SysAllocString (pv[1].pwszVal);
  268. #ifdef UNICODE
  269. wcscpy (szName, pv[0].pwszVal);
  270. #else
  271. WideCharToMultiByte (CP_ACP, 0, pv[0].pwszVal, -1,
  272. szName, MAX_PATH, NULL, NULL);
  273. #endif
  274. lItem = SendDlgItemMessage (hwnd,
  275. IDC_TESTLIST,
  276. CB_ADDSTRING,
  277. 0,
  278. reinterpret_cast<LPARAM>(szName));
  279. if (lItem >= 0)
  280. {
  281. SendDlgItemMessage (hwnd,
  282. IDC_TESTLIST,
  283. CB_SETITEMDATA,
  284. lItem,
  285. reinterpret_cast<LPARAM>(strID));
  286. }
  287. }
  288. }
  289. SendDlgItemMessage (hwnd,
  290. IDC_TESTLIST,
  291. CB_SETCURSEL,
  292. 0, 0);
  293. // Fill the test names listbox
  294. for (INT i=0;i<ARRAYSIZE(TESTLIST);i++)
  295. {
  296. lItem = SendDlgItemMessage (hwnd,
  297. IDC_TESTS,
  298. LB_ADDSTRING,
  299. 0,
  300. reinterpret_cast<LPARAM>(TESTLIST[i].szName));
  301. SendDlgItemMessage (hwnd,
  302. IDC_TESTS,
  303. LB_SETITEMDATA,
  304. lItem,
  305. reinterpret_cast<LPARAM>(TESTLIST[i].pfnTest));
  306. }
  307. CheckDlgButton (hwnd, IDC_EXIT, pSettings->bExit);
  308. //
  309. // Set some defaults
  310. CheckDlgButton (hwnd, IDC_LOGFILE, pSettings->fLogMask & LOG_FILE);
  311. SetDlgItemText (hwnd, IDC_LOGFILENAME, pSettings->szLogFile);
  312. CheckDlgButton (hwnd, IDC_LOGTIMES, pSettings->fLogMask & LOG_TIME);
  313. SetDlgItemInt (hwnd, ID_ITERATIONS,pSettings->nIter ? pSettings->nIter : 1, FALSE);
  314. }
  315. VOID
  316. FillSettings (HWND hwnd, TESTSETTINGS *pSettings)
  317. {
  318. //
  319. // Read the list of device id's selected
  320. //
  321. BOOL bAddAll = IsDlgButtonChecked (hwnd, IDC_TESTALL);
  322. if (bAddAll)
  323. {
  324. LRESULT lDevices = SendDlgItemMessage (hwnd, IDC_TESTLIST,
  325. CB_GETCOUNT, 0, 0);
  326. if (lDevices > 0)
  327. {
  328. pSettings->pstrDevices = new BSTR[lDevices+1];
  329. for (LRESULT i=0;i<lDevices;i++)
  330. {
  331. pSettings->pstrDevices[i] = reinterpret_cast<BSTR>(SendDlgItemMessage (
  332. hwnd,
  333. IDC_TESTLIST,
  334. CB_GETITEMDATA,
  335. i, 0));
  336. }
  337. pSettings->pstrDevices[i] = NULL;
  338. }
  339. }
  340. else
  341. {
  342. LRESULT lItem = SendDlgItemMessage (hwnd, IDC_TESTLIST, CB_GETCURSEL, 0, 0);
  343. if (lItem >=0 )
  344. {
  345. pSettings->pstrDevices = new BSTR[2];
  346. pSettings->pstrDevices[0] = reinterpret_cast<BSTR>(SendDlgItemMessage (
  347. hwnd,
  348. IDC_TESTLIST,
  349. CB_GETITEMDATA,
  350. lItem,
  351. 0));
  352. pSettings->pstrDevices[1] = NULL;
  353. }
  354. }
  355. //
  356. // Get the log settings
  357. //
  358. pSettings->fLogMask = LOG_WINDOW_ONLY;
  359. if (IsDlgButtonChecked (hwnd, IDC_LOGALL))
  360. {
  361. pSettings->fLogMask |= LOG_APIS;
  362. }
  363. if (IsDlgButtonChecked (hwnd, IDC_LOGTIMES))
  364. {
  365. pSettings->fLogMask |= LOG_TIME;
  366. }
  367. if (IsDlgButtonChecked (hwnd, IDC_LOGFILE))
  368. {
  369. pSettings->fLogMask |= LOG_FILE;
  370. }
  371. // get the file path
  372. GetDlgItemText (hwnd, IDC_LOGFILENAME, pSettings->szLogFile, MAX_PATH);
  373. // how many times to run
  374. pSettings->nIter = GetDlgItemInt (hwnd, ID_ITERATIONS, NULL, FALSE);
  375. // Whether to exit on test complete
  376. pSettings->bExit = IsDlgButtonChecked (hwnd, IDC_EXIT);
  377. //
  378. // Cycle through the tests in the list and check which ones to run
  379. for (size_t i=0;i<ARRAYSIZE(TESTLIST);i++)
  380. {
  381. if (SendDlgItemMessage (hwnd, IDC_TESTS, LB_GETSEL, i, 0) > 0)
  382. {
  383. TESTLIST[i].bRunMe = true;
  384. }
  385. else
  386. {
  387. TESTLIST[i].bRunMe = false;
  388. }
  389. }
  390. }
  391. VOID FreeDialogData (HWND hwnd)
  392. {
  393. LRESULT lDevices = SendDlgItemMessage (hwnd, IDC_TESTLIST,
  394. CB_GETCOUNT, 0, 0);
  395. BSTR str;
  396. if (lDevices > 0)
  397. {
  398. for (LRESULT i=0;i<lDevices;i++)
  399. {
  400. str = reinterpret_cast<BSTR>(SendDlgItemMessage (hwnd,
  401. IDC_TESTLIST,
  402. CB_GETITEMDATA,
  403. i, 0));
  404. SysFreeString (str);
  405. }
  406. }
  407. }
  408. CTest::CTest (TESTSETTINGS *pSettings)
  409. {
  410. m_pSettings = pSettings;
  411. OpenLogFile ();
  412. }
  413. CTest::~CTest ()
  414. {
  415. CloseLogFile ();
  416. }
  417. void
  418. CTest::OpenLogFile ()
  419. {
  420. m_hLogFile = CreateFile (m_pSettings->szLogFile,
  421. GENERIC_WRITE,
  422. FILE_SHARE_READ,
  423. NULL,
  424. CREATE_ALWAYS,
  425. FILE_ATTRIBUTE_NORMAL,
  426. NULL);
  427. #ifdef UNICODE
  428. // write the UNICODE header
  429. if (m_hLogFile != INVALID_HANDLE_VALUE)
  430. {
  431. WCHAR bom = 0xFEFF;
  432. DWORD dw;
  433. WriteFile (m_hLogFile, &bom, sizeof(WCHAR), &dw, NULL);
  434. }
  435. #endif
  436. LogString (TEXT("******* WIA Perf Test Starting. ********"));
  437. }
  438. void
  439. CTest::CloseLogFile ()
  440. {
  441. LogString (TEXT("******* WIA Perf Test Ending. *******"));
  442. if (INVALID_HANDLE_VALUE != m_hLogFile)
  443. {
  444. CloseHandle (m_hLogFile);
  445. }
  446. }
  447. // LogTime assumes the number of seconds will fit in a long
  448. //
  449. void
  450. CTest::LogTime (LPTSTR szAction,LARGE_INTEGER &liTimeElapsed)
  451. {
  452. LARGE_INTEGER liSeconds;
  453. if (m_pSettings->fLogMask & LOG_TIME)
  454. {
  455. liSeconds.QuadPart = (1000*liTimeElapsed.QuadPart)/liTimerFreq.QuadPart;
  456. LogString (TEXT("Time for %s:%lu milliseconds"), szAction, liSeconds.LowPart);
  457. }
  458. }
  459. void
  460. CTest::LogAPI (LPTSTR szAPI, HRESULT hr)
  461. {
  462. if (m_pSettings->fLogMask & LOG_APIS)
  463. {
  464. if (FAILED(hr))
  465. {
  466. LogString (TEXT("API call: %s failed %x"), szAPI, hr);
  467. }
  468. else
  469. {
  470. LogString (TEXT("API call: %s succeeded %x"), szAPI, hr);
  471. }
  472. }
  473. }
  474. void
  475. CTest::LogString (LPTSTR sz, ...)
  476. {
  477. TCHAR szOut[1024];
  478. va_list args;
  479. va_start (args, sz);
  480. wvsprintf (szOut, sz, args);
  481. va_end(args);
  482. // Note that we can write past the end of this buffer.
  483. lstrcat (szOut, TEXT("\r\n"));
  484. SendMessage (m_pSettings->hEdit,
  485. EM_REPLACESEL,
  486. 0,
  487. reinterpret_cast<LPARAM>(szOut));
  488. if (m_pSettings->fLogMask & LOG_FILE && INVALID_HANDLE_VALUE != m_hLogFile)
  489. {
  490. DWORD dw;
  491. WriteFile (m_hLogFile, szOut, sizeof(TCHAR)*(lstrlen(szOut)+1), &dw, NULL);
  492. }
  493. }
  494. VOID
  495. CTest::TstCreateDevice (CTest *pThis, BSTR strDeviceId)
  496. {
  497. LARGE_INTEGER liStart;
  498. LARGE_INTEGER liEnd;
  499. HRESULT hr;
  500. CComPtr<IWiaItem> pRoot;
  501. pThis->LogString (TEXT("--> Start test for CreateDevice"));
  502. QueryPerformanceCounter (&liStart);
  503. hr = g_pDevMgr->CreateDevice (strDeviceId, &pRoot);
  504. QueryPerformanceCounter (&liEnd);
  505. pRoot = NULL;
  506. liEnd.QuadPart = liEnd.QuadPart-liStart.QuadPart;
  507. pThis->LogTime (TEXT("CreateDevice"), liEnd);
  508. pThis->LogAPI (TEXT("IWiaDevMgr::CreateDevice"), hr);
  509. pThis->LogString (TEXT("<-- End test for CreateDevice"));
  510. }
  511. VOID
  512. CTest::LogDevInfo(BSTR strDeviceId)
  513. {
  514. CComPtr<IWiaItem> pRoot;
  515. if (SUCCEEDED(g_pDevMgr->CreateDevice (strDeviceId, &pRoot)))
  516. {
  517. CComQIPtr<IWiaPropertyStorage, &IID_IWiaPropertyStorage> pstg(pRoot);
  518. PROPVARIANT pv[3];
  519. PROPSPEC ps[3];
  520. ps[0].ulKind = ps[1].ulKind = ps[2].ulKind = PRSPEC_PROPID;
  521. ps[0].propid = WIA_DIP_DEV_NAME;
  522. ps[1].propid = WIA_DPC_PICTURES_TAKEN;
  523. ps[2].propid = WIA_DIP_PORT_NAME;
  524. if (NOERROR == pstg->ReadMultiple (3, ps, pv))
  525. {
  526. LogString (TEXT("Device ID : %ls"), strDeviceId);
  527. LogString (TEXT("Device Name : %ls"), pv[0].pwszVal);
  528. LogString (TEXT("On Port : %ls"), pv[2].pwszVal);
  529. LogString (TEXT("Number of stored images : %d"), pv[1].ulVal);
  530. }
  531. }
  532. }
  533. // Enumerate commands supported by the device
  534. VOID
  535. CTest::TstEnumCmds (CTest *pThis, BSTR strDeviceId)
  536. {
  537. LARGE_INTEGER liStart;
  538. LARGE_INTEGER liEnd;
  539. HRESULT hr;
  540. CComPtr<IWiaItem> pRoot;
  541. pThis->LogString (TEXT("--> Start test for EnumWIA_DEV_CAPS(WIA_DEVICE_COMMANDS)"));
  542. hr = g_pDevMgr->CreateDevice (strDeviceId, &pRoot);
  543. pThis->LogAPI(TEXT("IWiaDevMgr::CreateDevice"), hr);
  544. if (SUCCEEDED(hr))
  545. {
  546. CComPtr<IEnumWIA_DEV_CAPS> pCaps;
  547. WIA_DEV_CAP wdc;
  548. TCHAR sz[200];
  549. DWORD dwCmds = 0;
  550. DWORD dw;
  551. QueryPerformanceCounter (&liStart);
  552. hr = pRoot->EnumDeviceCapabilities (WIA_DEVICE_COMMANDS, &pCaps);
  553. QueryPerformanceCounter (&liEnd);
  554. liEnd.QuadPart = liEnd.QuadPart - liStart.QuadPart;
  555. pThis->LogTime (TEXT("IWiaItem::EnumDeviceCapabilities (WIA_DEVICE_COMMANDS)"), liEnd);
  556. pThis->LogAPI (TEXT("IWiaItem::EnumDeviceCapabilities (WIA_DEVICE_COMMANDS)"), hr);
  557. QueryPerformanceCounter (&liStart);
  558. while (NOERROR == hr)
  559. {
  560. hr = pCaps->Next (1, &wdc, &dw);
  561. dwCmds+=dw;
  562. if (wdc.bstrCommandline)
  563. {
  564. SysFreeString (wdc.bstrCommandline);
  565. }
  566. if (wdc.bstrDescription)
  567. {
  568. SysFreeString (wdc.bstrDescription);
  569. }
  570. if (wdc.bstrIcon)
  571. {
  572. SysFreeString (wdc.bstrIcon);
  573. }
  574. }
  575. QueryPerformanceCounter (&liEnd);
  576. liEnd.QuadPart = liEnd.QuadPart - liStart.QuadPart;
  577. wsprintf (sz, TEXT("%d commands from IEnumWIA_DEV_CAPS::Next"), dwCmds);
  578. pThis->LogTime (sz, liEnd);
  579. }
  580. pThis->LogString (TEXT("<-- End test for EnumWIA_DEV_CAPS(WIA_DEVICE_COMMANDS)"));
  581. }