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.

1351 lines
29 KiB

  1. /*==========================================================================;
  2. *
  3. * Copyright (C) 1999 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: fdtipc.cpp
  6. * Content: Implements the IPC calls for the full duplex test
  7. * History:
  8. * Date By Reason
  9. * ============
  10. * 08/26/99 pnewson created
  11. * 04/19/2000 pnewson Error handling cleanup
  12. * 06/28/2000 rodtoll Prefix Bug #38022
  13. ***************************************************************************/
  14. #include "dxvtlibpch.h"
  15. #undef DPF_SUBCOMP
  16. #define DPF_SUBCOMP DN_SUBCOMP_VOICE
  17. // static helper functions for this file
  18. HRESULT DoReceive(
  19. SFDTestCommand* pfdtc,
  20. HANDLE hEvent,
  21. HANDLE hReplyEvent,
  22. LPVOID lpvShMemPtr);
  23. HRESULT DoReply(HRESULT hr, HANDLE hReplyEvent, LPVOID lpvShMemPtr);
  24. #undef DPF_MODNAME
  25. #define DPF_MODNAME "CSupervisorIPC::CSupervisorIPC"
  26. CSupervisorIPC::CSupervisorIPC()
  27. : m_fInitComplete(FALSE)
  28. , m_hFullDuplexEvent(NULL)
  29. , m_hFullDuplexMutex(NULL)
  30. , m_hFullDuplexReplyEvent(NULL)
  31. , m_hFullDuplexShMemHandle(NULL)
  32. , m_hPriorityEvent(NULL)
  33. , m_hPriorityMutex(NULL)
  34. , m_hPriorityReplyEvent(NULL)
  35. , m_hPriorityShMemHandle(NULL)
  36. , m_lpvFullDuplexShMemPtr(NULL)
  37. , m_lpvPriorityShMemPtr(NULL)
  38. {
  39. ZeroMemory(&m_piFullDuplex, sizeof(PROCESS_INFORMATION));
  40. ZeroMemory(&m_piPriority, sizeof(PROCESS_INFORMATION));
  41. return;
  42. }
  43. #undef DPF_MODNAME
  44. #define DPF_MODNAME "CSupervisorIPC::Init"
  45. HRESULT CSupervisorIPC::Init()
  46. {
  47. LONG lRet;
  48. HRESULT hr;
  49. DPF_ENTER();
  50. if (!DNInitializeCriticalSection(&m_csLock))
  51. {
  52. return DVERR_OUTOFMEMORY;
  53. }
  54. DNEnterCriticalSection(&m_csLock);
  55. if (m_fInitComplete != FALSE)
  56. {
  57. hr = DVERR_GENERIC;
  58. goto error_cleanup;
  59. }
  60. // create the event objects - make sure they don't already
  61. // exist!
  62. m_hPriorityEvent = CreateEvent(NULL, FALSE, FALSE, gc_szPriorityEventName);
  63. lRet = GetLastError();
  64. if (m_hPriorityEvent == NULL)
  65. {
  66. hr = DVERR_GENERIC;
  67. goto error_cleanup;
  68. }
  69. if (lRet == ERROR_ALREADY_EXISTS)
  70. {
  71. hr = DVERR_GENERIC;
  72. goto error_cleanup;
  73. }
  74. m_hFullDuplexEvent = CreateEvent(NULL, FALSE, FALSE, gc_szFullDuplexEventName);
  75. lRet = GetLastError();
  76. if (m_hFullDuplexEvent == NULL)
  77. {
  78. hr = DVERR_GENERIC;
  79. goto error_cleanup;
  80. }
  81. if (lRet == ERROR_ALREADY_EXISTS)
  82. {
  83. hr = DVERR_GENERIC;
  84. goto error_cleanup;
  85. }
  86. m_hPriorityReplyEvent = CreateEvent(NULL, FALSE, FALSE, gc_szPriorityReplyEventName);
  87. lRet = GetLastError();
  88. if (m_hPriorityReplyEvent == NULL)
  89. {
  90. hr = DVERR_GENERIC;
  91. goto error_cleanup;
  92. }
  93. if (lRet == ERROR_ALREADY_EXISTS)
  94. {
  95. hr = DVERR_GENERIC;
  96. goto error_cleanup;
  97. }
  98. m_hFullDuplexReplyEvent = CreateEvent(NULL, FALSE, FALSE, gc_szFullDuplexReplyEventName);
  99. lRet = GetLastError();
  100. if (m_hFullDuplexReplyEvent == NULL)
  101. {
  102. hr = DVERR_GENERIC;
  103. goto error_cleanup;
  104. }
  105. if (lRet == ERROR_ALREADY_EXISTS)
  106. {
  107. hr = DVERR_GENERIC;
  108. goto error_cleanup;
  109. }
  110. // create the shared memory blocks
  111. m_hPriorityShMemHandle = CreateFileMapping(
  112. INVALID_HANDLE_VALUE,
  113. NULL,
  114. PAGE_READWRITE,
  115. 0,
  116. gc_dwPriorityShMemSize,
  117. gc_szPriorityShMemName);
  118. lRet = GetLastError();
  119. if (m_hPriorityShMemHandle == NULL)
  120. {
  121. hr = DVERR_GENERIC;
  122. goto error_cleanup;
  123. }
  124. if (lRet == ERROR_ALREADY_EXISTS)
  125. {
  126. hr = DVERR_GENERIC;
  127. goto error_cleanup;
  128. }
  129. m_lpvPriorityShMemPtr = MapViewOfFile(
  130. m_hPriorityShMemHandle,
  131. FILE_MAP_WRITE,
  132. 0,
  133. 0,
  134. gc_dwPriorityShMemSize);
  135. if (m_lpvPriorityShMemPtr == NULL)
  136. {
  137. hr = DVERR_GENERIC;
  138. goto error_cleanup;
  139. }
  140. m_hFullDuplexShMemHandle = CreateFileMapping(
  141. INVALID_HANDLE_VALUE,
  142. NULL,
  143. PAGE_READWRITE,
  144. 0,
  145. gc_dwFullDuplexShMemSize,
  146. gc_szFullDuplexShMemName);
  147. lRet = GetLastError();
  148. if (m_hFullDuplexShMemHandle == NULL)
  149. {
  150. hr = DVERR_GENERIC;
  151. goto error_cleanup;
  152. }
  153. if (lRet == ERROR_ALREADY_EXISTS)
  154. {
  155. hr = DVERR_GENERIC;
  156. goto error_cleanup;
  157. }
  158. m_lpvFullDuplexShMemPtr = MapViewOfFile(
  159. m_hFullDuplexShMemHandle,
  160. FILE_MAP_WRITE,
  161. 0,
  162. 0,
  163. gc_dwFullDuplexShMemSize);
  164. if (m_lpvFullDuplexShMemPtr == NULL)
  165. {
  166. hr = DVERR_GENERIC;
  167. goto error_cleanup;
  168. }
  169. // create the send mutexes
  170. m_hPriorityMutex = CreateMutex(NULL, FALSE, gc_szPrioritySendMutex);
  171. lRet = GetLastError();
  172. if (m_hPriorityMutex == NULL)
  173. {
  174. hr = DVERR_GENERIC;
  175. goto error_cleanup;
  176. }
  177. if (lRet == ERROR_ALREADY_EXISTS)
  178. {
  179. hr = DVERR_GENERIC;
  180. goto error_cleanup;
  181. }
  182. m_hFullDuplexMutex = CreateMutex(NULL, FALSE, gc_szFullDuplexSendMutex);
  183. lRet = GetLastError();
  184. if (m_hFullDuplexMutex == NULL)
  185. {
  186. hr = DVERR_GENERIC;
  187. goto error_cleanup;
  188. }
  189. if (lRet == ERROR_ALREADY_EXISTS)
  190. {
  191. hr = DVERR_GENERIC;
  192. goto error_cleanup;
  193. }
  194. m_fInitComplete = TRUE;
  195. DNLeaveCriticalSection(&m_csLock);
  196. DPF_EXIT();
  197. return S_OK;
  198. error_cleanup:
  199. if (m_hFullDuplexMutex != NULL)
  200. {
  201. CloseHandle(m_hFullDuplexMutex);
  202. m_hFullDuplexMutex = NULL;
  203. }
  204. if (m_hPriorityMutex != NULL)
  205. {
  206. CloseHandle(m_hPriorityMutex);
  207. m_hPriorityMutex = NULL;
  208. }
  209. if (m_lpvFullDuplexShMemPtr != NULL)
  210. {
  211. UnmapViewOfFile(m_lpvFullDuplexShMemPtr);
  212. m_lpvFullDuplexShMemPtr = NULL;
  213. }
  214. if (m_hFullDuplexShMemHandle != NULL)
  215. {
  216. CloseHandle(m_hFullDuplexShMemHandle);
  217. m_hFullDuplexShMemHandle = NULL;
  218. }
  219. if (m_lpvPriorityShMemPtr != NULL)
  220. {
  221. UnmapViewOfFile(m_lpvPriorityShMemPtr);
  222. m_lpvPriorityShMemPtr = NULL;
  223. }
  224. if (m_hPriorityShMemHandle != NULL)
  225. {
  226. CloseHandle(m_hPriorityShMemHandle);
  227. m_hPriorityShMemHandle = NULL;
  228. }
  229. if (m_hFullDuplexReplyEvent != NULL)
  230. {
  231. CloseHandle(m_hFullDuplexReplyEvent);
  232. m_hFullDuplexReplyEvent = NULL;
  233. }
  234. if (m_hPriorityReplyEvent != NULL)
  235. {
  236. CloseHandle(m_hPriorityReplyEvent);
  237. m_hPriorityReplyEvent = NULL;
  238. }
  239. if (m_hFullDuplexEvent != NULL)
  240. {
  241. CloseHandle(m_hFullDuplexEvent);
  242. m_hFullDuplexEvent = NULL;
  243. }
  244. if (m_hPriorityEvent != NULL)
  245. {
  246. CloseHandle(m_hPriorityEvent);
  247. m_hPriorityEvent = NULL;
  248. }
  249. DNLeaveCriticalSection(&m_csLock);
  250. DPF_EXIT();
  251. return hr;
  252. }
  253. #undef DPF_MODNAME
  254. #define DPF_MODNAME "CSupervisorIPC::Deinit"
  255. HRESULT CSupervisorIPC::Deinit()
  256. {
  257. LONG lRet;
  258. HRESULT hr = DV_OK;
  259. DPF_ENTER();
  260. DNEnterCriticalSection(&m_csLock);
  261. if (m_fInitComplete != TRUE)
  262. {
  263. hr = DVERR_NOTINITIALIZED;
  264. }
  265. m_fInitComplete = FALSE;
  266. if (m_hFullDuplexMutex != NULL)
  267. {
  268. CloseHandle(m_hFullDuplexMutex);
  269. m_hFullDuplexMutex = NULL;
  270. }
  271. if (m_hPriorityMutex != NULL)
  272. {
  273. CloseHandle(m_hPriorityMutex);
  274. m_hPriorityMutex = NULL;
  275. }
  276. if (m_lpvFullDuplexShMemPtr != NULL)
  277. {
  278. UnmapViewOfFile(m_lpvFullDuplexShMemPtr);
  279. m_lpvFullDuplexShMemPtr = NULL;
  280. }
  281. if (m_hFullDuplexShMemHandle != NULL)
  282. {
  283. CloseHandle(m_hFullDuplexShMemHandle);
  284. m_hFullDuplexShMemHandle = NULL;
  285. }
  286. if (m_lpvPriorityShMemPtr != NULL)
  287. {
  288. UnmapViewOfFile(m_lpvPriorityShMemPtr);
  289. m_lpvPriorityShMemPtr = NULL;
  290. }
  291. if (m_hPriorityShMemHandle != NULL)
  292. {
  293. CloseHandle(m_hPriorityShMemHandle);
  294. m_hPriorityShMemHandle = NULL;
  295. }
  296. if (m_hFullDuplexReplyEvent != NULL)
  297. {
  298. CloseHandle(m_hFullDuplexReplyEvent);
  299. m_hFullDuplexReplyEvent = NULL;
  300. }
  301. if (m_hPriorityReplyEvent != NULL)
  302. {
  303. CloseHandle(m_hPriorityReplyEvent);
  304. m_hPriorityReplyEvent = NULL;
  305. }
  306. if (m_hFullDuplexEvent != NULL)
  307. {
  308. CloseHandle(m_hFullDuplexEvent);
  309. m_hFullDuplexEvent = NULL;
  310. }
  311. if (m_hPriorityEvent != NULL)
  312. {
  313. CloseHandle(m_hPriorityEvent);
  314. m_hPriorityEvent = NULL;
  315. }
  316. DNLeaveCriticalSection(&m_csLock);
  317. DNDeleteCriticalSection(&m_csLock);
  318. DPF_EXIT();
  319. return hr;
  320. }
  321. #undef DPF_MODNAME
  322. #define DPF_MODNAME "CSupervisorIPC::SendToPriority"
  323. HRESULT CSupervisorIPC::SendToPriority(const SFDTestCommand *pfdtc)
  324. {
  325. HRESULT hr;
  326. DPF_ENTER();
  327. DNEnterCriticalSection(&m_csLock);
  328. hr = DoSend(
  329. pfdtc,
  330. m_piPriority.hProcess,
  331. m_hPriorityEvent,
  332. m_hPriorityReplyEvent,
  333. m_lpvPriorityShMemPtr,
  334. m_hPriorityMutex);
  335. DNLeaveCriticalSection(&m_csLock);
  336. DPF_EXIT();
  337. return hr;
  338. }
  339. #undef DPF_MODNAME
  340. #define DPF_MODNAME "CSupervisorIPC::SendToFullDuplex"
  341. HRESULT CSupervisorIPC::SendToFullDuplex(const SFDTestCommand *pfdtc)
  342. {
  343. HRESULT hr;
  344. DPF_ENTER();
  345. DNEnterCriticalSection(&m_csLock);
  346. hr = DoSend(
  347. pfdtc,
  348. m_piFullDuplex.hProcess,
  349. m_hFullDuplexEvent,
  350. m_hFullDuplexReplyEvent,
  351. m_lpvFullDuplexShMemPtr,
  352. m_hFullDuplexMutex);
  353. DNLeaveCriticalSection(&m_csLock);
  354. DPF_EXIT();
  355. return hr;
  356. }
  357. #undef DPF_MODNAME
  358. #define DPF_MODNAME "CSupervisorIPC::DoSend"
  359. HRESULT CSupervisorIPC::DoSend(
  360. const SFDTestCommand* pfdtc,
  361. HANDLE hProcess,
  362. HANDLE hEvent,
  363. HANDLE hReplyEvent,
  364. LPVOID lpvShMemPtr,
  365. HANDLE hMutex)
  366. {
  367. DWORD dwRet;
  368. LONG lRet;
  369. HRESULT hr;
  370. HANDLE hWaitArray[2];
  371. BOOL fHaveMutex = FALSE;
  372. DPF_ENTER();
  373. // grab the mutex
  374. dwRet = WaitForSingleObject(hMutex, gc_dwSendMutexTimeout);
  375. if (dwRet != WAIT_OBJECT_0)
  376. {
  377. if (dwRet == WAIT_TIMEOUT)
  378. {
  379. DPFX(DPFPREP, DVF_ERRORLEVEL, "Timed out waiting for send mutex");
  380. hr = DVERR_TIMEOUT;
  381. goto error_cleanup;
  382. }
  383. lRet = GetLastError();
  384. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error waiting for send mutex, code: %i", lRet);
  385. hr = DVERR_WIN32;
  386. goto error_cleanup;
  387. }
  388. fHaveMutex = TRUE;
  389. // copy the command into shared memory
  390. CopyMemory(lpvShMemPtr, pfdtc, pfdtc->dwSize);
  391. // signal the event
  392. if (!SetEvent(hEvent))
  393. {
  394. lRet = GetLastError();
  395. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to set event, code: %i", lRet);
  396. hr = DVERR_WIN32;
  397. goto error_cleanup;
  398. }
  399. // Wait for the reply event - note that we only expect the
  400. // supervisor process to call this function, and therefore
  401. // we don't check for directsound events occuring during this
  402. // time.
  403. // Also, wait on the process handle, if the process we are sending
  404. // to exits, we'll want to continue right away, not wait for a timeout.
  405. hWaitArray[0] = hReplyEvent;
  406. hWaitArray[1] = hProcess;
  407. dwRet = WaitForMultipleObjects(2, hWaitArray, FALSE, gc_dwCommandReplyTimeout);
  408. switch(dwRet)
  409. {
  410. case WAIT_OBJECT_0:
  411. // The other process replied, move along.
  412. break;
  413. case WAIT_OBJECT_0+1:
  414. // The other process exited!
  415. DPFX(DPFPREP, DVF_ERRORLEVEL, "Process exited while waiting for reply");
  416. hr = DVERR_TIMEOUT;
  417. goto error_cleanup;
  418. case WAIT_TIMEOUT:
  419. // The other process did not reply in a reasonable amount of time.
  420. DPFX(DPFPREP, DVF_ERRORLEVEL, "Timed out waiting for reply to command");
  421. hr = DVERR_TIMEOUT;
  422. goto error_cleanup;
  423. default:
  424. // No idea what happened here...
  425. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error waiting for reply event, code: %i", dwRet);
  426. hr = DVERR_GENERIC;
  427. goto error_cleanup;
  428. }
  429. // get the reply code (an HRESULT) from shared memory
  430. hr = *(HRESULT*)lpvShMemPtr;
  431. // release the mutex
  432. if (!ReleaseMutex(hMutex))
  433. {
  434. lRet = GetLastError();
  435. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error releasing mutex, code: %i", lRet);
  436. hr = DVERR_GENERIC;
  437. goto error_cleanup;
  438. }
  439. DPF_EXIT();
  440. return hr;
  441. error_cleanup:
  442. if (hMutex != NULL)
  443. {
  444. ReleaseMutex(hMutex);
  445. }
  446. DPF_EXIT();
  447. return hr;
  448. }
  449. #undef DPF_MODNAME
  450. #define DPF_MODNAME "CSupervisorIPC::StartPriorityProcess"
  451. HRESULT CSupervisorIPC::StartPriorityProcess()
  452. {
  453. STARTUPINFO si;
  454. DPF_ENTER();
  455. TCHAR szPriorityCommandLine[_MAX_PATH+1];
  456. DNEnterCriticalSection(&m_csLock);
  457. ZeroMemory(&si, sizeof(si));
  458. si.cb = sizeof(si);
  459. _tcsncpy( szPriorityCommandLine, gc_szPriorityCommand, _MAX_PATH );
  460. szPriorityCommandLine[_MAX_PATH] = 0;
  461. if (!CreateProcess(
  462. NULL,
  463. (LPTSTR) szPriorityCommandLine,
  464. NULL,
  465. NULL,
  466. FALSE,
  467. 0,
  468. NULL,
  469. NULL,
  470. &si,
  471. &m_piPriority))
  472. {
  473. m_piPriority.hProcess = NULL;
  474. m_piPriority.hThread = NULL;
  475. DNLeaveCriticalSection(&m_csLock);
  476. DPF_EXIT();
  477. return DVERR_WIN32;
  478. }
  479. // don't need the thread handle
  480. if (!CloseHandle(m_piPriority.hThread))
  481. {
  482. m_piPriority.hThread = NULL;
  483. DNLeaveCriticalSection(&m_csLock);
  484. DPF_EXIT();
  485. return DVERR_WIN32;
  486. }
  487. DNLeaveCriticalSection(&m_csLock);
  488. DPF_EXIT();
  489. return S_OK;
  490. }
  491. #undef DPF_MODNAME
  492. #define DPF_MODNAME "CSupervisorIPC::WaitOnChildren"
  493. HRESULT CSupervisorIPC::WaitOnChildren()
  494. {
  495. HANDLE rghChildren[2];
  496. DWORD dwRet;
  497. LONG lRet;
  498. HRESULT hr = DV_OK;
  499. BOOL fRet;
  500. DPF_ENTER();
  501. DNEnterCriticalSection(&m_csLock);
  502. rghChildren[0] = m_piPriority.hProcess;
  503. rghChildren[1] = m_piFullDuplex.hProcess;
  504. DNLeaveCriticalSection(&m_csLock);
  505. dwRet = WaitForMultipleObjects(2, rghChildren, TRUE, gc_dwChildWaitTimeout);
  506. if (dwRet == WAIT_TIMEOUT)
  507. {
  508. DPFX(DPFPREP, DVF_ERRORLEVEL, "WaitForMultipleObjects timed out waiting on child process handles");
  509. DPF_EXIT();
  510. return DVERR_CHILDPROCESSFAILED;
  511. }
  512. else if (dwRet == WAIT_OBJECT_0 || dwRet == WAIT_OBJECT_0 + 1)
  513. {
  514. // This is the expected behavior. The processes shut down
  515. // gracefully. Close and NULL out our handles to them.
  516. // Note that just because the process shut down gracefully,
  517. // that does not mean that it did not have an error. The
  518. // process returns an HRESULT for it's exit code. Check it.
  519. // Note that this code assumes HRESULTS are DWORDS. Hopefully
  520. // this won't break in Win64!
  521. DNEnterCriticalSection(&m_csLock);
  522. fRet = GetExitCodeProcess(m_piPriority.hProcess, (LPDWORD)&hr);
  523. if (!fRet || FAILED(hr))
  524. {
  525. lRet = GetLastError();
  526. CloseHandle(m_piPriority.hProcess);
  527. CloseHandle(m_piFullDuplex.hProcess);
  528. m_piPriority.hProcess = NULL;
  529. m_piFullDuplex.hProcess = NULL;
  530. DNLeaveCriticalSection(&m_csLock);
  531. if (!fRet && SUCCEEDED(hr))
  532. {
  533. DPFX(DPFPREP, DVF_ERRORLEVEL, "GetExitCodeProcess failed, lRet: %i", lRet);
  534. DPF_EXIT();
  535. return DVERR_GENERIC;
  536. }
  537. DPFX(DPFPREP, DVF_ERRORLEVEL, "Priority Process exited with error code, hr: %i", hr);
  538. DPF_EXIT();
  539. return hr;
  540. }
  541. if (!CloseHandle(m_piPriority.hProcess))
  542. {
  543. lRet = GetLastError();
  544. CloseHandle(m_piFullDuplex.hProcess);
  545. m_piPriority.hProcess = NULL;
  546. m_piFullDuplex.hProcess = NULL;
  547. DNLeaveCriticalSection(&m_csLock);
  548. DPFX(DPFPREP, DVF_ERRORLEVEL, "CloseHandle failed, lRet: %i", lRet);
  549. DPF_EXIT();
  550. return DVERR_GENERIC;
  551. }
  552. fRet = GetExitCodeProcess(m_piFullDuplex.hProcess, (LPDWORD)&hr);
  553. if (!fRet || FAILED(hr))
  554. {
  555. lRet = GetLastError();
  556. CloseHandle(m_piFullDuplex.hProcess);
  557. m_piPriority.hProcess = NULL;
  558. m_piFullDuplex.hProcess = NULL;
  559. DNLeaveCriticalSection(&m_csLock);
  560. if (!fRet && SUCCEEDED(hr))
  561. {
  562. DPFX(DPFPREP, DVF_ERRORLEVEL, "GetExitCodeProcess failed, lRet: %i", lRet);
  563. DPF_EXIT();
  564. return DVERR_GENERIC;
  565. }
  566. DPFX(DPFPREP, DVF_ERRORLEVEL, "FullDuplex Process exited with error code, hr: %i", hr);
  567. DPF_EXIT();
  568. return hr;
  569. }
  570. if (!CloseHandle(m_piFullDuplex.hProcess))
  571. {
  572. lRet = GetLastError();
  573. m_piPriority.hProcess = NULL;
  574. m_piFullDuplex.hProcess = NULL;
  575. DNLeaveCriticalSection(&m_csLock);
  576. DPFX(DPFPREP, DVF_ERRORLEVEL, "CloseHandle failed, lRet: %i", lRet);
  577. DPF_EXIT();
  578. return DVERR_GENERIC;
  579. }
  580. m_piPriority.hProcess = NULL;
  581. m_piFullDuplex.hProcess = NULL;
  582. DNLeaveCriticalSection(&m_csLock);
  583. DPF_EXIT();
  584. return DV_OK;
  585. }
  586. else
  587. {
  588. // Not sure what happened...
  589. DPF_EXIT();
  590. return DVERR_GENERIC;
  591. }
  592. }
  593. #undef DPF_MODNAME
  594. #define DPF_MODNAME "CSupervisorIPC::TerminateChildProcesses"
  595. HRESULT CSupervisorIPC::TerminateChildProcesses()
  596. {
  597. LONG lRet;
  598. HRESULT hr = DV_OK;
  599. DPF_ENTER();
  600. // The child processes did not exit gracefully. So now
  601. // we get to kill them off rudely. Note that this
  602. // function may be called at any time, including when
  603. // there are no child processes running.
  604. DNEnterCriticalSection(&m_csLock);
  605. if (m_piPriority.hProcess != NULL)
  606. {
  607. if (!TerminateProcess(m_piPriority.hProcess, 0))
  608. {
  609. lRet = GetLastError();
  610. DPFX(DPFPREP, DVF_ERRORLEVEL, "TerminateProcess failed on priority process, code: %i", lRet);
  611. if (!CloseHandle(m_piPriority.hProcess))
  612. {
  613. lRet = GetLastError();
  614. DPFX(DPFPREP, DVF_ERRORLEVEL, "CloseHandle failed on priority process handle, code: %i", lRet);
  615. }
  616. m_piPriority.hProcess = NULL;
  617. hr = DVERR_GENERIC;
  618. }
  619. if (!CloseHandle(m_piPriority.hProcess))
  620. {
  621. lRet = GetLastError();
  622. DPFX(DPFPREP, DVF_ERRORLEVEL, "CloseHandle failed on priority process handle, code: %i", lRet);
  623. hr = DVERR_GENERIC;
  624. }
  625. m_piPriority.hProcess = NULL;
  626. }
  627. if (m_piFullDuplex.hProcess != NULL)
  628. {
  629. if (!TerminateProcess(m_piFullDuplex.hProcess, 0))
  630. {
  631. lRet = GetLastError();
  632. DPFX(DPFPREP, DVF_ERRORLEVEL, "TerminateProcess failed on full duplex process, code: %i", lRet);
  633. hr = DVERR_GENERIC;
  634. }
  635. if (!CloseHandle(m_piFullDuplex.hProcess))
  636. {
  637. lRet = GetLastError();
  638. DPFX(DPFPREP, DVF_ERRORLEVEL, "CloseHandle failed on full duplex process handle, code: %i", lRet);
  639. hr = DVERR_GENERIC;
  640. }
  641. m_piFullDuplex.hProcess = NULL;
  642. }
  643. DNLeaveCriticalSection(&m_csLock);
  644. DPF_EXIT();
  645. return hr;
  646. }
  647. #undef DPF_MODNAME
  648. #define DPF_MODNAME "CSupervisorIPC::StartFullDuplexProcess"
  649. HRESULT CSupervisorIPC::StartFullDuplexProcess()
  650. {
  651. STARTUPINFO si;
  652. TCHAR szFullDuplexCommandLine[_MAX_PATH+1];
  653. DPF_ENTER();
  654. DNEnterCriticalSection(&m_csLock);
  655. ZeroMemory(&si, sizeof(si));
  656. si.cb = sizeof(si);
  657. _tcsncpy( szFullDuplexCommandLine, gc_szFullDuplexCommand, _MAX_PATH );
  658. szFullDuplexCommandLine[_MAX_PATH] = 0;
  659. if (!CreateProcess(
  660. NULL,
  661. (LPTSTR) szFullDuplexCommandLine,
  662. NULL,
  663. NULL,
  664. FALSE,
  665. 0,
  666. NULL,
  667. NULL,
  668. &si,
  669. &m_piFullDuplex))
  670. {
  671. m_piFullDuplex.hProcess = NULL;
  672. m_piFullDuplex.hThread = NULL;
  673. DNLeaveCriticalSection(&m_csLock);
  674. DPF_EXIT();
  675. return DVERR_WIN32;
  676. }
  677. // don't need the thread handle
  678. if (!CloseHandle(m_piFullDuplex.hThread))
  679. {
  680. m_piFullDuplex.hThread = NULL;
  681. DNLeaveCriticalSection(&m_csLock);
  682. DPF_EXIT();
  683. return DVERR_WIN32;
  684. }
  685. DNLeaveCriticalSection(&m_csLock);
  686. DPF_EXIT();
  687. return S_OK;
  688. }
  689. #undef DPF_MODNAME
  690. #define DPF_MODNAME "CSupervisorIPC::WaitForStartupSignals"
  691. HRESULT CSupervisorIPC::WaitForStartupSignals()
  692. {
  693. HANDLE rghEvents[2];
  694. DWORD dwRet;
  695. HRESULT hr;
  696. LONG lRet;
  697. // wait to be signaled by both child processes, indicating that
  698. // they are ready to go.
  699. DNEnterCriticalSection(&m_csLock);
  700. rghEvents[0] = m_hPriorityReplyEvent;
  701. rghEvents[1] = m_hFullDuplexReplyEvent;
  702. DNLeaveCriticalSection(&m_csLock);
  703. dwRet = WaitForMultipleObjects(2, rghEvents, TRUE, gc_dwChildStartupTimeout);
  704. if (dwRet != WAIT_OBJECT_0 && dwRet != WAIT_OBJECT_0 + 1)
  705. {
  706. if (dwRet == WAIT_TIMEOUT)
  707. {
  708. DPFX(DPFPREP, DVF_ERRORLEVEL, "Timeout waiting for child processes to startup");
  709. return DVERR_TIMEOUT;
  710. }
  711. else
  712. {
  713. lRet = GetLastError();
  714. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error waiting for signals from child processes, code: %i", lRet);
  715. return DVERR_WIN32;
  716. }
  717. }
  718. return DV_OK;
  719. }
  720. #undef DPF_MODNAME
  721. #define DPF_MODNAME "CPriorityIPC::CPriorityIPC"
  722. CPriorityIPC::CPriorityIPC()
  723. : m_fInitComplete(FALSE)
  724. , m_hPriorityEvent(NULL)
  725. , m_hPriorityMutex(NULL)
  726. , m_hPriorityReplyEvent(NULL)
  727. , m_hPriorityShMemHandle(NULL)
  728. , m_lpvPriorityShMemPtr(NULL)
  729. {
  730. return;
  731. }
  732. #undef DPF_MODNAME
  733. #define DPF_MODNAME "CPriorityIPC::Init"
  734. HRESULT CPriorityIPC::Init()
  735. {
  736. LONG lRet;
  737. HRESULT hr;
  738. DPF_ENTER();
  739. if (!DNInitializeCriticalSection(&m_csLock))
  740. {
  741. return DVERR_OUTOFMEMORY;
  742. }
  743. DNEnterCriticalSection(&m_csLock);
  744. if (m_fInitComplete == TRUE)
  745. {
  746. DPFX(DPFPREP, DVF_ERRORLEVEL, "CPriorityIPC::Init - already initialized");
  747. hr = DVERR_INITIALIZED;
  748. goto error_cleanup;
  749. }
  750. m_hPriorityEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, gc_szPriorityEventName);
  751. if (m_hPriorityEvent == NULL)
  752. {
  753. lRet = GetLastError();
  754. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to open Priority event, code: %i", lRet);
  755. hr = DVERR_GENERIC;
  756. goto error_cleanup;
  757. }
  758. m_hPriorityReplyEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, gc_szPriorityReplyEventName);
  759. if (m_hPriorityReplyEvent == NULL)
  760. {
  761. lRet = GetLastError();
  762. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to open Priority Reply event, code: %i", lRet);
  763. hr = DVERR_GENERIC;
  764. goto error_cleanup;
  765. }
  766. m_hPriorityShMemHandle = OpenFileMapping(FILE_MAP_WRITE, FALSE, gc_szPriorityShMemName);
  767. if (m_hPriorityShMemHandle == NULL)
  768. {
  769. lRet = GetLastError();
  770. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to open Priority FileMapping object, code: %i", lRet);
  771. hr = DVERR_GENERIC;
  772. goto error_cleanup;
  773. }
  774. m_lpvPriorityShMemPtr = MapViewOfFile(
  775. m_hPriorityShMemHandle,
  776. FILE_MAP_WRITE,
  777. 0,
  778. 0,
  779. gc_dwPriorityShMemSize);
  780. if (m_lpvPriorityShMemPtr == NULL)
  781. {
  782. lRet = GetLastError();
  783. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to Map view of Priority FileMapping object, code: %i", lRet);
  784. hr = DVERR_GENERIC;
  785. goto error_cleanup;
  786. }
  787. m_fInitComplete = TRUE;
  788. DNLeaveCriticalSection(&m_csLock);
  789. DPF_EXIT();
  790. return S_OK;
  791. error_cleanup:
  792. if (m_lpvPriorityShMemPtr != NULL)
  793. {
  794. UnmapViewOfFile(m_lpvPriorityShMemPtr);
  795. m_lpvPriorityShMemPtr = NULL;
  796. }
  797. if (m_hPriorityShMemHandle != NULL)
  798. {
  799. CloseHandle(m_hPriorityShMemHandle);
  800. m_hPriorityShMemHandle = NULL;
  801. }
  802. if (m_hPriorityReplyEvent != NULL)
  803. {
  804. CloseHandle(m_hPriorityReplyEvent);
  805. m_hPriorityReplyEvent = NULL;
  806. }
  807. if (m_hPriorityEvent != NULL)
  808. {
  809. CloseHandle(m_hPriorityEvent);
  810. m_hPriorityEvent = NULL;
  811. }
  812. DNLeaveCriticalSection(&m_csLock);
  813. DPF_EXIT();
  814. return hr;
  815. }
  816. #undef DPF_MODNAME
  817. #define DPF_MODNAME "CPriorityIPC::Deinit"
  818. HRESULT CPriorityIPC::Deinit()
  819. {
  820. LONG lRet;
  821. HRESULT hr = DV_OK;
  822. DPF_ENTER();
  823. DNEnterCriticalSection(&m_csLock);
  824. if (m_fInitComplete != TRUE)
  825. {
  826. DPFX(DPFPREP, DVF_ERRORLEVEL, "CPriorityIPC::Deinit called on uninitialized object");
  827. hr = DVERR_NOTINITIALIZED;
  828. }
  829. m_fInitComplete = FALSE;
  830. if (m_lpvPriorityShMemPtr != NULL)
  831. {
  832. UnmapViewOfFile(m_lpvPriorityShMemPtr);
  833. m_lpvPriorityShMemPtr = NULL;
  834. }
  835. if (m_hPriorityShMemHandle != NULL)
  836. {
  837. CloseHandle(m_hPriorityShMemHandle);
  838. m_hPriorityShMemHandle = NULL;
  839. }
  840. if (m_hPriorityReplyEvent != NULL)
  841. {
  842. CloseHandle(m_hPriorityReplyEvent);
  843. m_hPriorityReplyEvent = NULL;
  844. }
  845. if (m_hPriorityEvent != NULL)
  846. {
  847. CloseHandle(m_hPriorityEvent);
  848. m_hPriorityEvent = NULL;
  849. }
  850. DNLeaveCriticalSection(&m_csLock);
  851. DNDeleteCriticalSection(&m_csLock);
  852. DPF_EXIT();
  853. return hr;
  854. }
  855. #undef DPF_MODNAME
  856. #define DPF_MODNAME "CPriorityIPC::SignalParentReady"
  857. HRESULT CPriorityIPC::SignalParentReady()
  858. {
  859. BOOL fRet;
  860. LONG lRet;
  861. DPF_ENTER();
  862. DNEnterCriticalSection(&m_csLock);
  863. fRet = SetEvent(m_hPriorityReplyEvent);
  864. if (!fRet)
  865. {
  866. lRet = GetLastError();
  867. DPFX(DPFPREP, 0, "Error Setting Priority Reply Event, code: %i", lRet);
  868. DPF_EXIT();
  869. return DVERR_WIN32;
  870. }
  871. DNLeaveCriticalSection(&m_csLock);
  872. DPF_EXIT();
  873. return DV_OK;
  874. }
  875. #undef DPF_MODNAME
  876. #define DPF_MODNAME "CPriorityIPC::Receive"
  877. HRESULT CPriorityIPC::Receive(SFDTestCommand* pfdtc)
  878. {
  879. HRESULT hr;
  880. DPF_ENTER();
  881. DNEnterCriticalSection(&m_csLock);
  882. hr = DoReceive(pfdtc, m_hPriorityEvent, m_hPriorityReplyEvent, m_lpvPriorityShMemPtr);
  883. DNLeaveCriticalSection(&m_csLock);
  884. DPF_EXIT();
  885. return hr;
  886. }
  887. #undef DPF_MODNAME
  888. #define DPF_MODNAME "CPriorityIPC::Reply"
  889. HRESULT CPriorityIPC::Reply(HRESULT hr)
  890. {
  891. DPF_ENTER();
  892. DNEnterCriticalSection(&m_csLock);
  893. hr = DoReply(hr, m_hPriorityReplyEvent, m_lpvPriorityShMemPtr);
  894. DNLeaveCriticalSection(&m_csLock);
  895. DPF_EXIT();
  896. return hr;
  897. }
  898. #undef DPF_MODNAME
  899. #define DPF_MODNAME "CFullDuplexIPC::CFullDuplexIPC"
  900. CFullDuplexIPC::CFullDuplexIPC()
  901. : m_fInitComplete(FALSE)
  902. , m_hFullDuplexEvent(NULL)
  903. , m_hFullDuplexMutex(NULL)
  904. , m_hFullDuplexReplyEvent(NULL)
  905. , m_hFullDuplexShMemHandle(NULL)
  906. , m_lpvFullDuplexShMemPtr(NULL)
  907. {
  908. return;
  909. }
  910. #undef DPF_MODNAME
  911. #define DPF_MODNAME "CFullDuplexIPC::Init"
  912. HRESULT CFullDuplexIPC::Init()
  913. {
  914. LONG lRet;
  915. HRESULT hr;
  916. DPF_ENTER();
  917. if (!DNInitializeCriticalSection(&m_csLock))
  918. {
  919. return DVERR_OUTOFMEMORY;
  920. }
  921. DNEnterCriticalSection(&m_csLock);
  922. if (m_fInitComplete != FALSE)
  923. {
  924. DPFX(DPFPREP, DVF_ERRORLEVEL, "CFullDuplexIPC::Init - already initialized");
  925. hr = DVERR_INITIALIZED;
  926. goto error_cleanup;
  927. }
  928. m_hFullDuplexEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, gc_szFullDuplexEventName);
  929. if (m_hFullDuplexEvent == NULL)
  930. {
  931. lRet = GetLastError();
  932. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to open FullDuplex event, code: %i", lRet);
  933. hr = DVERR_GENERIC;
  934. goto error_cleanup;
  935. }
  936. m_hFullDuplexReplyEvent = OpenEvent(EVENT_ALL_ACCESS, FALSE, gc_szFullDuplexReplyEventName);
  937. if (m_hFullDuplexReplyEvent == NULL)
  938. {
  939. lRet = GetLastError();
  940. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to open FullDuplex Reply event, code: %i", lRet);
  941. hr = DVERR_GENERIC;
  942. goto error_cleanup;
  943. }
  944. m_hFullDuplexShMemHandle = OpenFileMapping(FILE_MAP_WRITE, FALSE, gc_szFullDuplexShMemName);
  945. if (m_hFullDuplexShMemHandle == NULL)
  946. {
  947. lRet = GetLastError();
  948. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to open FullDuplex FileMapping object, code: %i", lRet);
  949. hr = DVERR_GENERIC;
  950. goto error_cleanup;
  951. }
  952. m_lpvFullDuplexShMemPtr = MapViewOfFile(
  953. m_hFullDuplexShMemHandle,
  954. FILE_MAP_WRITE,
  955. 0,
  956. 0,
  957. gc_dwFullDuplexShMemSize);
  958. if (m_lpvFullDuplexShMemPtr == NULL)
  959. {
  960. lRet = GetLastError();
  961. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to Map view of FullDuplex FileMapping object, code: %i", lRet);
  962. hr = DVERR_GENERIC;
  963. goto error_cleanup;
  964. }
  965. m_fInitComplete = TRUE;
  966. DNLeaveCriticalSection(&m_csLock);
  967. DPF_EXIT();
  968. return S_OK;
  969. error_cleanup:
  970. if (m_lpvFullDuplexShMemPtr != NULL)
  971. {
  972. UnmapViewOfFile(m_lpvFullDuplexShMemPtr);
  973. m_lpvFullDuplexShMemPtr = NULL;
  974. }
  975. if (m_hFullDuplexShMemHandle != NULL)
  976. {
  977. CloseHandle(m_hFullDuplexShMemHandle);
  978. m_hFullDuplexShMemHandle = NULL;
  979. }
  980. if (m_hFullDuplexReplyEvent != NULL)
  981. {
  982. CloseHandle(m_hFullDuplexReplyEvent);
  983. m_hFullDuplexReplyEvent = NULL;
  984. }
  985. if (m_hFullDuplexEvent != NULL)
  986. {
  987. CloseHandle(m_hFullDuplexEvent);
  988. m_hFullDuplexEvent = NULL;
  989. }
  990. DNLeaveCriticalSection(&m_csLock);
  991. DPF_EXIT();
  992. return hr;
  993. }
  994. #undef DPF_MODNAME
  995. #define DPF_MODNAME "CFullDuplexIPC::Deinit"
  996. HRESULT CFullDuplexIPC::Deinit()
  997. {
  998. LONG lRet;
  999. HRESULT hr = DV_OK;
  1000. DPF_ENTER();
  1001. DNEnterCriticalSection(&m_csLock);
  1002. if (m_fInitComplete != TRUE)
  1003. {
  1004. DPFX(DPFPREP, DVF_ERRORLEVEL, "CFullDuplexIPC::Deinit called on uninitialized object");
  1005. hr = DVERR_NOTINITIALIZED;
  1006. }
  1007. m_fInitComplete = FALSE;
  1008. if (m_lpvFullDuplexShMemPtr != NULL)
  1009. {
  1010. UnmapViewOfFile(m_lpvFullDuplexShMemPtr);
  1011. m_lpvFullDuplexShMemPtr = NULL;
  1012. }
  1013. if (m_hFullDuplexShMemHandle != NULL)
  1014. {
  1015. CloseHandle(m_hFullDuplexShMemHandle);
  1016. m_hFullDuplexShMemHandle = NULL;
  1017. }
  1018. if (m_hFullDuplexReplyEvent != NULL)
  1019. {
  1020. CloseHandle(m_hFullDuplexReplyEvent);
  1021. m_hFullDuplexReplyEvent = NULL;
  1022. }
  1023. if (m_hFullDuplexEvent != NULL)
  1024. {
  1025. CloseHandle(m_hFullDuplexEvent);
  1026. m_hFullDuplexEvent = NULL;
  1027. }
  1028. DNLeaveCriticalSection(&m_csLock);
  1029. DNDeleteCriticalSection(&m_csLock);
  1030. DPF_EXIT();
  1031. return hr;
  1032. }
  1033. #undef DPF_MODNAME
  1034. #define DPF_MODNAME "CFullDuplexIPC::SignalParentReady"
  1035. HRESULT CFullDuplexIPC::SignalParentReady()
  1036. {
  1037. BOOL fRet;
  1038. LONG lRet;
  1039. DPF_ENTER();
  1040. DNEnterCriticalSection(&m_csLock);
  1041. fRet = SetEvent(m_hFullDuplexReplyEvent);
  1042. if (!fRet)
  1043. {
  1044. lRet = GetLastError();
  1045. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error Setting FullDuplex Event, code: %i", lRet);
  1046. DPF_EXIT();
  1047. return DVERR_WIN32;
  1048. }
  1049. DNLeaveCriticalSection(&m_csLock);
  1050. DPF_EXIT();
  1051. return DV_OK;
  1052. }
  1053. #undef DPF_MODNAME
  1054. #define DPF_MODNAME "CFullDuplexIPC::Receive"
  1055. HRESULT CFullDuplexIPC::Receive(SFDTestCommand* pfdtc)
  1056. {
  1057. HRESULT hr;
  1058. DPF_ENTER();
  1059. DNEnterCriticalSection(&m_csLock);
  1060. hr = DoReceive(pfdtc, m_hFullDuplexEvent, m_hFullDuplexReplyEvent, m_lpvFullDuplexShMemPtr);
  1061. DNLeaveCriticalSection(&m_csLock);
  1062. DPF_EXIT();
  1063. return hr;
  1064. }
  1065. #undef DPF_MODNAME
  1066. #define DPF_MODNAME "CFullDuplexIPC::Reply"
  1067. HRESULT CFullDuplexIPC::Reply(HRESULT hrReply)
  1068. {
  1069. HRESULT hr;
  1070. DPF_ENTER();
  1071. DNEnterCriticalSection(&m_csLock);
  1072. hr = DoReply(hrReply, m_hFullDuplexReplyEvent, m_lpvFullDuplexShMemPtr);
  1073. DNLeaveCriticalSection(&m_csLock);
  1074. DPF_EXIT();
  1075. return hr;
  1076. }
  1077. #undef DPF_MODNAME
  1078. #define DPF_MODNAME "DoReceive"
  1079. HRESULT DoReceive(
  1080. SFDTestCommand* pfdtc,
  1081. HANDLE hEvent,
  1082. HANDLE hReplyEvent,
  1083. LPVOID lpvShMemPtr)
  1084. {
  1085. DWORD dwRet;
  1086. LONG lRet;
  1087. DPF_ENTER();
  1088. dwRet = WaitForSingleObject(hEvent, gc_dwCommandReceiveTimeout);
  1089. switch (dwRet)
  1090. {
  1091. case WAIT_OBJECT_0:
  1092. // this is the expected signal, continue
  1093. break;
  1094. case WAIT_FAILED:
  1095. lRet = GetLastError();
  1096. DPFX(DPFPREP, DVF_ERRORLEVEL, "Error waiting for event, code: %i", lRet);
  1097. DPF_EXIT();
  1098. return DVERR_WIN32;
  1099. case WAIT_TIMEOUT:
  1100. DPFX(DPFPREP, DVF_ERRORLEVEL, "Timed out waiting to receive command");
  1101. DPF_EXIT();
  1102. return DVERR_TIMEOUT;
  1103. default:
  1104. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unknown error waiting for event");
  1105. DPF_EXIT();
  1106. return DVERR_UNKNOWN;
  1107. }
  1108. // Copy the command from shared memory to the caller's
  1109. // buffer. Ensure the caller's buffer is large enough
  1110. if (pfdtc->dwSize < ((SFDTestCommand*)lpvShMemPtr)->dwSize)
  1111. {
  1112. // reply to both the sender and receiver that the
  1113. // buffer was not large enough! We already have an
  1114. // error, so ingore the return code from the reply
  1115. // call
  1116. DoReply(DVERR_BUFFERTOOSMALL, hReplyEvent, lpvShMemPtr);
  1117. DPF_EXIT();
  1118. return DVERR_BUFFERTOOSMALL;
  1119. }
  1120. CopyMemory(pfdtc, lpvShMemPtr, ((SFDTestCommand*)lpvShMemPtr)->dwSize);
  1121. // Let the caller know how much was copied
  1122. pfdtc->dwSize = ((SFDTestCommand*)lpvShMemPtr)->dwSize;
  1123. // all done.
  1124. DPF_EXIT();
  1125. return S_OK;
  1126. }
  1127. #undef DPF_MODNAME
  1128. #define DPF_MODNAME "DoReply"
  1129. HRESULT DoReply(HRESULT hr, HANDLE hReplyEvent, LPVOID lpvShMemPtr)
  1130. {
  1131. LONG lRet;
  1132. DPF_ENTER();
  1133. // Copy the return code to shared memory
  1134. *((HRESULT*)lpvShMemPtr) = hr;
  1135. // Signal that we have replied
  1136. if (!SetEvent(hReplyEvent))
  1137. {
  1138. lRet = GetLastError();
  1139. DPFX(DPFPREP, DVF_ERRORLEVEL, "Unable to set event, code: %i", lRet);
  1140. DPF_EXIT();
  1141. return DVERR_WIN32;
  1142. }
  1143. DPF_EXIT();
  1144. return S_OK;
  1145. }