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.

744 lines
17 KiB

  1. /*****************************************************************************
  2. *
  3. * DIReg.c
  4. *
  5. * Copyright (c) 1996 Microsoft Corporation. All Rights Reserved.
  6. *
  7. * Abstract:
  8. *
  9. * OLE self-registration.
  10. *
  11. * Contents:
  12. *
  13. * DllRegisterServer()
  14. * DllUnregisterServer()
  15. *
  16. *****************************************************************************/
  17. #include "dinputpr.h"
  18. /*****************************************************************************
  19. *
  20. * The sqiffle for this file.
  21. *
  22. *****************************************************************************/
  23. #define sqfl sqflDll
  24. /*****************************************************************************
  25. *
  26. * RegSetStringEx
  27. *
  28. * Add a REG_SZ to hkey\sub::value.
  29. *
  30. *****************************************************************************/
  31. void INTERNAL
  32. RegSetStringEx(HKEY hk, LPCTSTR ptszValue, LPCTSTR ptszData)
  33. {
  34. LONG lRc = RegSetValueEx(hk, ptszValue, 0, REG_SZ,
  35. (PV)ptszData, cbCtch(lstrlen(ptszData)+1));
  36. }
  37. /*****************************************************************************
  38. *
  39. * RegDelStringEx
  40. *
  41. * Remove a REG_SZ from hkey\sub::value. The data is ignored.
  42. * It's passed so that RegDelStringEx matches the prototype for a
  43. * REGSTRINGACTION.
  44. *
  45. *****************************************************************************/
  46. void INTERNAL
  47. RegDelStringEx(HKEY hk, LPCTSTR ptszValue, LPCTSTR ptszData)
  48. {
  49. LONG lRc = RegDeleteValue(hk, ptszValue);
  50. }
  51. /*****************************************************************************
  52. *
  53. * RegCloseFinish
  54. *
  55. * Just close the subkey already.
  56. *
  57. *****************************************************************************/
  58. void INTERNAL
  59. RegCloseFinish(HKEY hk, LPCTSTR ptszSub, HKEY hkSub)
  60. {
  61. LONG lRc = RegCloseKey(hkSub);
  62. }
  63. /*****************************************************************************
  64. *
  65. * RegDelFinish
  66. *
  67. * Delete a key if there is nothing in it.
  68. *
  69. * OLE unregistration rules demand that you not delete a key if OLE
  70. * has added something to it.
  71. *
  72. *****************************************************************************/
  73. void INTERNAL
  74. RegDelFinish(HKEY hk, LPCTSTR ptszSub, HKEY hkSub)
  75. {
  76. LONG lRc;
  77. DWORD cKeys = 0, cValues = 0;
  78. RegQueryInfoKey(hkSub, 0, 0, 0, &cKeys, 0, 0, &cValues, 0, 0, 0, 0);
  79. RegCloseKey(hkSub);
  80. if ((cKeys | cValues) == 0) {
  81. #ifdef WINNT
  82. lRc = DIWinnt_RegDeleteKey(hk, ptszSub);
  83. #else
  84. lRc = RegDeleteKey(hk, ptszSub);
  85. #endif
  86. } else {
  87. lRc = 0;
  88. }
  89. }
  90. #ifdef WINNT //The following are only used on WINNT
  91. /*****************************************************************************
  92. *
  93. * @doc INTERNAL
  94. *
  95. * @func void | RegSetPermissionsOnDescendants |
  96. *
  97. * Sets the specified permissions on all descendants of the specified key.
  98. *
  99. * @parm HKEY | hKey |
  100. *
  101. * The reg key on whose descendants we're operating.
  102. *
  103. * @parm SECURITY_DESCRIPTOR* | psd |
  104. *
  105. * Ptr to the SECURITY_DESCRIPTOR we're using.
  106. *
  107. * @returns
  108. *
  109. * Nothing.
  110. * Note that this recurses while having TCHAR szKeyName[MAX_PATH+1]
  111. * for each level. If stack space is a concern, can allocate it on the heap,
  112. * and free after obtain the HKEY (i.e. before recursing).
  113. *
  114. *****************************************************************************/
  115. void INTERNAL
  116. RegSetPermissionsOnDescendants(HKEY hKey, SECURITY_DESCRIPTOR* psd)
  117. {
  118. DWORD dwIndex = 0;
  119. LONG lRetCode = ERROR_SUCCESS;
  120. while (lRetCode == ERROR_SUCCESS)
  121. {
  122. TCHAR szKeyName[MAX_PATH+1];
  123. DWORD cbKeyName = MAX_PATH+1;
  124. lRetCode = RegEnumKeyEx(hKey,
  125. dwIndex,
  126. szKeyName,
  127. &cbKeyName,
  128. NULL, NULL, NULL, NULL);
  129. if (lRetCode == ERROR_SUCCESS)
  130. {
  131. LONG lRetSub;
  132. HKEY hkSubKey;
  133. lRetSub = RegOpenKeyEx(hKey, szKeyName, 0, DI_KEY_ALL_ACCESS | WRITE_DAC, &hkSubKey);
  134. if (lRetSub == ERROR_SUCCESS)
  135. {
  136. //set security on it and its descendants
  137. lRetSub = RegSetKeySecurity(hkSubKey,
  138. (SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
  139. psd);
  140. RegSetPermissionsOnDescendants(hkSubKey, psd);
  141. RegCloseKey(hkSubKey);
  142. if(lRetSub != ERROR_SUCCESS)
  143. {
  144. RPF("Couldn't RegSetKeySecurity on %hs", szKeyName);
  145. }
  146. }
  147. else
  148. {
  149. RPF("Couldn't open enumed subkey %hs", szKeyName);
  150. }
  151. dwIndex++;
  152. }
  153. }
  154. }
  155. /*****************************************************************************
  156. *
  157. * @doc INTERNAL
  158. *
  159. * @func HRESULT | RegSetSecurity |
  160. *
  161. * Set the security of
  162. * SYSTEM\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\Joystick\\OEM
  163. * SYSTEM\\CurrentControlSet\\Control\\MediaResources\\Joystick\\Dinput.dll
  164. * to be accessible to Everyone on Win2K,
  165. *. to be accessible to Everyone but without WRITE_DAC and WRITE_OWNER permissions on WinXP;
  166. * SYSTEM\\CurrentControlSet\\Control\\MediaProperties\\PrivateProperties\\DirectInput
  167. * to be accessible to Everyone but without WRITE_DAC and WRITE_OWNER permissions on Win2k and WinXP.
  168. *
  169. * @returns
  170. *
  171. * S_OK on success, E_FAIL on error.
  172. *
  173. *****************************************************************************/
  174. HRESULT INTERNAL
  175. RegSetSecurity(void)
  176. {
  177. SID_IDENTIFIER_AUTHORITY sia = SECURITY_WORLD_SID_AUTHORITY;
  178. PSID pEveryoneSid = NULL;
  179. SECURITY_DESCRIPTOR sd;
  180. PACL pDacl = NULL;
  181. DWORD dwAclSize;
  182. HKEY hkOEM, hkJoy, hkDin, hkPP, hkMedR, hkJDi;
  183. LONG lRetCode;
  184. BOOL bSuccess = FALSE; // assume this function fails
  185. BOOL bWinXP = FALSE;
  186. //determine whether we're running on Win2k or WinXP, since we would like
  187. //on Win2k to emulate the same behaviour as previously, but set different reg key
  188. //permissions on WinXp (see Whistler bugs 318865, 326784).
  189. if (DIGetOSVersion() == WINWH_OS)
  190. {
  191. bWinXP = TRUE;
  192. }
  193. //
  194. // open the keys for WRITE_DAC access
  195. //
  196. //MediaProperties/PrivateProperties/DirectInput
  197. lRetCode = RegOpenKeyEx(
  198. HKEY_LOCAL_MACHINE,
  199. REGSTR_PATH_PRIVATEPROPERTIES,
  200. 0,
  201. KEY_WRITE,
  202. &hkPP
  203. );
  204. if(lRetCode != ERROR_SUCCESS) {
  205. RPF("Couldn't open REGSTR_PATH_PRIVATEPROPERTIES");
  206. return E_FAIL;
  207. }
  208. lRetCode = RegCreateKeyEx(
  209. hkPP,
  210. TEXT("DirectInput"),
  211. 0,
  212. NULL,
  213. REG_OPTION_NON_VOLATILE,
  214. DI_KEY_ALL_ACCESS | WRITE_DAC,
  215. NULL,
  216. &hkDin,
  217. NULL
  218. );
  219. if(lRetCode != ERROR_SUCCESS) {
  220. RPF("Couldn't open DirectInput");
  221. return E_FAIL;
  222. }
  223. //MediaResources/Joystick/Dinput.dll
  224. lRetCode = RegOpenKeyEx(
  225. HKEY_LOCAL_MACHINE,
  226. REGSTR_PATH_MEDIARESOURCES,
  227. 0,
  228. KEY_WRITE,
  229. &hkMedR
  230. );
  231. if(lRetCode != ERROR_SUCCESS) {
  232. RPF("Couldn't open REGSTR_PATH_MEDIARESOURCES");
  233. return E_FAIL;
  234. }
  235. //on Win2K, we need to create MediaResources\Joystick\Dinput.dll reg key w/ KEY_ALL_ACCESS (Whistler bug 326874)
  236. if (bWinXP == TRUE)
  237. {
  238. lRetCode = RegCreateKeyEx(
  239. hkMedR,
  240. TEXT("Joystick"),
  241. 0,
  242. NULL,
  243. REG_OPTION_NON_VOLATILE,
  244. KEY_WRITE,
  245. NULL,
  246. &hkJoy,
  247. NULL
  248. );
  249. if(lRetCode != ERROR_SUCCESS) {
  250. RPF("Couldn't open Joystick");
  251. return E_FAIL;
  252. }
  253. lRetCode = RegCreateKeyEx(
  254. hkJoy,
  255. TEXT("Dinput.dll"),
  256. 0,
  257. NULL,
  258. REG_OPTION_NON_VOLATILE,
  259. DI_KEY_ALL_ACCESS | WRITE_DAC,
  260. NULL,
  261. &hkJDi,
  262. NULL
  263. );
  264. if(lRetCode != ERROR_SUCCESS) {
  265. RPF("Couldn't open Dinput.dll");
  266. return E_FAIL;
  267. }
  268. //MediaProperties/Joystick/OEM
  269. lRetCode = RegOpenKeyEx(
  270. HKEY_LOCAL_MACHINE,
  271. REGSTR_PATH_JOYOEM,
  272. 0,
  273. WRITE_DAC | KEY_ENUMERATE_SUB_KEYS,
  274. &hkOEM
  275. );
  276. if(lRetCode != ERROR_SUCCESS) {
  277. RPF("Couldn't open REGSTR_PATH_JOYOEM");
  278. return E_FAIL;
  279. }
  280. }
  281. else
  282. {
  283. lRetCode = RegCreateKeyEx(
  284. hkMedR,
  285. TEXT("Joystick"),
  286. 0,
  287. NULL,
  288. REG_OPTION_NON_VOLATILE,
  289. KEY_ALL_ACCESS,
  290. NULL,
  291. &hkJoy,
  292. NULL
  293. );
  294. if(lRetCode != ERROR_SUCCESS) {
  295. RPF("Couldn't open Joystick");
  296. return E_FAIL;
  297. }
  298. lRetCode = RegCreateKeyEx(
  299. hkJoy,
  300. TEXT("Dinput.dll"),
  301. 0,
  302. NULL,
  303. REG_OPTION_NON_VOLATILE,
  304. KEY_ALL_ACCESS,
  305. NULL,
  306. &hkJDi,
  307. NULL
  308. );
  309. if(lRetCode != ERROR_SUCCESS) {
  310. RPF("Couldn't open Dinput.dll");
  311. return E_FAIL;
  312. }
  313. }
  314. //
  315. // prepare a Sid representing any Interactively logged-on user
  316. //
  317. if(!AllocateAndInitializeSid(
  318. &sia,
  319. 1,
  320. SECURITY_WORLD_RID,
  321. 0, 0, 0, 0, 0, 0, 0,
  322. &pEveryoneSid
  323. ))
  324. {
  325. RPF("Couldn't AllocateAndInitializeSid");
  326. goto cleanup;
  327. }
  328. //
  329. // compute size of new acl
  330. //
  331. dwAclSize = sizeof(ACL) + sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)
  332. + GetLengthSid(pEveryoneSid);
  333. //
  334. // allocate storage for Acl
  335. //
  336. pDacl = (PACL)HeapAlloc(GetProcessHeap(), 0, dwAclSize);
  337. if(pDacl == NULL) {
  338. RPF("pACl is NULL");
  339. goto cleanup;
  340. }
  341. if(!InitializeAcl(pDacl, dwAclSize, ACL_REVISION)) {
  342. RPF("Couldn't InitializeAcl");
  343. goto cleanup;
  344. }
  345. //
  346. // grant the Interactive Sid KEY_READ access to the perf key
  347. //
  348. if(!AddAccessAllowedAceEx(
  349. pDacl,
  350. ACL_REVISION,
  351. CONTAINER_INHERIT_ACE,
  352. DI_KEY_ALL_ACCESS,
  353. pEveryoneSid
  354. ))
  355. {
  356. RPF("Couldn't AddAccessAllowedAceEx");
  357. goto cleanup;
  358. }
  359. if(!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION))
  360. {
  361. RPF("Couldn't InitilizeSecurityDescriptor");
  362. goto cleanup;
  363. }
  364. if(!SetSecurityDescriptorDacl(&sd, TRUE, pDacl, FALSE))
  365. {
  366. RPF("Couldn't SetSecurityDescriptorDacl");
  367. goto cleanup;
  368. }
  369. //make sure that it is not overwritten by the parent's security changes
  370. if(!SetSecurityDescriptorControl(&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED))
  371. {
  372. RPF("Couldn't SetSecurityDescriptorControl");
  373. goto cleanup;
  374. }
  375. //
  376. // apply the security descriptor to the registry keys and their subkeys
  377. //
  378. //MediaProperties/PrivateProperties/DirectInput and its subkeys
  379. lRetCode = RegSetKeySecurity(
  380. hkDin,
  381. (SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
  382. &sd
  383. );
  384. if(lRetCode != ERROR_SUCCESS) {
  385. RPF("Couldn't RegSetKeySecurity on DirectInput");
  386. goto cleanup;
  387. }
  388. RegSetPermissionsOnDescendants(hkDin, &sd);
  389. //on WinXP, we set the same permissions on Joystick/OEM and MediaResources/Joystick/Dinput.dll and their subkeys
  390. if (bWinXP == TRUE)
  391. {
  392. //Joystick/OEM
  393. lRetCode = RegSetKeySecurity(
  394. hkOEM,
  395. (SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
  396. &sd
  397. );
  398. if(lRetCode != ERROR_SUCCESS) {
  399. RPF("Couldn't RegSetKeySecurity on OEM");
  400. goto cleanup;
  401. }
  402. RegSetPermissionsOnDescendants(hkOEM, &sd);
  403. //MediaResources/Joystick/Dinput.dll and its subkeys
  404. lRetCode = RegSetKeySecurity(
  405. hkJDi,
  406. (SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
  407. &sd
  408. );
  409. if(lRetCode != ERROR_SUCCESS) {
  410. RPF("Couldn't RegSetKeySecurity on Dinput.dll");
  411. goto cleanup;
  412. }
  413. RegSetPermissionsOnDescendants(hkJDi, &sd);
  414. //indicate success
  415. bSuccess = TRUE;
  416. }
  417. else
  418. {
  419. //on Win2K, we leave Joystick/OEM keys alone,
  420. //and give full access to MediaResources\Joystick\Dinput.dll and its subkeys to everyone
  421. PACL pDaclFull = NULL;
  422. pDaclFull = (PACL)HeapAlloc(GetProcessHeap(), 0, dwAclSize);
  423. if(pDaclFull == NULL) {
  424. RPF("pAClFull is NULL");
  425. goto cleanupW2k;
  426. }
  427. if(!InitializeAcl(pDaclFull, dwAclSize, ACL_REVISION)) {
  428. RPF("Couldn't InitializeAcl");
  429. goto cleanupW2k;
  430. }
  431. if(!AddAccessAllowedAceEx(
  432. pDaclFull,
  433. ACL_REVISION,
  434. CONTAINER_INHERIT_ACE,
  435. KEY_ALL_ACCESS,
  436. pEveryoneSid
  437. ))
  438. {
  439. RPF("Couldn't AddAccessAllowedAceEx");
  440. goto cleanupW2k;
  441. }
  442. if(!SetSecurityDescriptorDacl(&sd, TRUE, pDaclFull, FALSE))
  443. {
  444. RPF("Couldn't SetSecurityDescriptorDacl");
  445. goto cleanupW2k;
  446. }
  447. //make sure that it is not overwritten by the parent's security changes
  448. if(!SetSecurityDescriptorControl(&sd, SE_DACL_PROTECTED, SE_DACL_PROTECTED))
  449. {
  450. RPF("Couldn't SetSecurityDescriptorControl");
  451. goto cleanupW2k;
  452. }
  453. //apply it to the key and its subkeys
  454. lRetCode = RegSetKeySecurity(
  455. hkJoy,
  456. (SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
  457. &sd
  458. );
  459. if(lRetCode != ERROR_SUCCESS) {
  460. RPF("Couldn't RegSetKeySecurity on Dinput.dll");
  461. goto cleanupW2k;
  462. }
  463. lRetCode = RegSetKeySecurity(
  464. hkJDi,
  465. (SECURITY_INFORMATION)DACL_SECURITY_INFORMATION,
  466. &sd
  467. );
  468. if(lRetCode != ERROR_SUCCESS) {
  469. RPF("Couldn't RegSetKeySecurity on Dinput.dll");
  470. goto cleanupW2k;
  471. }
  472. RegSetPermissionsOnDescendants(hkJDi, &sd);
  473. //indicate success
  474. bSuccess = TRUE;
  475. cleanupW2k:;
  476. if(pDaclFull != NULL)
  477. {
  478. HeapFree(GetProcessHeap(), 0, pDaclFull);
  479. }
  480. }
  481. cleanup:
  482. RegCloseKey(hkOEM);
  483. RegCloseKey(hkJDi);
  484. RegCloseKey(hkDin);
  485. RegCloseKey(hkPP);
  486. RegCloseKey(hkMedR);
  487. RegCloseKey(hkJoy);
  488. //
  489. // free allocated resources
  490. //
  491. if(pDacl != NULL) {
  492. HeapFree(GetProcessHeap(), 0, pDacl);
  493. }
  494. if(pEveryoneSid != NULL) {
  495. FreeSid(pEveryoneSid);
  496. }
  497. if(bSuccess) {
  498. return S_OK;
  499. } else {
  500. return E_FAIL;
  501. }
  502. }
  503. /*****************************************************************************
  504. *
  505. * @doc INTERNAL
  506. *
  507. * @func HRESULT | DummyRegSetSecurity |
  508. *
  509. * Do nothing
  510. *
  511. * @returns
  512. *
  513. * S_OK.
  514. *
  515. *****************************************************************************/
  516. HRESULT INTERNAL
  517. DummyRegSetSecurity(void)
  518. {
  519. return S_OK;
  520. }
  521. #endif //WINNT
  522. /*****************************************************************************
  523. *
  524. * REGVTBL
  525. *
  526. * Functions for dorking with a registry key, either coming or going.
  527. *
  528. *****************************************************************************/
  529. typedef struct REGVTBL {
  530. /* How to create/open a key */
  531. LONG (INTERNAL *KeyAction)(HKEY hk, LPCTSTR ptszSub, PHKEY phkOut);
  532. /* How to create/delete a string */
  533. void (INTERNAL *StringAction)(HKEY hk, LPCTSTR ptszValue, LPCTSTR ptszData);
  534. /* How to finish using a key */
  535. void (INTERNAL *KeyFinish)(HKEY hk, LPCTSTR ptszSub, HKEY hkSub);
  536. #ifdef WINNT
  537. /* How to set security on OEM key */
  538. HRESULT (INTERNAL *SetSecurity)( void );
  539. #endif //WINNT
  540. } REGVTBL, *PREGVTBL;
  541. typedef const REGVTBL *PCREGVTBL;
  542. #ifdef WINNT
  543. const REGVTBL c_vtblAdd = { RegCreateKey, RegSetStringEx, RegCloseFinish, RegSetSecurity };
  544. const REGVTBL c_vtblDel = { RegOpenKey, RegDelStringEx, RegDelFinish, DummyRegSetSecurity };
  545. #else
  546. const REGVTBL c_vtblAdd = { RegCreateKey, RegSetStringEx, RegCloseFinish };
  547. const REGVTBL c_vtblDel = { RegOpenKey, RegDelStringEx, RegDelFinish };
  548. #endif //WINNT
  549. /*****************************************************************************
  550. *
  551. * @doc INTERNAL
  552. *
  553. * @func void | DllServerAction |
  554. *
  555. * Register or unregister our objects with OLE/COM/ActiveX/
  556. * whatever its name is.
  557. *
  558. *****************************************************************************/
  559. #pragma BEGIN_CONST_DATA
  560. extern const TCHAR c_tszNil[];
  561. #define ctchClsid ctchGuid
  562. const TCHAR c_tszClsidGuid[] =
  563. TEXT("CLSID\\{%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}");
  564. const TCHAR c_tszInProcServer32[] = TEXT("InProcServer32");
  565. const TCHAR c_tszThreadingModel[] = TEXT("ThreadingModel");
  566. const TCHAR c_tszBoth[] = TEXT("Both");
  567. #pragma END_CONST_DATA
  568. void INTERNAL
  569. DllServerAction(PCREGVTBL pvtbl)
  570. {
  571. TCHAR tszThisDll[MAX_PATH];
  572. UINT iclsidmap;
  573. GetModuleFileName(g_hinst, tszThisDll, cA(tszThisDll));
  574. for (iclsidmap = 0; iclsidmap < cclsidmap; iclsidmap++) {
  575. TCHAR tszClsid[7+ctchClsid];
  576. HKEY hkClsid;
  577. HKEY hkSub;
  578. REFCLSID rclsid = c_rgclsidmap[iclsidmap].rclsid;
  579. wsprintf(tszClsid, c_tszClsidGuid,
  580. rclsid->Data1, rclsid->Data2, rclsid->Data3,
  581. rclsid->Data4[0], rclsid->Data4[1],
  582. rclsid->Data4[2], rclsid->Data4[3],
  583. rclsid->Data4[4], rclsid->Data4[5],
  584. rclsid->Data4[6], rclsid->Data4[7]);
  585. if (pvtbl->KeyAction(HKEY_CLASSES_ROOT, tszClsid, &hkClsid) == 0) {
  586. TCHAR tszName[127];
  587. /* Do the type name */
  588. LoadString(g_hinst, c_rgclsidmap[iclsidmap].ids,
  589. tszName, cA(tszName));
  590. pvtbl->StringAction(hkClsid, 0, tszName);
  591. /* Do the in-proc server name and threading model */
  592. if (pvtbl->KeyAction(hkClsid, c_tszInProcServer32, &hkSub) == 0) {
  593. pvtbl->StringAction(hkSub, 0, tszThisDll);
  594. pvtbl->StringAction(hkSub, c_tszThreadingModel, c_tszBoth);
  595. pvtbl->KeyFinish(hkClsid, c_tszInProcServer32, hkSub);
  596. }
  597. pvtbl->KeyFinish(HKEY_CLASSES_ROOT, tszClsid, hkClsid);
  598. }
  599. }
  600. #ifdef WINNT
  601. pvtbl->SetSecurity();
  602. #endif
  603. }
  604. /*****************************************************************************
  605. *
  606. * @doc INTERNAL
  607. *
  608. * @func void | DllRegisterServer |
  609. *
  610. * Register our classes with OLE/COM/ActiveX/whatever its name is.
  611. *
  612. *****************************************************************************/
  613. void EXTERNAL
  614. DllRegisterServer(void)
  615. {
  616. DllServerAction(&c_vtblAdd);
  617. }
  618. /*****************************************************************************
  619. *
  620. * @doc INTERNAL
  621. *
  622. * @func void | DllUnregisterServer |
  623. *
  624. * Unregister our classes from OLE/COM/ActiveX/whatever its name is.
  625. *
  626. *****************************************************************************/
  627. void EXTERNAL
  628. DllUnregisterServer(void)
  629. {
  630. DllServerAction(&c_vtblDel);
  631. }