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.

1709 lines
41 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. ochelper.c
  5. Abstract:
  6. Helper/callback routines, available to plug-in components
  7. when processing interface routine calls.
  8. Author:
  9. Ted Miller (tedm) 13-Sep-1996
  10. Revision History:
  11. --*/
  12. #include "precomp.h"
  13. #pragma hdrstop
  14. #include <stdio.h>
  15. #include <stdarg.h>
  16. //
  17. // Window handle of wizard dialog. Set when the OC Manager client
  18. // calls OcRememberWizardDialogHandle.
  19. //
  20. HWND WizardDialogHandle;
  21. VOID
  22. OcHelperShowHideWizardPage(
  23. IN PVOID OcManagerContext,
  24. IN BOOL bShow
  25. )
  26. {
  27. POC_MANAGER p = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  28. if (p->Callbacks.ShowHideWizardPage)
  29. {
  30. // If we have a callback, hide the wizard.
  31. p->Callbacks.ShowHideWizardPage(bShow);
  32. }
  33. }
  34. VOID
  35. OcHelperTickGauge(
  36. IN PVOID OcManagerContext
  37. )
  38. /*++
  39. Routine Description:
  40. Function used while processing the OC_COMPLETE_INSTALLATION Interface function
  41. to step the progress gauge being run by the OC Manager.
  42. Ignored at other times.
  43. Arguments:
  44. OcManagerContext - supplies OC Manager context information the component gets
  45. from the OcManagerContext field of the OCMANAGER_ROUTINES structure.
  46. Return Value:
  47. None.
  48. --*/
  49. {
  50. pOcTickSetupGauge(((PHELPER_CONTEXT)OcManagerContext)->OcManager);
  51. }
  52. VOID
  53. #ifdef UNICODE
  54. OcHelperSetProgressTextW(
  55. #else
  56. OcHelperSetProgressTextA(
  57. #endif
  58. IN PVOID OcManagerContext,
  59. IN LPCTSTR Text
  60. )
  61. /*++
  62. Routine Description:
  63. Function used while processing the OC_COMPLETE_INSTALLATION Interface function
  64. to change the text associated with the progress gauge being run by
  65. the OC Manager. Ignored at other times.
  66. Arguments:
  67. OcManagerContext - supplies OC Manager context information the component gets
  68. from the OcManagerContext field of the OCMANAGER_ROUTINES structure.
  69. Text - Supplies the text for the progress gauge. The component should try to
  70. respect the current language parameters established by OC_SET_LANGUAGE.
  71. The OC Manager will make a copy of the string and truncate it as necessary.
  72. Return Value:
  73. None.
  74. --*/
  75. {
  76. POC_MANAGER p = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  77. #ifdef DEBUGPERFTRACE
  78. static DWORD lasttickcount = 0;
  79. static DWORD currenttickcount = 0;
  80. static DWORD diff, lastdiff;
  81. if (!lasttickcount)
  82. lasttickcount = GetTickCount();
  83. lasttickcount = currenttickcount;
  84. currenttickcount = GetTickCount();
  85. lastdiff = diff;
  86. diff = currenttickcount - lasttickcount;
  87. TRACE(( TEXT("SetProgressText at %d (last = %d, diff = %d, last diff = %d)\n"), currenttickcount, lasttickcount, diff, lastdiff ));
  88. if (diff > 1000*3) {
  89. WRN(( TEXT("It's been > 3 seconds since the last tick count update...the user is getting impatient.\n") ));
  90. }
  91. #endif
  92. if(p->ProgressTextWindow) {
  93. SetWindowText(p->ProgressTextWindow,Text);
  94. }
  95. }
  96. #ifdef UNICODE
  97. VOID
  98. OcHelperSetProgressTextA(
  99. IN PVOID OcManagerContext,
  100. IN LPCSTR Text
  101. )
  102. /*++
  103. Routine Description:
  104. ANSI version of OcHelperSetProgressText().
  105. Arguments:
  106. Return Value:
  107. --*/
  108. {
  109. LPCWSTR p;
  110. if(p = pSetupAnsiToUnicode(Text)){
  111. OcHelperSetProgressTextW(OcManagerContext,p);
  112. pSetupFree(p);
  113. }
  114. }
  115. #endif
  116. UINT
  117. _OcHelperSetPrivateData(
  118. IN PVOID OcManagerContext,
  119. IN LPCVOID Name,
  120. IN PVOID Data,
  121. IN UINT Size,
  122. IN UINT Type,
  123. IN BOOL IsNativeCharWidth
  124. )
  125. {
  126. PHELPER_CONTEXT HelperContext;
  127. DWORD rc;
  128. DWORD Disposition;
  129. HKEY hKey;
  130. LONG id;
  131. HelperContext = OcManagerContext;
  132. //
  133. // Fetch the component id. If we're processing an interface routine
  134. // then use the component id of the active component. Otherwise
  135. // resort to the component id stored in the helper context.
  136. //
  137. if(HelperContext->OcManager->CurrentComponentStringId == -1) {
  138. id = HelperContext->ComponentStringId;
  139. } else {
  140. id = HelperContext->OcManager->CurrentComponentStringId;
  141. }
  142. rc = RegCreateKeyEx(
  143. HelperContext->OcManager->hKeyPrivateData,
  144. pSetupStringTableStringFromId(HelperContext->OcManager->ComponentStringTable,id),
  145. 0,
  146. NULL,
  147. REG_OPTION_VOLATILE,
  148. KEY_SET_VALUE,
  149. NULL,
  150. &hKey,
  151. &Disposition
  152. );
  153. if(rc == NO_ERROR) {
  154. if(IsNativeCharWidth) {
  155. rc = RegSetValueEx(hKey,Name,0,Type,Data,Size);
  156. } else {
  157. rc = RegSetValueExA(hKey,Name,0,Type,Data,Size);
  158. }
  159. RegCloseKey(hKey);
  160. }
  161. return(rc);
  162. }
  163. UINT
  164. #ifdef UNICODE
  165. OcHelperSetPrivateDataW(
  166. #else
  167. OcHelperSetPrivateDataA(
  168. #endif
  169. IN PVOID OcManagerContext,
  170. IN LPCTSTR Name,
  171. IN PVOID Data,
  172. IN UINT Size,
  173. IN UINT Type
  174. )
  175. /*++
  176. Routine Description:
  177. Function to set a named datum that can then be retrieved later
  178. by the component or by any other component, via the GetPrivateData
  179. helper routine.
  180. This routine can be called at any time.
  181. Arguments:
  182. OcManagerContext - supplies OC Manager context information the component gets
  183. from the OcManagerContext field of the OCMANAGER_ROUTINES structure.
  184. Name - Supplies a name for the datum. If a datum with this name
  185. already exists for the component, it is overwritten.
  186. Data - Supplies the data itself. The OC Manager makes a copy of the data
  187. so the component need not ensure that this buffer remains valid.
  188. Size - Supplies the size in bytes of the data.
  189. Type - Supplies the type of the data. Components should use the standard registry
  190. type names (REG_SZ, REG_BINARY, etc.) to facilitate inter-component
  191. data sharing.
  192. Return Value:
  193. Win32 error code indicating outcome; NO_ERROR means success.
  194. --*/
  195. {
  196. return(_OcHelperSetPrivateData(OcManagerContext,Name,Data,Size,Type,TRUE));
  197. }
  198. #ifdef UNICODE
  199. UINT
  200. OcHelperSetPrivateDataA(
  201. IN PVOID OcManagerContext,
  202. IN LPCSTR Name,
  203. IN PVOID Data,
  204. IN UINT Size,
  205. IN UINT Type
  206. )
  207. /*++
  208. Routine Description:
  209. ANSI version of OcHelperSetPrivateData().
  210. Arguments:
  211. Return Value:
  212. --*/
  213. {
  214. return(_OcHelperSetPrivateData(OcManagerContext,Name,Data,Size,Type,FALSE));
  215. }
  216. #endif
  217. UINT
  218. _OcHelperGetPrivateData(
  219. IN PVOID OcManagerContext,
  220. IN LPCTSTR ComponentId, OPTIONAL
  221. IN LPCVOID Name,
  222. OUT PVOID Data, OPTIONAL
  223. IN OUT PUINT Size,
  224. OUT PUINT Type,
  225. IN BOOL IsNativeCharWidth
  226. )
  227. {
  228. PHELPER_CONTEXT HelperContext;
  229. PCTSTR ComponentName;
  230. DWORD rc;
  231. DWORD Disposition;
  232. HKEY hKey;
  233. LONG id;
  234. HelperContext = OcManagerContext;
  235. //
  236. // Figure out the name of the component that owns the data.
  237. //
  238. if(ComponentId) {
  239. ComponentName = ComponentId;
  240. } else {
  241. if(HelperContext->OcManager->CurrentComponentStringId == -1) {
  242. id = HelperContext->ComponentStringId;
  243. } else {
  244. id = HelperContext->OcManager->CurrentComponentStringId;
  245. }
  246. ComponentName = pSetupStringTableStringFromId(
  247. HelperContext->OcManager->ComponentStringTable,
  248. id
  249. );
  250. if (!ComponentName) {
  251. rc = GetLastError();
  252. goto exit;
  253. }
  254. }
  255. rc = RegCreateKeyEx(
  256. HelperContext->OcManager->hKeyPrivateData,
  257. ComponentName,
  258. 0,
  259. NULL,
  260. REG_OPTION_VOLATILE,
  261. KEY_QUERY_VALUE,
  262. NULL,
  263. &hKey,
  264. &Disposition
  265. );
  266. if(rc == NO_ERROR) {
  267. if(IsNativeCharWidth) {
  268. rc = RegQueryValueEx(hKey,Name,0,Type,Data,Size);
  269. } else {
  270. rc = RegQueryValueExA(hKey,Name,0,Type,Data,Size);
  271. }
  272. if(rc == ERROR_MORE_DATA) {
  273. rc = ERROR_INSUFFICIENT_BUFFER;
  274. }
  275. RegCloseKey(hKey);
  276. }
  277. exit:
  278. return(rc);
  279. }
  280. UINT
  281. #ifdef UNICODE
  282. OcHelperGetPrivateDataW(
  283. #else
  284. OcHelperGetPrivateDataA(
  285. #endif
  286. IN PVOID OcManagerContext,
  287. IN LPCTSTR ComponentId, OPTIONAL
  288. IN LPCTSTR Name,
  289. OUT PVOID Data, OPTIONAL
  290. IN OUT PUINT Size,
  291. OUT PUINT Type
  292. )
  293. /*++
  294. Routine Description:
  295. Function to retrieve a named datum that was previously set via
  296. SetPrivateData by the component or by any other component.
  297. This routine can be called at any time.
  298. Arguments:
  299. OcManagerContext - supplies OC Manager context information the component gets
  300. from the OcManagerContext field of the OCMANAGER_ROUTINES structure.
  301. ComponentId - Supplies the short descriptive name of the component
  302. (as specified in OC.INF) that set/owns the datum to be retrieved.
  303. NULL means the current component.
  304. Name - Supplies the name of the datum to be retrieved.
  305. Data - If specified, receives the data. If not specified, the routine puts
  306. the required size in Size and returns NO_ERROR.
  307. Size - On input, supplies the size of the buffer pointed to by Data
  308. (ignored if Data is not specified). On output, receives the size of
  309. the data stored, or the required size if the buffer is not large enough.
  310. Type - Upon successful completion receives the type of the datum.
  311. Return Value:
  312. NO_ERROR: If Data was specified, the requested datum had been placed in
  313. the caller's buffer. If Data was not specified, then the required size
  314. has been placed in the UINT pointed to by Size.
  315. ERROR_INSUFFICIENT_BUFFER: the specified buffer size is too small
  316. to contain the datum. The required size has been placed in the UINT
  317. pointed to by Size.
  318. Other Win32 error codes indicate additional error cases, such as the
  319. datum not being found, etc.
  320. --*/
  321. {
  322. return(_OcHelperGetPrivateData(OcManagerContext,ComponentId,Name,Data,Size,Type,TRUE));
  323. }
  324. #ifdef UNICODE
  325. UINT
  326. OcHelperGetPrivateDataA(
  327. IN PVOID OcManagerContext,
  328. IN LPCSTR ComponentId, OPTIONAL
  329. IN LPCSTR Name,
  330. OUT PVOID Data, OPTIONAL
  331. IN OUT PUINT Size,
  332. OUT PUINT Type
  333. )
  334. /*++
  335. Routine Description:
  336. ANSI version of OcHelperGetPrivateData().
  337. Arguments:
  338. Return Value:
  339. --*/
  340. {
  341. LPCWSTR component;
  342. UINT u;
  343. if(ComponentId) {
  344. component = pSetupAnsiToUnicode(ComponentId);
  345. if(!component) {
  346. u = ERROR_NOT_ENOUGH_MEMORY;
  347. goto c0;
  348. }
  349. } else {
  350. component = NULL;
  351. }
  352. u = _OcHelperGetPrivateData(OcManagerContext,component,Name,Data,Size,Type,FALSE);
  353. if(component) {
  354. pSetupFree(component);
  355. }
  356. c0:
  357. return(u);
  358. }
  359. #endif
  360. UINT
  361. OcHelperSetSetupMode(
  362. IN PVOID OcManagerContext,
  363. IN DWORD SetupMode
  364. )
  365. /*++
  366. Routine Description:
  367. Function that can be used at any time while a component is
  368. processing any Interface function or any of its wizard pages.
  369. It is used to set the current setup mode. It is expected that if a
  370. component supplies a setup mode page, it will use this routine to
  371. inform the OC Manager of the setup mode the user chose.
  372. Arguments:
  373. OcManagerContext - supplies OC Manager context information the component gets
  374. from the OcManagerContext field of the OCMANAGER_ROUTINES structure.
  375. SetupMode - Supplies a numeric value that indicates the setup mode. This may be
  376. any of the 4 standard values (minimal, laptop, custom, or typical).
  377. It can also be any other private value that has meaning to a suite or bundle.
  378. In that case the low 8 bits are interpreted as one of the standard values
  379. so the mode can be meaningful to other components and the OC Manager,
  380. who know nothing about private mode types. The component setting the mode
  381. should attempt to set this as reasonably as possible. The upper 24 bits are
  382. essentially private mode data.
  383. Return Value:
  384. The return value is the previous mode.
  385. --*/
  386. {
  387. POC_MANAGER p = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  388. UINT Mode;
  389. Mode = p->SetupMode;
  390. p->SetupMode = SetupMode;
  391. return(Mode);
  392. }
  393. UINT
  394. OcHelperGetSetupMode(
  395. IN PVOID OcManagerContext
  396. )
  397. /*++
  398. Routine Description:
  399. Function that can be used at any time while a component is processing
  400. any Interface function or any of its wizard pages. It is used to query
  401. the current setup mode. Note that this can be a private mode type,
  402. in which case the low 8 bits of the mode value can be interpreted as one of
  403. the standard 4 mode types and the upper 24 bits are private mode data.
  404. The mode can also be unknown. See section 3.2.1 for a list of standard
  405. mode types.
  406. Arguments:
  407. OcManagerContext - supplies OC Manager context information the component gets
  408. from the OcManagerContext field of the OCMANAGER_ROUTINES structure.
  409. Return Value:
  410. The return value is the current mode.
  411. --*/
  412. {
  413. POC_MANAGER p = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  414. return(p->SetupMode);
  415. }
  416. BOOL
  417. #ifdef UNICODE
  418. OcHelperQuerySelectionStateW(
  419. #else
  420. OcHelperQuerySelectionStateA(
  421. #endif
  422. IN PVOID OcManagerContext,
  423. IN LPCTSTR SubcomponentId,
  424. IN UINT StateType
  425. )
  426. /*++
  427. Routine Description:
  428. Function that can be used at any time.
  429. It is used determine the selection status of a particular subcomponent.
  430. Arguments:
  431. OcManagerContext - supplies OC Manager context information the component gets
  432. from the OcManagerContext field of the OCMANAGER_ROUTINES structure.
  433. SubcomponentId - Supplies a subidentifier meaningful to the component
  434. being called. The OC Manager imposes no semantics on this subidentifier.
  435. StateType - supplies a constant indicating which state is to be returned
  436. (original or current).
  437. Return Value:
  438. Boolean value indicating whether the subcomponent is selected
  439. for installation. If FALSE, GetLastError() will return something other than
  440. NO_ERROR if an error occurred, such as SubcomponentId being invalid.
  441. --*/
  442. {
  443. POC_MANAGER p = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  444. OPTIONAL_COMPONENT Oc;
  445. LONG l;
  446. BOOL b;
  447. l = pSetupStringTableLookUpStringEx(
  448. p->ComponentStringTable,
  449. (LPTSTR)SubcomponentId,
  450. STRTAB_CASE_INSENSITIVE,
  451. &Oc,
  452. sizeof(OPTIONAL_COMPONENT)
  453. );
  454. if(l == -1) {
  455. SetLastError(ERROR_INVALID_NAME);
  456. return(FALSE);
  457. }
  458. switch(StateType) {
  459. case OCSELSTATETYPE_ORIGINAL:
  460. b = (Oc.OriginalSelectionState != SELSTATE_NO);
  461. SetLastError(NO_ERROR);
  462. break;
  463. case OCSELSTATETYPE_CURRENT:
  464. b = (Oc.SelectionState != SELSTATE_NO);
  465. SetLastError(NO_ERROR);
  466. break;
  467. case OCSELSTATETYPE_FINAL:
  468. b = (Oc.SelectionState != SELSTATE_NO);
  469. SetLastError(NO_ERROR);
  470. break;
  471. default:
  472. b = FALSE;
  473. SetLastError(ERROR_INVALID_PARAMETER);
  474. break;
  475. }
  476. return(b);
  477. }
  478. #ifdef UNICODE
  479. OcHelperQuerySelectionStateA(
  480. IN PVOID OcManagerContext,
  481. IN LPCSTR SubcomponentId,
  482. IN UINT StateType
  483. )
  484. {
  485. LPCWSTR id;
  486. DWORD d;
  487. BOOL b;
  488. id = pSetupAnsiToUnicode(SubcomponentId);
  489. if(!id) {
  490. SetLastError(ERROR_NOT_ENOUGH_MEMORY);
  491. return(FALSE);
  492. }
  493. b = OcHelperQuerySelectionStateW(OcManagerContext,id,StateType);
  494. d = GetLastError();
  495. pSetupFree(id);
  496. SetLastError(d);
  497. return(b);
  498. }
  499. #endif
  500. UINT
  501. #ifdef UNICODE
  502. OcHelperCallPrivateFunctionW(
  503. #else
  504. OcHelperCallPrivateFunctionA(
  505. #endif
  506. IN PVOID OcManagerContext,
  507. IN LPCTSTR ComponentId,
  508. IN LPCTSTR SubcomponentId,
  509. IN UINT Function,
  510. IN UINT Param1,
  511. IN OUT PVOID Param2,
  512. OUT PUINT Result
  513. )
  514. /*++
  515. Routine Description:
  516. Function that can be used at any time while a component is
  517. processing any Interface function. It is used to call another component's
  518. interface entry point to perform some private function. This function
  519. cannot be used to call a standard interface function, nor can it be used
  520. to call a function in the DLL of the component making the call.
  521. Arguments:
  522. OcManagerContext - supplies OC Manager context information the component gets
  523. from the OcManagerContext field of the OCMANAGER_ROUTINES structure.
  524. ComponentId - Supplies the short descriptive name of the component
  525. (as specified in OC.INF) to be called. This may not be the name of
  526. the current component.
  527. SubcomponentId - Supplies a subidentifier meaningful to the component
  528. being called. The OC Manager imposes no semantics on this subidentifier.
  529. Function - Supplies a function code meaningful to the component being called.
  530. This may not be one of the standard interface function codes.
  531. Param1, Param2 - Supply values meaningful to the component being called.
  532. The OC Manager imposes no semantics on these values.
  533. Result - If the OC Manager is successful in calling the other component,
  534. then this receives the return value from the other component's
  535. interface routine.
  536. Return Value:
  537. Win32 error code indicating outcome. If NO_ERROR, then the other component
  538. was called and the result is stored in Result. If not NO_ERROR,
  539. then the other component was not called.
  540. ERROR_BAD_ENVIRONMENT - the function was called when the component is not
  541. processing an interface routine, or the caller is attempting to
  542. call a routine in itself.
  543. ERROR_INVALID_FUNCTION - Function is less then OC_PRIVATE_BASE.
  544. ERROR_ACCESS_DENIED - the requested component has no interface entry point.
  545. --*/
  546. {
  547. POC_MANAGER p = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  548. BOOL b;
  549. LONG l;
  550. UINT i;
  551. OPTIONAL_COMPONENT OtherOc;
  552. LONG PreviousCurrentComponentStringId;
  553. //
  554. // Validate that we are processing an interface function and
  555. // that the requested function is not a standard function,
  556. // and that the caller wants to call a different component.
  557. //
  558. if(Function < OC_PRIVATE_BASE) {
  559. return(ERROR_INVALID_FUNCTION);
  560. }
  561. l = pSetupStringTableLookUpStringEx(
  562. p->ComponentStringTable,
  563. (PTSTR)ComponentId,
  564. STRTAB_CASE_INSENSITIVE,
  565. &OtherOc,
  566. sizeof(OPTIONAL_COMPONENT)
  567. );
  568. if(l == -1) {
  569. return(ERROR_INVALID_FUNCTION);
  570. }
  571. //
  572. // Make sure the component is top-level.
  573. //
  574. for(b=FALSE,i=0; !b && (i<p->TopLevelOcCount); i++) {
  575. if(p->TopLevelOcStringIds[i] == l) {
  576. b = TRUE;
  577. }
  578. }
  579. if((l == p->CurrentComponentStringId) || (p->CurrentComponentStringId == -1) || !b) {
  580. return(ERROR_BAD_ENVIRONMENT);
  581. }
  582. //
  583. // Make sure the component has an entry point.
  584. //
  585. if(!OtherOc.InstallationRoutine) {
  586. return(ERROR_ACCESS_DENIED);
  587. }
  588. //
  589. // Call the other component.
  590. //
  591. #ifdef UNICODE
  592. //
  593. // If necessary, convert component and subcomponent to ansi
  594. //
  595. if(OtherOc.Flags & OCFLAG_ANSI) {
  596. LPCSTR comp,subcomp;
  597. comp = pSetupUnicodeToAnsi(ComponentId);
  598. if(!comp) {
  599. return(ERROR_NOT_ENOUGH_MEMORY);
  600. }
  601. if(SubcomponentId) {
  602. subcomp = pSetupUnicodeToAnsi(SubcomponentId);
  603. if(!subcomp) {
  604. pSetupFree(comp);
  605. return(ERROR_NOT_ENOUGH_MEMORY);
  606. }
  607. } else {
  608. subcomp = NULL;
  609. }
  610. PreviousCurrentComponentStringId = p->CurrentComponentStringId;
  611. p->CurrentComponentStringId = l;
  612. *Result = CallComponent(p, &OtherOc, comp, subcomp, Function, Param1, Param2);
  613. pSetupFree(comp);
  614. if(subcomp) {
  615. pSetupFree(subcomp);
  616. }
  617. } else
  618. #endif
  619. {
  620. PreviousCurrentComponentStringId = p->CurrentComponentStringId;
  621. p->CurrentComponentStringId = l;
  622. *Result = CallComponent(p, &OtherOc, ComponentId, SubcomponentId, Function, Param1, Param2);
  623. }
  624. p->CurrentComponentStringId = PreviousCurrentComponentStringId;
  625. return(NO_ERROR);
  626. }
  627. #ifdef UNICODE
  628. UINT
  629. OcHelperCallPrivateFunctionA(
  630. IN PVOID OcManagerContext,
  631. IN LPCSTR ComponentId,
  632. IN LPCSTR SubcomponentId,
  633. IN UINT Function,
  634. IN UINT Param1,
  635. IN OUT PVOID Param2,
  636. OUT PUINT Result
  637. )
  638. /*++
  639. Routine Description:
  640. ANSI version of OcHelperCallPrivateFunction().
  641. Arguments:
  642. Return Value:
  643. --*/
  644. {
  645. LPCWSTR comp,subcomp;
  646. UINT u;
  647. comp = pSetupAnsiToUnicode(ComponentId);
  648. if(!comp) {
  649. u = ERROR_NOT_ENOUGH_MEMORY;
  650. goto c0;
  651. }
  652. if(SubcomponentId) {
  653. subcomp = pSetupAnsiToUnicode(SubcomponentId);
  654. if(!subcomp) {
  655. u = ERROR_NOT_ENOUGH_MEMORY;
  656. goto c1;
  657. }
  658. } else {
  659. subcomp = NULL;
  660. }
  661. u = OcHelperCallPrivateFunctionW(
  662. OcManagerContext,
  663. comp,
  664. subcomp,
  665. Function,
  666. Param1,
  667. Param2,
  668. Result
  669. );
  670. if(subcomp) {
  671. pSetupFree(subcomp);
  672. }
  673. c1:
  674. pSetupFree(comp);
  675. c0:
  676. return(u);
  677. }
  678. #endif
  679. BOOL
  680. OcHelperConfirmCancel(
  681. IN HWND ParentWindow
  682. )
  683. /*++
  684. Routine Description:
  685. Ask the user whether he's sure he wants to cancel.
  686. Arguments:
  687. ParentWindow - supplies window handle of parent window for ui
  688. Return Value:
  689. TRUE if user wants to cancel. FALSE if not.
  690. --*/
  691. {
  692. TCHAR Message[1000];
  693. TCHAR Caption[200];
  694. int i;
  695. FormatMessage(
  696. FORMAT_MESSAGE_FROM_HMODULE,
  697. MyModuleHandle,
  698. MSG_QUERY_CANCEL,
  699. 0,
  700. Message,
  701. sizeof(Message)/sizeof(TCHAR),
  702. NULL
  703. );
  704. LoadString(MyModuleHandle,IDS_SETUP,Caption,sizeof(Caption)/sizeof(TCHAR));
  705. i = MessageBox(
  706. ParentWindow,
  707. Message,
  708. Caption,
  709. MB_YESNO | MB_ICONQUESTION | MB_DEFBUTTON2 | MB_SETFOREGROUND
  710. );
  711. return(i == IDYES);
  712. }
  713. HWND
  714. OcHelperQueryWizardDialogHandle(
  715. IN PVOID OcManagerContext
  716. )
  717. {
  718. UNREFERENCED_PARAMETER(OcManagerContext);
  719. return(WizardDialogHandle);
  720. }
  721. BOOL
  722. OcHelperSetReboot(
  723. IN PVOID OcManagerContext,
  724. IN BOOL Reserved
  725. )
  726. {
  727. POC_MANAGER p = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  728. UNREFERENCED_PARAMETER(Reserved);
  729. p->Callbacks.SetReboot();
  730. return(FALSE);
  731. }
  732. VOID
  733. OcRememberWizardDialogHandle(
  734. IN PVOID OcManagerContext,
  735. IN HWND DialogHandle
  736. )
  737. /*++
  738. Routine Description:
  739. This routine is called by the OC Manager client to inform the
  740. common library of the wizard's dialog handle.
  741. We will also turn around and notify all top-level components
  742. of the wizard dialog handle.
  743. Before doing so, we write the title into the window header.
  744. Arguments:
  745. OcManagerContext - value returned from OcInitialize().
  746. DialogHandle - supplies the dialog handle of the wizard.
  747. Return Value:
  748. None.
  749. --*/
  750. {
  751. UINT i;
  752. POC_MANAGER OcManager;
  753. WizardDialogHandle = DialogHandle;
  754. OcManager = OcManagerContext;
  755. if (*OcManager->WindowTitle) {
  756. SetWindowText(WizardDialogHandle, OcManager->WindowTitle);
  757. }
  758. for(i=0; i<OcManager->TopLevelOcCount; i++) {
  759. OcInterfaceWizardCreated(OcManager,OcManager->TopLevelOcStringIds[i],DialogHandle);
  760. }
  761. }
  762. HINF
  763. OcHelperGetInfHandle(
  764. IN UINT InfIndex,
  765. IN PVOID OcManagerContext
  766. )
  767. /*++
  768. Routine Description:
  769. This routine returns a handle to a well-known inf file that
  770. has been opened by oc manager.
  771. Arguments:
  772. InfIndex - supplies value indicating which inf's handle is desired
  773. OcManagerContext - value returned from OcInitialize().
  774. Return Value:
  775. Handle to INF, NULL if error.
  776. --*/
  777. {
  778. POC_MANAGER OcManager = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  779. return((InfIndex == INFINDEX_UNATTENDED) ? OcManager->UnattendedInf : NULL);
  780. }
  781. BOOL
  782. OcHelperClearExternalError (
  783. IN POC_MANAGER OcManager,
  784. IN LONG ComponentId,
  785. IN LONG SubcomponentId OPTIONAL
  786. )
  787. {
  788. LONG l;
  789. DWORD d;
  790. HKEY hKey;
  791. LPCTSTR pValueName;
  792. //
  793. // If Subcomponetid is Zero then use the ComponentId
  794. //
  795. if ( SubcomponentId ) {
  796. pValueName = pSetupStringTableStringFromId(OcManager->ComponentStringTable,SubcomponentId);
  797. } else {
  798. pValueName = pSetupStringTableStringFromId(OcManager->ComponentStringTable,ComponentId);
  799. }
  800. if (!pValueName) {
  801. return ERROR_FILE_NOT_FOUND;
  802. }
  803. //
  804. // Attempt to open the key if successful delete it
  805. //
  806. l = RegOpenKeyEx(
  807. HKEY_LOCAL_MACHINE,
  808. szOcManagerErrors,
  809. 0,
  810. KEY_QUERY_VALUE | KEY_SET_VALUE,
  811. &hKey
  812. );
  813. //
  814. // If an errror Key does not exist
  815. //
  816. if(l != NO_ERROR) {
  817. d = l;
  818. goto c1;
  819. }
  820. //
  821. // Delete this Subcomponent Key
  822. //
  823. l = RegDeleteValue( hKey, pValueName);
  824. d = l;
  825. RegCloseKey(hKey);
  826. c1:
  827. SetLastError(d);
  828. return(d == NO_ERROR);
  829. }
  830. BOOL
  831. _OcHelperReportExternalError(
  832. IN PVOID OcManagerContext,
  833. IN LPCTSTR ComponentId,
  834. IN LPCTSTR SubcomponentId, OPTIONAL
  835. IN DWORD_PTR MessageId,
  836. IN DWORD Flags,
  837. IN va_list *arglist,
  838. IN BOOL NativeCharWidth
  839. )
  840. {
  841. POC_MANAGER OcManager = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  842. HMODULE Module;
  843. OPTIONAL_COMPONENT Oc;
  844. LONG l;
  845. DWORD d;
  846. DWORD flags;
  847. LPTSTR MessageBuffer=NULL;
  848. LPCTSTR KeyValue;
  849. TCHAR fallback[20];
  850. HKEY hKey;
  851. DWORD Size;
  852. TCHAR *p;
  853. BOOL fmErr = FALSE;
  854. if ( ComponentId == NULL ) {
  855. //
  856. // if Component ID is null use the Suite Inf name
  857. //
  858. KeyValue = OcManager->SuiteName;
  859. //
  860. // if the message isn't preformatted, we have to retreive an error
  861. // from a component dll. But if a component id wasn't specified, then
  862. // we cannot retrieve from a comopnent dll. In that case, assume
  863. // that the message is to be retreived from the main OCM dll.
  864. //
  865. ZeroMemory( &Oc, sizeof(OPTIONAL_COMPONENT));
  866. if ((Flags & ERRFLG_PREFORMATTED) == 0) {
  867. Flags |= ERRFLG_OCM_MESSAGE;
  868. }
  869. } else {
  870. //
  871. // If SubcomponentId was Optional use the ComponentId
  872. //
  873. if ( SubcomponentId ) {
  874. KeyValue = SubcomponentId;
  875. } else {
  876. KeyValue = ComponentId;
  877. }
  878. // Look up the string in the master component table.
  879. // If it's not there, bail now.
  880. //
  881. l = pSetupStringTableLookUpStringEx(
  882. OcManager->ComponentStringTable,
  883. (PTSTR)ComponentId,
  884. STRTAB_CASE_INSENSITIVE,
  885. &Oc,
  886. sizeof(OPTIONAL_COMPONENT)
  887. );
  888. if(l == -1) {
  889. d = ERROR_INVALID_DATA;
  890. goto c0;
  891. }
  892. }
  893. //
  894. // Determine flags for FormatMessage
  895. //
  896. flags = FORMAT_MESSAGE_ALLOCATE_BUFFER;
  897. if(Flags & ERRFLG_SYSTEM_MESSAGE) {
  898. flags |= FORMAT_MESSAGE_FROM_SYSTEM;
  899. } else {
  900. flags |= FORMAT_MESSAGE_FROM_HMODULE;
  901. }
  902. if(Flags & ERRFLG_IGNORE_INSERTS) {
  903. flags |= FORMAT_MESSAGE_IGNORE_INSERTS;
  904. }
  905. if(Flags & ERRFLG_OCM_MESSAGE) {
  906. flags |= FORMAT_MESSAGE_FROM_HMODULE;
  907. }
  908. //
  909. // Format the message.
  910. //
  911. #ifdef UNICODE
  912. if(!NativeCharWidth) {
  913. if (Flags & ERRFLG_PREFORMATTED ) {
  914. MessageBuffer = (LPTSTR) MessageId;
  915. } else {
  916. try {
  917. d = FormatMessageA(
  918. flags,
  919. Flags & ERRFLG_OCM_MESSAGE?MyModuleHandle:Oc.InstallationDll, // ignored if system message
  920. (DWORD)MessageId,
  921. 0,
  922. (LPSTR)&MessageBuffer,
  923. 0,
  924. arglist
  925. );
  926. } except(EXCEPTION_EXECUTE_HANDLER) {
  927. fmErr = TRUE;
  928. }
  929. if (fmErr) {
  930. d = ERROR_INVALID_DATA;
  931. goto c0;
  932. }
  933. if(d) {
  934. //
  935. // Need to convert resulting message from ansi to unicode
  936. // so we can deal with it below. The LocalAlloc below overallocates
  937. // if some of the ansi chars are double-byte chars, too bad.
  938. //
  939. l = lstrlen(MessageBuffer)+1;
  940. if(p = (PVOID)LocalAlloc(LMEM_FIXED,l*sizeof(WCHAR))) {
  941. d = MultiByteToWideChar(CP_ACP,MB_PRECOMPOSED,(LPSTR)MessageBuffer,-1,p,l);
  942. if ( ! (Flags & ERRFLG_PREFORMATTED) ) {
  943. LocalFree((HLOCAL)MessageBuffer);
  944. }
  945. if(d) {
  946. MessageBuffer = p;
  947. } else {
  948. LocalFree((HLOCAL)p);
  949. }
  950. } else {
  951. if ( ! (Flags & ERRFLG_PREFORMATTED) ) {
  952. LocalFree((HLOCAL)MessageBuffer);
  953. }
  954. d = 0;
  955. }
  956. }
  957. }
  958. } else
  959. #endif
  960. {
  961. if (Flags & ERRFLG_PREFORMATTED ) {
  962. MessageBuffer = (LPTSTR) MessageId;
  963. d = 1;
  964. } else {
  965. try {
  966. d = FormatMessage(
  967. flags,
  968. Flags & ERRFLG_OCM_MESSAGE?MyModuleHandle:Oc.InstallationDll, // ignored if system message
  969. (DWORD)MessageId,
  970. 0,
  971. (LPTSTR)&MessageBuffer,
  972. 0,
  973. arglist
  974. );
  975. } except(EXCEPTION_EXECUTE_HANDLER) {
  976. fmErr = TRUE;
  977. }
  978. if (fmErr) {
  979. d = ERROR_INVALID_DATA;
  980. goto c0;
  981. }
  982. }
  983. if(!d) {
  984. //
  985. // Put *something* in there
  986. //
  987. wsprintf(
  988. fallback,
  989. TEXT("#%s0x%x"),
  990. (Flags & ERRFLG_SYSTEM_MESSAGE) ? TEXT("SYS") : TEXT(""),
  991. MessageId
  992. );
  993. MessageBuffer = fallback;
  994. }
  995. }
  996. l = RegCreateKeyEx(
  997. HKEY_LOCAL_MACHINE,
  998. szOcManagerErrors,
  999. 0,
  1000. NULL,
  1001. REG_OPTION_NON_VOLATILE,
  1002. KEY_QUERY_VALUE | KEY_SET_VALUE,
  1003. NULL,
  1004. &hKey,
  1005. &d
  1006. );
  1007. if(l != NO_ERROR) {
  1008. d = l;
  1009. goto c1;
  1010. }
  1011. //
  1012. // Figure out how large of a buffer we need to encompass
  1013. // the existing key and the string we're going to add to the end.
  1014. //
  1015. l = RegQueryValueEx(hKey,KeyValue,NULL,NULL,NULL,&Size);
  1016. if(l == NO_ERROR) {
  1017. if(Size == 0) {
  1018. Size = 1; // terminating nul
  1019. }
  1020. } else {
  1021. Size = sizeof(TCHAR); // terminating nul
  1022. }
  1023. Size += ((lstrlen(MessageBuffer) + 1) * sizeof(TCHAR));
  1024. //
  1025. // Allocate a buffer, read in the existing entry, and add the string
  1026. // to the end.
  1027. //
  1028. p = (PVOID)LocalAlloc(LMEM_FIXED,Size);
  1029. if(!p) {
  1030. d = ERROR_NOT_ENOUGH_MEMORY;
  1031. goto c2;
  1032. }
  1033. l = RegQueryValueEx(hKey,KeyValue,NULL,NULL,(BYTE *)p,&Size);
  1034. if(l == NO_ERROR) {
  1035. Size /= sizeof(TCHAR);
  1036. if(Size == 0) {
  1037. Size = 1;
  1038. }
  1039. } else {
  1040. Size = 1;
  1041. }
  1042. lstrcpy(p+(Size-1),MessageBuffer);
  1043. Size += lstrlen(MessageBuffer);
  1044. p[Size++] = 0;
  1045. d = RegSetValueEx(hKey,KeyValue,0,REG_MULTI_SZ,(CONST BYTE *)p,Size*sizeof(TCHAR));
  1046. LocalFree((HLOCAL)p);
  1047. c2:
  1048. RegCloseKey(hKey);
  1049. c1:
  1050. if(MessageBuffer && MessageBuffer != fallback && MessageBuffer != (LPTSTR)MessageId ) {
  1051. LocalFree((HLOCAL)MessageBuffer);
  1052. d = 0;
  1053. }
  1054. c0:
  1055. SetLastError(d);
  1056. return(d == NO_ERROR);
  1057. }
  1058. BOOL
  1059. pOcHelperReportExternalError(
  1060. IN POC_MANAGER OcManager,
  1061. IN LONG ComponentId,
  1062. IN LONG SubcomponentId, OPTIONAL
  1063. IN DWORD_PTR MessageId,
  1064. IN DWORD Flags,
  1065. ...
  1066. )
  1067. {
  1068. BOOL b;
  1069. DWORD d;
  1070. va_list arglist;
  1071. HELPER_CONTEXT OcManagerContext;
  1072. OcManagerContext.OcManager = OcManager;
  1073. va_start(arglist,Flags);
  1074. b = _OcHelperReportExternalError(
  1075. &OcManagerContext,
  1076. ComponentId ? pSetupStringTableStringFromId(OcManager->ComponentStringTable,ComponentId):NULL,
  1077. SubcomponentId ? pSetupStringTableStringFromId(OcManager->ComponentStringTable,SubcomponentId):NULL,
  1078. MessageId,
  1079. Flags,
  1080. &arglist,
  1081. TRUE
  1082. );
  1083. d = GetLastError();
  1084. va_end(arglist);
  1085. SetLastError(d);
  1086. return(b);
  1087. }
  1088. BOOL
  1089. #ifdef UNICODE
  1090. OcHelperReportExternalErrorW(
  1091. #else
  1092. OcHelperReportExternalErrorA(
  1093. #endif
  1094. IN PVOID OcManagerContext,
  1095. IN LPCTSTR ComponentId,
  1096. IN LPCTSTR SubcomponentId, OPTIONAL
  1097. IN DWORD_PTR MessageId,
  1098. IN DWORD Flags,
  1099. ...
  1100. )
  1101. {
  1102. BOOL b;
  1103. DWORD d;
  1104. va_list arglist;
  1105. va_start(arglist,Flags);
  1106. b = _OcHelperReportExternalError(
  1107. OcManagerContext,
  1108. ComponentId,
  1109. SubcomponentId,
  1110. MessageId,
  1111. Flags,
  1112. &arglist,
  1113. TRUE
  1114. );
  1115. d = GetLastError();
  1116. va_end(arglist);
  1117. SetLastError(d);
  1118. return(b);
  1119. }
  1120. #ifdef UNICODE
  1121. BOOL
  1122. OcHelperReportExternalErrorA(
  1123. IN PVOID OcManagerContext,
  1124. IN LPCSTR ComponentId,
  1125. IN LPCSTR SubcomponentId, OPTIONAL
  1126. IN DWORD_PTR MessageId,
  1127. IN DWORD Flags,
  1128. ...
  1129. )
  1130. {
  1131. LPCWSTR componentId,subcomponentId;
  1132. DWORD d;
  1133. BOOL b;
  1134. va_list arglist;
  1135. if (ComponentId) {
  1136. componentId = pSetupAnsiToUnicode(ComponentId);
  1137. if(!componentId) {
  1138. d = ERROR_NOT_ENOUGH_MEMORY;
  1139. b = FALSE;
  1140. goto e0;
  1141. }
  1142. } else {
  1143. componentId = NULL;
  1144. }
  1145. if(SubcomponentId) {
  1146. subcomponentId = pSetupAnsiToUnicode(SubcomponentId);
  1147. if(!subcomponentId) {
  1148. d = ERROR_NOT_ENOUGH_MEMORY;
  1149. b = FALSE;
  1150. goto e1;
  1151. }
  1152. } else {
  1153. subcomponentId = NULL;
  1154. }
  1155. va_start(arglist,Flags);
  1156. b = _OcHelperReportExternalError(
  1157. OcManagerContext,
  1158. componentId,
  1159. subcomponentId,
  1160. MessageId,
  1161. Flags,
  1162. &arglist,
  1163. FALSE
  1164. );
  1165. d = GetLastError();
  1166. va_end(arglist);
  1167. if(subcomponentId) {
  1168. pSetupFree(subcomponentId);
  1169. }
  1170. e1:
  1171. if (componentId) {
  1172. pSetupFree(componentId);
  1173. }
  1174. e0:
  1175. SetLastError(d);
  1176. return(b);
  1177. }
  1178. #endif
  1179. #if 0
  1180. BOOL
  1181. #ifdef UNICODE
  1182. OcHelperSetSelectionStateW(
  1183. #else
  1184. OcHelperSetSelectionStateA(
  1185. #endif
  1186. IN PVOID OcManagerContext,
  1187. IN LPCTSTR SubcomponentId,
  1188. IN UINT WhichState,
  1189. IN UINT NewState
  1190. )
  1191. /*++
  1192. Routine Description:
  1193. Function that can be used at any time.
  1194. It is used determine the selection status of a particular subcomponent.
  1195. Arguments:
  1196. OcManagerContext - supplies OC Manager context information the component gets
  1197. from the OcManagerContext field of the OCMANAGER_ROUTINES structure.
  1198. SubcomponentId - Supplies a subidentifier meaningful to the component
  1199. being called. The OC Manager imposes no semantics on this subidentifier.
  1200. StateType - supplies a constant indicating which state is to be returned
  1201. (original or current).
  1202. Return Value:
  1203. Boolean value indicating whether the subcomponent is selected
  1204. for installation. If FALSE, GetLastError() will return something other than
  1205. NO_ERROR if an error occurred, such as SubcomponentId being invalid.
  1206. --*/
  1207. {
  1208. POC_MANAGER p = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  1209. OPTIONAL_COMPONENT Oc;
  1210. LONG l;
  1211. BOOL b;
  1212. UINT *state;
  1213. l = pSetupStringTableLookUpStringEx(
  1214. p->ComponentStringTable,
  1215. (LPTSTR)SubcomponentId,
  1216. STRTAB_CASE_INSENSITIVE,
  1217. &Oc,
  1218. sizeof(OPTIONAL_COMPONENT)
  1219. );
  1220. if(l == -1) {
  1221. SetLastError(ERROR_INVALID_NAME);
  1222. return(FALSE);
  1223. }
  1224. switch (WhichState) {
  1225. case OCSELSTATETYPE_ORIGINAL:
  1226. state = &Oc.
  1227. break;
  1228. case OCSELSTATETYPE_CURRENT:
  1229. state = &Oc.
  1230. break;
  1231. case OCSELSTATETYPE_FINAL:
  1232. state = &Oc.
  1233. break;
  1234. default:
  1235. SetLastError(ERROR_INVALID_PARAMETER);
  1236. return FALSE;
  1237. }
  1238. if (NewState != SubcompOn && NewState != SubcompOff) {
  1239. SetLastError(ERROR_INVALID_PARAMETER);
  1240. return FALSE;
  1241. }
  1242. *state = NewState;
  1243. pOcSetStatesStringWorker(l, NewState, OcPage);
  1244. return(b);
  1245. }
  1246. #endif
  1247. BOOL
  1248. #ifdef UNICODE
  1249. OcLogErrorW(
  1250. #else
  1251. OcLogErrorA(
  1252. #endif
  1253. IN PVOID OcManagerContext,
  1254. IN DWORD ErrorLevel,
  1255. IN LPCTSTR Msg,
  1256. ...
  1257. )
  1258. {
  1259. TCHAR sz[5000];
  1260. va_list arglist;
  1261. POC_MANAGER p = ((PHELPER_CONTEXT)OcManagerContext)->OcManager;
  1262. va_start(arglist, Msg);
  1263. _vstprintf(sz, Msg, arglist);
  1264. va_end(arglist);
  1265. return p->Callbacks.LogError(ErrorLevel, sz);
  1266. }
  1267. #ifdef UNICODE
  1268. BOOL
  1269. OcLogErrorA(
  1270. IN PVOID OcManagerContext,
  1271. IN DWORD ErrorLevel,
  1272. IN LPCSTR Msg,
  1273. ...
  1274. )
  1275. {
  1276. PWSTR p;
  1277. BOOL b;
  1278. char sz[5000];
  1279. va_list arglist;
  1280. va_start(arglist, Msg);
  1281. vsprintf(sz, Msg, arglist);
  1282. va_end(arglist);
  1283. p = pSetupAnsiToUnicode(sz);
  1284. if (p) {
  1285. b = OcLogErrorW(OcManagerContext, ErrorLevel, p);
  1286. pSetupFree(p);
  1287. } else {
  1288. b = FALSE;
  1289. }
  1290. return(b);
  1291. }
  1292. #endif
  1293. //
  1294. // Now that we've got all the routines defined, we can build a table
  1295. // of routines.
  1296. //
  1297. OCMANAGER_ROUTINESA HelperRoutinesA = { NULL, // Context, filled in later.
  1298. OcHelperTickGauge,
  1299. OcHelperSetProgressTextA,
  1300. OcHelperSetPrivateDataA,
  1301. OcHelperGetPrivateDataA,
  1302. OcHelperSetSetupMode,
  1303. OcHelperGetSetupMode,
  1304. OcHelperQuerySelectionStateA,
  1305. OcHelperCallPrivateFunctionA,
  1306. OcHelperConfirmCancel,
  1307. OcHelperQueryWizardDialogHandle,
  1308. OcHelperSetReboot,
  1309. OcHelperGetInfHandle,
  1310. OcHelperReportExternalErrorA,
  1311. OcHelperShowHideWizardPage
  1312. };
  1313. #ifdef UNICODE
  1314. OCMANAGER_ROUTINESW HelperRoutinesW = { NULL, // Context, filled in later.
  1315. OcHelperTickGauge,
  1316. OcHelperSetProgressTextW,
  1317. OcHelperSetPrivateDataW,
  1318. OcHelperGetPrivateDataW,
  1319. OcHelperSetSetupMode,
  1320. OcHelperGetSetupMode,
  1321. OcHelperQuerySelectionStateW,
  1322. OcHelperCallPrivateFunctionW,
  1323. OcHelperConfirmCancel,
  1324. OcHelperQueryWizardDialogHandle,
  1325. OcHelperSetReboot,
  1326. OcHelperGetInfHandle,
  1327. OcHelperReportExternalErrorW,
  1328. OcHelperShowHideWizardPage
  1329. };
  1330. #endif
  1331. EXTRA_ROUTINESA ExtraRoutinesA = { sizeof(EXTRA_ROUTINESA),
  1332. OcLogErrorA
  1333. };
  1334. #ifdef UNICODE
  1335. EXTRA_ROUTINESW ExtraRoutinesW = { sizeof(EXTRA_ROUTINESW),
  1336. OcLogErrorW
  1337. };
  1338. #endif