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.

960 lines
20 KiB

  1. //
  2. // Simple test program for imaging library
  3. //
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6. #include <stdarg.h>
  7. #include <windows.h>
  8. #include <objbase.h>
  9. #include <urlmon.h>
  10. #include <commdlg.h>
  11. #include <imaging.h>
  12. #include <initguid.h>
  13. #include <imgguids.h>
  14. CHAR* programName; // program name
  15. HINSTANCE appInstance; // handle to the application instance
  16. HWND hwndMain; // handle to application's main window
  17. IImagingFactory* imgfact; // pointer to IImageingFactory object
  18. IImage* curimage; // pointer to IImage object
  19. CHAR curFilename[MAX_PATH]; // current image filename
  20. IImageDecoder *decoder;
  21. IBitmapImage *bitmap;
  22. ImageInfo imageinfo;
  23. BOOL hastimedimension, loopingset, viewagain;
  24. UINT numframes, lastsuccframe, currentframe, delay;
  25. INT loopcount;
  26. //
  27. // Display an error message dialog
  28. //
  29. BOOL
  30. CheckHRESULT(
  31. HRESULT hr,
  32. INT line
  33. )
  34. {
  35. if (SUCCEEDED(hr))
  36. return TRUE;
  37. CHAR buf[1024];
  38. sprintf(buf, "Error on line %d: 0x%x\n", line, hr);
  39. MessageBoxA(hwndMain, buf, programName, MB_OK);
  40. return FALSE;
  41. }
  42. #define CHECKHR(hr) CheckHRESULT(hr, __LINE__)
  43. #define LASTWIN32HRESULT HRESULT_FROM_WIN32(GetLastError())
  44. #if DBG
  45. #define VERBOSE(args) printf args
  46. #else
  47. #define VERBOSE(args)
  48. #endif
  49. //
  50. // Helper class to convert ANSI strings to Unicode strings
  51. //
  52. inline BOOL
  53. UnicodeToAnsiStr(
  54. const WCHAR* unicodeStr,
  55. CHAR* ansiStr,
  56. INT ansiSize
  57. )
  58. {
  59. return WideCharToMultiByte(
  60. CP_ACP,
  61. 0,
  62. unicodeStr,
  63. -1,
  64. ansiStr,
  65. ansiSize,
  66. NULL,
  67. NULL) > 0;
  68. }
  69. inline BOOL
  70. AnsiToUnicodeStr(
  71. const CHAR* ansiStr,
  72. WCHAR* unicodeStr,
  73. INT unicodeSize
  74. )
  75. {
  76. return MultiByteToWideChar(
  77. CP_ACP,
  78. 0,
  79. ansiStr,
  80. -1,
  81. unicodeStr,
  82. unicodeSize) > 0;
  83. }
  84. class UnicodeStrFromAnsi
  85. {
  86. public:
  87. UnicodeStrFromAnsi(const CHAR* ansiStr)
  88. {
  89. if (ansiStr == NULL)
  90. {
  91. valid = TRUE;
  92. unicodeStr = NULL;
  93. }
  94. else
  95. {
  96. // NOTE: we only handle strings with length < MAX_PATH.
  97. valid = AnsiToUnicodeStr(ansiStr, buf, MAX_PATH);
  98. unicodeStr = valid ? buf : NULL;
  99. }
  100. }
  101. BOOL IsValid() const
  102. {
  103. return valid;
  104. }
  105. operator WCHAR*()
  106. {
  107. return unicodeStr;
  108. }
  109. private:
  110. BOOL valid;
  111. WCHAR* unicodeStr;
  112. WCHAR buf[MAX_PATH];
  113. };
  114. //
  115. // Convert current image to a bitmap image
  116. //
  117. IBitmapImage*
  118. ConvertImageToBitmap(
  119. IImage* image,
  120. INT width = 0,
  121. INT height = 0,
  122. PixelFormatID pixfmt = PIXFMT_DONTCARE,
  123. InterpolationHint hint = INTERP_DEFAULT
  124. )
  125. {
  126. if (!image)
  127. return NULL;
  128. HRESULT hr;
  129. IBitmapImage* bmp;
  130. hr = image->QueryInterface(IID_IBitmapImage, (VOID**) &bmp);
  131. if (SUCCEEDED(hr))
  132. {
  133. SIZE size;
  134. PixelFormatID fmt;
  135. // Current image is already a bitmap image and
  136. // its dimension and pixel format are already as expected
  137. hr = bmp->GetSize(&size);
  138. if (!CHECKHR(hr))
  139. return NULL;
  140. hr = bmp->GetPixelFormatID(&fmt);
  141. if (!CHECKHR(hr))
  142. return NULL;
  143. if ((width == 0 || size.cx == width) &&
  144. (height == 0 || size.cy == height) &&
  145. (pixfmt == PIXFMT_DONTCARE || pixfmt == fmt))
  146. {
  147. return bmp;
  148. }
  149. bmp->Release();
  150. }
  151. // Convert the current image to a bitmap image
  152. if (width == 0 && height == 0)
  153. {
  154. ImageInfo imageInfo;
  155. hr = image->GetImageInfo(&imageInfo);
  156. // If the source image is scalable, then compute
  157. // the appropriate pixel dimension for the bitmap
  158. if (SUCCEEDED(hr) && (imageInfo.Flags & IMGFLAG_SCALABLE))
  159. {
  160. width = (INT) (96.0 * imageInfo.Width / imageInfo.Xdpi + 0.5);
  161. height = (INT) (96.0 * imageInfo.Height / imageInfo.Ydpi + 0.5);
  162. }
  163. }
  164. hr = imgfact->CreateBitmapFromImage(
  165. image,
  166. width,
  167. height,
  168. pixfmt,
  169. hint,
  170. &bmp);
  171. return SUCCEEDED(hr) ? bmp : NULL;
  172. }
  173. //
  174. // Get pixel format strings
  175. //
  176. const CHAR*
  177. GetPixelFormatStr(
  178. PixelFormatID pixfmt
  179. )
  180. {
  181. switch (pixfmt)
  182. {
  183. case PIXFMT_8BPP_INDEXED: return "8bpp indexed";
  184. case PIXFMT_16BPP_GRAYSCALE: return "16bpp grayscale";
  185. case PIXFMT_16BPP_RGB555: return "16bpp RGB 5-5-5";
  186. case PIXFMT_16BPP_RGB565: return "16bpp RGB 5-6-5";
  187. case PIXFMT_16BPP_ARGB1555: return "16bpp ARGB 1-5-5-5";
  188. case PIXFMT_24BPP_RGB: return "24bpp RGB";
  189. case PIXFMT_32BPP_RGB: return "32bpp RGB";
  190. case PIXFMT_32BPP_ARGB: return "32bpp ARGB";
  191. case PIXFMT_32BPP_PARGB: return "32bpp premultiplied ARGB";
  192. case PIXFMT_48BPP_RGB: return "48bpp RGB";
  193. case PIXFMT_64BPP_ARGB: return "64bpp ARGB";
  194. case PIXFMT_64BPP_PARGB: return "64bpp premultiplied ARGB";
  195. case PIXFMT_UNDEFINED:
  196. default: return "Unknown";
  197. }
  198. }
  199. //
  200. // Force a refresh of the image window
  201. //
  202. inline VOID RefreshImageDisplay()
  203. {
  204. InvalidateRect(hwndMain, NULL, FALSE);
  205. // Update window title
  206. CHAR title[2*MAX_PATH];
  207. CHAR* p = title;
  208. strcpy(p, curFilename);
  209. SetWindowText(hwndMain, title);
  210. }
  211. //
  212. // Decodes the specified frame and sets it up for drawing
  213. //
  214. HRESULT
  215. DrawFrame(UINT frame)
  216. {
  217. HRESULT hresult;
  218. if (hastimedimension)
  219. {
  220. if (numframes != -1 && frame > numframes)
  221. {
  222. return IMGERR_NOFRAME;
  223. }
  224. GUID guid = FRAMEDIM_TIME;
  225. hresult = decoder->SelectActiveFrame(&guid, frame);
  226. if (FAILED(hresult))
  227. return hresult;
  228. lastsuccframe = frame;
  229. IPropertySetStorage *propsetstorage;
  230. hresult = decoder->GetProperties(&propsetstorage);
  231. if (FAILED(hresult))
  232. propsetstorage = NULL;
  233. IPropertyStorage *propstorage;
  234. if (propsetstorage)
  235. {
  236. hresult = propsetstorage->Open(FMTID_ImageInformation, STGM_READ |
  237. STGM_SHARE_EXCLUSIVE, &propstorage);
  238. if (FAILED(hresult))
  239. propstorage = NULL;
  240. }
  241. if (propstorage)
  242. {
  243. PROPSPEC propspec[2];
  244. PROPVARIANT propvariant[2];
  245. propspec[0].ulKind = PRSPEC_LPWSTR;
  246. propspec[0].lpwstr = L"Frame delay";
  247. propspec[1].ulKind = PRSPEC_LPWSTR;
  248. propspec[1].lpwstr = L"Loop count";
  249. hresult = propstorage->ReadMultiple(2, propspec, propvariant);
  250. propstorage->Release();
  251. if (SUCCEEDED(hresult))
  252. {
  253. if (propvariant[0].vt != VT_EMPTY)
  254. delay = propvariant[0].uiVal;
  255. else
  256. delay = 0;
  257. if (!loopingset)
  258. {
  259. if (propvariant[1].vt != VT_EMPTY)
  260. {
  261. loopcount = propvariant[1].iVal;
  262. }
  263. else
  264. {
  265. loopcount = 0;
  266. }
  267. loopingset = TRUE;
  268. }
  269. }
  270. else
  271. {
  272. delay = 0;
  273. }
  274. }
  275. }
  276. IImageSink *sink;
  277. bitmap->QueryInterface(IID_IImageSink, (void**)&sink);
  278. hresult = decoder->BeginDecode(sink, NULL);
  279. sink->Release();
  280. if (FAILED(hresult))
  281. return hresult;
  282. hresult = decoder->Decode();
  283. if (FAILED(hresult))
  284. return hresult;
  285. hresult = decoder->EndDecode(S_OK);
  286. if (FAILED(hresult))
  287. return hresult;
  288. if (curimage)
  289. {
  290. curimage->Release();
  291. curimage = NULL;
  292. }
  293. bitmap->QueryInterface(IID_IImage, (void**)&curimage);
  294. return S_OK;
  295. }
  296. //
  297. // Sets us the app for decompressing multiple frames
  298. //
  299. VOID
  300. SetCurrentImage()
  301. {
  302. HRESULT hresult;
  303. hresult = decoder->GetImageInfo(&imageinfo);
  304. if (FAILED(hresult))
  305. return;
  306. if (bitmap)
  307. {
  308. bitmap->Release();
  309. bitmap = NULL;
  310. }
  311. imgfact->CreateNewBitmap(imageinfo.Width, imageinfo.Height, PIXFMT_32BPP_ARGB, &bitmap);
  312. UINT count;
  313. GUID *dimensions;
  314. hastimedimension = FALSE;
  315. hresult = decoder->QueryFrameDimensions(&count, &dimensions);
  316. if (SUCCEEDED(hresult))
  317. {
  318. for (UINT i=0;i<count;i++)
  319. {
  320. if (dimensions[i] == FRAMEDIM_TIME)
  321. {
  322. hastimedimension = TRUE;
  323. }
  324. }
  325. } else if (hresult != E_NOTIMPL) {
  326. return;
  327. }
  328. DrawFrame(0);
  329. SetTimer(hwndMain, 0, delay*10, NULL);
  330. RefreshImageDisplay();
  331. }
  332. //
  333. // Resize the window so it fits the image
  334. //
  335. #define MINWINWIDTH 200
  336. #define MINWINHEIGHT 100
  337. #define MAXWINWIDTH 1024
  338. #define MAXWINHEIGHT 768
  339. VOID
  340. DoSizeWindowToFit(
  341. HWND hwnd,
  342. BOOL strict = FALSE
  343. )
  344. {
  345. HRESULT hr;
  346. IBitmapImage* bmp;
  347. SIZE size;
  348. // Check if the current image is a bitmap image
  349. // in that case, we'll get the pixel dimension
  350. hr = curimage->QueryInterface(IID_IBitmapImage, (VOID**) &bmp);
  351. if (SUCCEEDED(hr))
  352. {
  353. hr = bmp->GetSize(&size);
  354. bmp->Release();
  355. }
  356. // Otherwise, try to get device-independent image dimension
  357. if (FAILED(hr))
  358. {
  359. hr = curimage->GetPhysicalDimension(&size);
  360. if (FAILED(hr))
  361. return;
  362. size.cx = (INT) (size.cx * 96.0 / 2540.0 + 0.5);
  363. size.cy = (INT) (size.cy * 96.0 / 2540.0 + 0.5);
  364. }
  365. if (SUCCEEDED(hr))
  366. {
  367. // Figure out window border dimensions
  368. RECT r1, r2;
  369. INT w, h;
  370. w = size.cx;
  371. h = size.cy;
  372. if (!strict)
  373. {
  374. if (w < MINWINWIDTH)
  375. w = MINWINWIDTH;
  376. else if (w > MAXWINWIDTH)
  377. w = MAXWINWIDTH;
  378. if (h < MINWINHEIGHT)
  379. h = MINWINHEIGHT;
  380. else if (h > MAXWINHEIGHT)
  381. h = MAXWINHEIGHT;
  382. }
  383. GetWindowRect(hwnd, &r1);
  384. GetClientRect(hwnd, &r2);
  385. w += (r1.right - r1.left) - (r2.right - r2.left);
  386. h += (r1.bottom - r1.top) - (r2.bottom - r2.top);
  387. // Resize the window
  388. do
  389. {
  390. SetWindowPos(
  391. hwnd,
  392. NULL,
  393. 0, 0,
  394. w, h,
  395. SWP_NOMOVE | SWP_NOZORDER);
  396. GetClientRect(hwnd, &r2);
  397. h += GetSystemMetrics(SM_CYMENU);
  398. }
  399. while (r2.bottom == 0);
  400. }
  401. }
  402. //
  403. // Create an image object from a file
  404. //
  405. VOID
  406. OpenImageFile(
  407. const CHAR* filename
  408. )
  409. {
  410. HRESULT hr;
  411. IStream* stream;
  412. // Use URLMON.DLL to turn file into stream
  413. CHAR fullpath[MAX_PATH];
  414. CHAR* p;
  415. if (!GetFullPathName(filename, MAX_PATH, fullpath, &p))
  416. return;
  417. hr = URLOpenBlockingStreamA(NULL, fullpath, &stream, 0, NULL);
  418. if (!CHECKHR(hr))
  419. return;
  420. if (decoder)
  421. {
  422. decoder->TerminateDecoder();
  423. decoder->Release();
  424. decoder = NULL;
  425. }
  426. hr = imgfact->CreateImageDecoder(stream, DECODERINIT_NONE, &decoder);
  427. stream->Release();
  428. // Set the new image as the current image
  429. if (CHECKHR(hr))
  430. {
  431. SetCurrentImage();
  432. DoSizeWindowToFit(hwndMain);
  433. }
  434. }
  435. //
  436. // Handle window repaint event
  437. //
  438. VOID
  439. DoPaint(
  440. HWND hwnd
  441. )
  442. {
  443. HDC hdc;
  444. PAINTSTRUCT ps;
  445. RECT rect;
  446. DWORD timer;
  447. HRESULT hr = E_FAIL;
  448. hdc = BeginPaint(hwnd, &ps);
  449. GetClientRect(hwnd, &rect);
  450. SetStretchBltMode(hdc, COLORONCOLOR);
  451. timer = GetTickCount();
  452. IBitmapImage* bmp;
  453. bmp = ConvertImageToBitmap(
  454. curimage,
  455. rect.right,
  456. rect.bottom,
  457. PIXFMT_32BPP_ARGB,
  458. INTERP_BICUBIC);
  459. if (!bmp)
  460. goto endPaint;
  461. //VERBOSE(("Stretch time: %dms, ", GetTickCount() - timer));
  462. IImage* image;
  463. hr = bmp->QueryInterface(IID_IImage, (VOID**) &image);
  464. bmp->Release();
  465. if (FAILED(hr))
  466. goto endPaint;
  467. //timer = GetTickCount();
  468. hr = image->Draw(hdc, &rect, NULL);
  469. //VERBOSE(("GDI time: %dms\n", GetTickCount() - timer));
  470. image->Release();
  471. endPaint:
  472. if (FAILED(hr))
  473. FillRect(hdc, &rect, (HBRUSH) GetStockObject(BLACK_BRUSH));
  474. EndPaint(hwnd, &ps);
  475. }
  476. //
  477. // Compose a file type filter string given an array of
  478. // ImageCodecInfo structures
  479. //
  480. #define SizeofWSTR(s) (sizeof(WCHAR) * (wcslen(s) + 1))
  481. #define SizeofSTR(s) (strlen(s) + 1)
  482. CHAR*
  483. MakeFilterFromCodecs(
  484. UINT count,
  485. const ImageCodecInfo* codecs,
  486. BOOL open
  487. )
  488. {
  489. static const CHAR allFiles[] = "All Files\0*.*\0";
  490. // Figure out the total size of the filter string
  491. UINT index, size;
  492. for (index=size=0; index < count; index++)
  493. {
  494. size += SizeofWSTR(codecs[index].FormatDescription) +
  495. SizeofWSTR(codecs[index].FilenameExtension);
  496. }
  497. if (open)
  498. size += sizeof(allFiles);
  499. size += sizeof(CHAR);
  500. // Allocate memory
  501. CHAR *filter = (CHAR*) malloc(size);
  502. CHAR* p = filter;
  503. const WCHAR* ws;
  504. if (!filter)
  505. return NULL;
  506. for (index=0; index < count; index++)
  507. {
  508. ws = codecs[index].FormatDescription;
  509. size = SizeofWSTR(ws);
  510. if (UnicodeToAnsiStr(ws, p, size))
  511. p += SizeofSTR(p);
  512. else
  513. break;
  514. ws = codecs[index].FilenameExtension;
  515. size = SizeofWSTR(ws);
  516. if (UnicodeToAnsiStr(ws, p, size))
  517. p += SizeofSTR(p);
  518. else
  519. break;
  520. }
  521. if (index < count)
  522. {
  523. free(filter);
  524. return NULL;
  525. }
  526. if (open)
  527. {
  528. size = sizeof(allFiles);
  529. memcpy(p, allFiles, size);
  530. p += size;
  531. }
  532. *((CHAR*) p) = '\0';
  533. return filter;
  534. }
  535. //
  536. // Open image file
  537. //
  538. VOID
  539. DoOpen(
  540. HWND hwnd
  541. )
  542. {
  543. OPENFILENAME ofn;
  544. CHAR filename[MAX_PATH];
  545. ZeroMemory(&ofn, sizeof(ofn));
  546. ofn.lStructSize = sizeof(ofn);
  547. ofn.hwndOwner = hwnd;
  548. ofn.hInstance = appInstance;
  549. ofn.lpstrFile = filename;
  550. ofn.nMaxFile = MAX_PATH;
  551. ofn.lpstrTitle = "Open Image File";
  552. ofn.lpstrInitialDir = ".";
  553. ofn.Flags = OFN_FILEMUSTEXIST;
  554. filename[0] = '\0';
  555. // Make up the file type filter string
  556. HRESULT hr;
  557. ImageCodecInfo* codecs;
  558. UINT count;
  559. hr = imgfact->GetInstalledDecoders(&count, &codecs);
  560. if (!CHECKHR(hr))
  561. return;
  562. CHAR* filter = MakeFilterFromCodecs(count, codecs, TRUE);
  563. if (codecs)
  564. CoTaskMemFree(codecs);
  565. if (!filter)
  566. {
  567. CHECKHR(LASTWIN32HRESULT);
  568. return;
  569. }
  570. ofn.lpstrFilter = filter;
  571. // Present the file/open dialog
  572. if (GetOpenFileName(&ofn))
  573. OpenImageFile(filename);
  574. free(filter);
  575. }
  576. //
  577. //Figures out which frame to draw next and draws it.
  578. //
  579. void
  580. NextFrame()
  581. {
  582. BOOL tryagain = TRUE;
  583. while (tryagain)
  584. {
  585. tryagain = FALSE;
  586. HRESULT hresult = DrawFrame(currentframe);
  587. if (SUCCEEDED(hresult))
  588. {
  589. if (viewagain)
  590. currentframe++;
  591. }
  592. else if (hresult == IMGERR_NOFRAME)
  593. {
  594. if (currentframe > 0)
  595. {
  596. if (loopcount != 0)
  597. {
  598. if (loopcount > 0)
  599. loopcount--;
  600. currentframe = 0;
  601. tryagain = TRUE;
  602. }
  603. else
  604. {
  605. currentframe--;
  606. tryagain = TRUE;
  607. viewagain = FALSE;
  608. }
  609. }
  610. else
  611. {
  612. printf("No frames are displayable.\n");
  613. exit(1);
  614. }
  615. }
  616. }
  617. }
  618. //
  619. // Window callback procedure
  620. //
  621. LRESULT CALLBACK
  622. MyWindowProc(
  623. HWND hwnd,
  624. UINT uMsg,
  625. WPARAM wParam,
  626. LPARAM lParam
  627. )
  628. {
  629. switch (uMsg)
  630. {
  631. case WM_KEYDOWN:
  632. //For debugging
  633. //NextFrame();
  634. //RefreshImageDisplay();
  635. break;
  636. case WM_PAINT:
  637. DoPaint(hwnd);
  638. break;
  639. case WM_DESTROY:
  640. PostQuitMessage(0);
  641. break;
  642. case WM_TIMER:
  643. {
  644. KillTimer(hwndMain, 0);
  645. NextFrame();
  646. RefreshImageDisplay();
  647. if (viewagain)
  648. SetTimer(hwndMain, 0, delay*10, NULL);
  649. break;
  650. }
  651. default:
  652. return DefWindowProc(hwnd, uMsg, wParam, lParam);
  653. }
  654. return 0;
  655. }
  656. //
  657. // Create main application window
  658. //
  659. #define MYWNDCLASSNAME "AniTest"
  660. VOID
  661. CreateMainWindow(
  662. VOID
  663. )
  664. {
  665. //
  666. // Register window class
  667. //
  668. WNDCLASS wndClass =
  669. {
  670. CS_HREDRAW|CS_VREDRAW,
  671. MyWindowProc,
  672. 0,
  673. 0,
  674. appInstance,
  675. LoadIcon(NULL, IDI_APPLICATION),
  676. LoadCursor(NULL, IDC_ARROW),
  677. NULL,
  678. NULL,
  679. MYWNDCLASSNAME
  680. };
  681. RegisterClass(&wndClass);
  682. hwndMain = CreateWindow(
  683. MYWNDCLASSNAME,
  684. MYWNDCLASSNAME,
  685. WS_OVERLAPPEDWINDOW,
  686. CW_USEDEFAULT,
  687. CW_USEDEFAULT,
  688. CW_USEDEFAULT,
  689. CW_USEDEFAULT,
  690. NULL,
  691. NULL,
  692. appInstance,
  693. NULL);
  694. if (!hwndMain)
  695. {
  696. CHECKHR(HRESULT_FROM_WIN32(GetLastError()));
  697. exit(-1);
  698. }
  699. }
  700. //
  701. // Main program entrypoint
  702. //
  703. INT _cdecl
  704. main(
  705. INT argc,
  706. CHAR **argv
  707. )
  708. {
  709. programName = *argv++;
  710. argc--;
  711. appInstance = GetModuleHandle(NULL);
  712. CoInitialize(NULL);
  713. bitmap = NULL;
  714. decoder = NULL;
  715. numframes = -1;
  716. lastsuccframe = -1;
  717. currentframe = 0;
  718. loopingset = FALSE;
  719. viewagain = TRUE;
  720. //
  721. // Create an IImagingFactory object
  722. //
  723. HRESULT hr;
  724. hr = CoCreateInstance(
  725. CLSID_ImagingFactory,
  726. NULL,
  727. CLSCTX_INPROC_SERVER,
  728. IID_IImagingFactory,
  729. (VOID**) &imgfact);
  730. if (!CHECKHR(hr))
  731. exit(-1);
  732. //
  733. // Create the main application window
  734. //
  735. CreateMainWindow();
  736. //
  737. // Create a test image
  738. //
  739. if (argc != 0)
  740. OpenImageFile(*argv);
  741. if (!curimage)
  742. exit(-1);
  743. DoSizeWindowToFit(hwndMain);
  744. ShowWindow(hwndMain, SW_SHOW);
  745. //
  746. // Main message loop
  747. //
  748. MSG msg;
  749. while (GetMessage(&msg, NULL, 0, 0))
  750. {
  751. TranslateMessage(&msg);
  752. DispatchMessage(&msg);
  753. }
  754. imgfact->Release();
  755. CoUninitialize();
  756. return (INT)(msg.wParam);
  757. }