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.

1070 lines
23 KiB

  1. #include "sacsvr.h"
  2. #include <TChar.h>
  3. //
  4. // Handle to the SAC Driver object
  5. //
  6. // The SAC driver requires us to use the same driver handle
  7. // that we registered with, to unregister.
  8. // Hence, we must keep this handle after we register ourselves
  9. // with the SAC driver so that we can unregister.
  10. //
  11. HANDLE m_SacDriverHandle = INVALID_HANDLE_VALUE;
  12. //
  13. // This event is fired when the SAC driver wants us
  14. // to launch a Command Prompt session
  15. //
  16. HANDLE m_RequestSacCmdEvent = NULL;
  17. //
  18. // In response to our attempt at launching a Command Prompt session,
  19. // we signal the appropriate status event
  20. //
  21. HANDLE m_RequestSacCmdSuccessEvent = NULL;
  22. HANDLE m_RequestSacCmdFailureEvent = NULL;
  23. //
  24. // the Command Prompt session exe
  25. //
  26. #define SAC_CMD_SCRAPER_PATH TEXT("sacsess.exe")
  27. #define SETREGISTRYDW( constVal, keyHandle1, keyHandle2, keyName, val, size ) \
  28. val = constVal ; \
  29. if( RegSetValueEx( keyHandle2, keyName, 0, REG_DWORD, (LPBYTE)&val, size \
  30. ) != ERROR_SUCCESS ) \
  31. { \
  32. if( keyHandle1 ) { \
  33. RegCloseKey( keyHandle1 ); \
  34. } \
  35. RegCloseKey( keyHandle2 ); \
  36. return ( FALSE ); \
  37. }
  38. #define REG_CONSOLE_KEY L".DEFAULT\\Console"
  39. //Add other FAREAST languages
  40. #define JAP_CODEPAGE 932
  41. #define CHS_CODEPAGE 936
  42. #define KOR_CODEPAGE 949
  43. #define CHT_CODEPAGE 950
  44. #define JAP_FONTSIZE 786432
  45. #define CHT_FONTSIZE 917504
  46. #define KOR_FONTSIZE 917504
  47. #define CHS_FONTSIZE 917504
  48. BOOL
  49. CreateClient(
  50. DWORD* pdwPid
  51. );
  52. BOOL
  53. CreateSessionProcess(
  54. DWORD* dwProcessId,
  55. HANDLE* hProcess
  56. );
  57. BOOL
  58. SetServiceStartType(
  59. IN PWSTR RegKey,
  60. IN DWORD StartType
  61. )
  62. /*++
  63. Routine Description:
  64. Arguments:
  65. None.
  66. Return Value:
  67. true - success
  68. false, otherwise
  69. --*/
  70. {
  71. DWORD rc;
  72. HKEY hKey;
  73. //
  74. // Open the service configuration key
  75. //
  76. rc = RegOpenKeyEx( HKEY_LOCAL_MACHINE,
  77. RegKey,
  78. 0,
  79. KEY_WRITE,
  80. &hKey );
  81. if( rc == NO_ERROR ) {
  82. rc = RegSetValueEx(
  83. hKey,
  84. TEXT("Start"),
  85. 0,
  86. REG_DWORD,
  87. (LPBYTE)&StartType,
  88. sizeof(DWORD)
  89. );
  90. RegCloseKey( hKey );
  91. }
  92. //
  93. // Success
  94. //
  95. return rc == NO_ERROR ? TRUE : FALSE;
  96. }
  97. BOOL
  98. InitSacCmd(
  99. VOID
  100. )
  101. /*++
  102. Routine Description:
  103. This routine initializes the relationship between the SACDRV and this service.
  104. We register an event with the SACDRV so that when a 'cmd' command is executed
  105. in the EMS, the event is fired and we launch a sac cmd session.
  106. Arguments:
  107. none
  108. Return Value:
  109. TRUE - if SacCmd was initialized successfully
  110. otherwise, FALSE
  111. --*/
  112. {
  113. BOOL bStatus;
  114. //
  115. // Initialize the our SAC Cmd Info
  116. //
  117. do {
  118. //
  119. // These events use the auto-reset mechanism since they are used as syncronization events
  120. //
  121. m_RequestSacCmdEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  122. if (m_RequestSacCmdEvent == NULL) {
  123. bStatus = FALSE;
  124. break;
  125. }
  126. m_RequestSacCmdSuccessEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  127. if (m_RequestSacCmdSuccessEvent == NULL) {
  128. bStatus = FALSE;
  129. break;
  130. }
  131. m_RequestSacCmdFailureEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  132. if (m_RequestSacCmdFailureEvent == NULL) {
  133. bStatus = FALSE;
  134. break;
  135. }
  136. //
  137. // Reset the service start type to Manual. By doing this,
  138. // we enable the scenario where the system boots with headless
  139. // enabled and then the user disables headless. In this scenario,
  140. // the service will not auto start next boot. This works because
  141. // the SAC driver moves the service start type from manual to auto,
  142. // if and only if the start type is manual.
  143. //
  144. // Note: we do this before we register with the SAC so we are sure
  145. // it happens
  146. //
  147. bStatus = SetServiceStartType(
  148. L"System\\CurrentControlSet\\Services\\sacsvr",
  149. SERVICE_DEMAND_START
  150. );
  151. if (! bStatus) {
  152. SvcDebugOut("Failed to set service start type\n", bStatus);
  153. break;
  154. } else {
  155. SvcDebugOut("Succeded to set service start type\n", bStatus);
  156. }
  157. //
  158. // Send the SAC Driver the event handles and the pointer to our
  159. // communication buffer
  160. //
  161. bStatus = SacRegisterCmdEvent(
  162. &m_SacDriverHandle,
  163. m_RequestSacCmdEvent,
  164. m_RequestSacCmdSuccessEvent,
  165. m_RequestSacCmdFailureEvent
  166. );
  167. if (! bStatus) {
  168. SvcDebugOut("Failed registration\n", bStatus);
  169. } else {
  170. SvcDebugOut("Succeeded registration\n", bStatus);
  171. }
  172. } while ( FALSE );
  173. //
  174. // clean up, if necessary
  175. //
  176. if (!bStatus) {
  177. if (m_RequestSacCmdEvent != NULL) {
  178. CloseHandle(m_RequestSacCmdEvent);
  179. m_RequestSacCmdEvent = NULL;
  180. }
  181. if (m_RequestSacCmdSuccessEvent != NULL) {
  182. CloseHandle(m_RequestSacCmdSuccessEvent);
  183. m_RequestSacCmdSuccessEvent = NULL;
  184. }
  185. if (m_RequestSacCmdFailureEvent != NULL) {
  186. CloseHandle(m_RequestSacCmdFailureEvent);
  187. m_RequestSacCmdFailureEvent = NULL;
  188. }
  189. }
  190. return bStatus;
  191. }
  192. BOOL
  193. ShutdownSacCmd(
  194. void
  195. )
  196. /*++
  197. Routine Description:
  198. This routine removes the relationship between the SACDRV and this service.
  199. Arguments:
  200. none
  201. Return Value:
  202. TRUE - if SacCmd was initialized successfully
  203. otherwise, FALSE
  204. --*/
  205. {
  206. BOOL Status;
  207. //
  208. // default status
  209. //
  210. Status = TRUE;
  211. //
  212. // Send the SAC Driver notification to remove the event handles
  213. // and the pointer to our communication buffer
  214. //
  215. if (! SacUnRegisterCmdEvent(&m_SacDriverHandle)) {
  216. Status = FALSE;
  217. }
  218. return Status;
  219. }
  220. VOID
  221. CompleteSacRequest(
  222. BOOLEAN Status
  223. )
  224. /*++
  225. Routine Description:
  226. This routine notifies the SAC driver about the status
  227. of the attempt to launc the SAC session.
  228. Arguments:
  229. Status - TRUE if the session was successfully launched,
  230. FALSE otherwise
  231. Return Value:
  232. None
  233. --*/
  234. {
  235. //
  236. // Fire the event corresponding to the request completion status
  237. //
  238. if (Status == TRUE) {
  239. SetEvent(m_RequestSacCmdSuccessEvent);
  240. } else {
  241. SetEvent(m_RequestSacCmdFailureEvent);
  242. }
  243. }
  244. BOOL
  245. ListenerThread(
  246. VOID
  247. )
  248. /*++
  249. Routine Description:
  250. This routine waits around for a "lauch a SAC session"
  251. event message from the SAC driver.
  252. Arguments:
  253. None
  254. Return Value:
  255. Status
  256. --*/
  257. {
  258. HANDLE eventArray[ 1 ];
  259. DWORD dwWaitRet = 0;
  260. DWORD dwPid = 0;
  261. BOOL bContinue;
  262. BOOL bStatus;
  263. //
  264. // setup the event array
  265. //
  266. enum {
  267. SAC_CMD_LAUNCH_EVENT = WAIT_OBJECT_0
  268. };
  269. eventArray[ 0 ] = m_RequestSacCmdEvent;
  270. //
  271. // While we want to continue, service events
  272. //
  273. bStatus = TRUE;
  274. bContinue = TRUE;
  275. while ( bContinue ) {
  276. dwWaitRet = WaitForMultipleObjects (
  277. sizeof(eventArray)/sizeof(HANDLE),
  278. eventArray,
  279. FALSE,
  280. INFINITE
  281. );
  282. switch (dwWaitRet) {
  283. case SAC_CMD_LAUNCH_EVENT:
  284. //
  285. // Attempt to launch the command console process
  286. //
  287. if ( !CreateClient( &dwPid ) ) {
  288. //
  289. // Notify the SAC driver that we failed to
  290. // launch the SAC session
  291. //
  292. CompleteSacRequest( FALSE );
  293. bStatus = FALSE;
  294. break;
  295. }
  296. //
  297. // Notify the SAC driver that we successfully
  298. // launched the SAC session
  299. //
  300. CompleteSacRequest(TRUE);
  301. break;
  302. default:
  303. bContinue = FALSE;
  304. bStatus = FALSE;
  305. break;
  306. }
  307. }
  308. return( bStatus );
  309. }
  310. BOOL
  311. CreateClient(
  312. OUT DWORD *pdwPid
  313. )
  314. /*++
  315. Routine Description:
  316. This routine launches the SAC session
  317. Arguments:
  318. pdwPid - the PID of the newly created SAC session process
  319. Return Value:
  320. Status
  321. --*/
  322. {
  323. BOOL bRetVal;
  324. BOOL bSuccess;
  325. DWORD dwProcessId;
  326. HANDLE hProcess;
  327. DWORD dwExitCode;
  328. //
  329. // default: we failed to create the process
  330. //
  331. bRetVal = FALSE;
  332. hProcess = NULL;
  333. do {
  334. //
  335. // Create the Command Console session process
  336. //
  337. bSuccess = CreateSessionProcess(
  338. &dwProcessId,
  339. &hProcess
  340. );
  341. if ( !bSuccess ) {
  342. break;
  343. }
  344. if ( hProcess == NULL ) {
  345. break;
  346. }
  347. //
  348. // Send back PID to caller
  349. //
  350. *pdwPid = dwProcessId;
  351. //
  352. // Check if the process has really started. It may not have started properly
  353. // in the following cases and yet the createprocess return code
  354. // will not say it
  355. //
  356. // 1. Could not launch process on the desktop because of lack of perms or
  357. // heap memory. Doing GetExitCodeProcess immediate may not help always.
  358. //
  359. GetExitCodeProcess( hProcess, &dwExitCode );
  360. //
  361. // Make sure the process is still active before we declare victory
  362. //
  363. if (dwExitCode != STILL_ACTIVE ) {
  364. break;
  365. }
  366. //
  367. // We successfully created the process
  368. //
  369. bRetVal = TRUE;
  370. } while ( FALSE );
  371. //
  372. // We are done with the process handle
  373. //
  374. if (hProcess) {
  375. CloseHandle( hProcess );
  376. }
  377. return(bRetVal);
  378. }
  379. PTCHAR
  380. GetPathOfTheExecutable(
  381. VOID
  382. )
  383. /*++
  384. Routine Description:
  385. Find out where the SAC session executable is located.
  386. Arguments:
  387. NONE
  388. Return Value:
  389. Failure: NULL
  390. SUCCESS: pointer to path (caller must free)
  391. --*/
  392. {
  393. TCHAR SystemDir[MAX_PATH+1];
  394. PTCHAR pBuffer;
  395. ULONG length;
  396. //
  397. // default: we didnt create a new path
  398. //
  399. pBuffer = NULL;
  400. do {
  401. //
  402. // get the system path
  403. //
  404. length = GetSystemDirectoryW(SystemDir, MAX_PATH+1);
  405. if (length == 0) {
  406. break;
  407. }
  408. //
  409. // compute the length
  410. //
  411. length += 1; // backslash
  412. length += lstrlen(SAC_CMD_SCRAPER_PATH);
  413. length += 1; // NULL termination
  414. //
  415. // allocate our new path
  416. //
  417. pBuffer = malloc(length * sizeof(WCHAR));
  418. if (pBuffer == NULL) {
  419. break;
  420. }
  421. //
  422. // create the path
  423. //
  424. wnsprintf(
  425. pBuffer,
  426. length,
  427. TEXT("%s\\%s"),
  428. SystemDir,
  429. SAC_CMD_SCRAPER_PATH
  430. );
  431. } while ( FALSE );
  432. return pBuffer;
  433. }
  434. void
  435. FillProcessStartupInfo(
  436. STARTUPINFO *si
  437. )
  438. /*++
  439. Routine Description:
  440. Populate the process startup info structure for the
  441. SAC session process.
  442. Arguments:
  443. si - the startup info
  444. Return Value:
  445. None
  446. --*/
  447. {
  448. ASSERT( si != NULL );
  449. ZeroMemory(si, sizeof(STARTUPINFO));
  450. si->cb = sizeof(STARTUPINFO);
  451. si->wShowWindow = SW_SHOW;
  452. return;
  453. }
  454. BOOL
  455. CreateSessionProcess(
  456. OUT DWORD *dwProcessId,
  457. OUT HANDLE *hProcess
  458. )
  459. /*++
  460. Routine Description:
  461. This routine does the real work to launch the SAC session process.
  462. Arguments:
  463. dwProcessId - the PID of the SAC session process
  464. Return Value:
  465. TRUE - the process was created successfully,
  466. FALSE - otherwise
  467. --*/
  468. {
  469. PROCESS_INFORMATION pi;
  470. STARTUPINFO si;
  471. PTCHAR pCmdBuf;
  472. BOOL dwStatus;
  473. PWCHAR SessionPath = SAC_CMD_SCRAPER_PATH;
  474. do {
  475. //
  476. // get the pathname to the SAC session exe
  477. //
  478. pCmdBuf = GetPathOfTheExecutable();
  479. if (pCmdBuf == NULL) {
  480. dwStatus = FALSE;
  481. break;
  482. }
  483. //
  484. //
  485. //
  486. FillProcessStartupInfo( &si );
  487. //
  488. //
  489. //
  490. dwStatus = CreateProcess(
  491. pCmdBuf,
  492. SessionPath,
  493. NULL,
  494. NULL,
  495. TRUE,
  496. CREATE_NEW_PROCESS_GROUP | CREATE_NEW_CONSOLE,
  497. NULL,
  498. NULL,
  499. &si,
  500. &pi
  501. );
  502. //
  503. // release our SAC session path
  504. //
  505. free(pCmdBuf);
  506. if ( !dwStatus ) {
  507. break;
  508. }
  509. //
  510. //
  511. //
  512. *hProcess = pi.hProcess;
  513. CloseHandle( pi.hThread );
  514. *dwProcessId = pi.dwProcessId;
  515. } while ( FALSE );
  516. return( dwStatus );
  517. }
  518. BOOL
  519. FormSACSessKeyForCmd(
  520. LPWSTR *lpszKey
  521. )
  522. /*++
  523. Routine Description:
  524. This routine forms the reg key used to specify the console
  525. fonts for the sacsess.exe app.
  526. See comments for HandleJapSpecificRegKeys
  527. Mem allocation by this function.
  528. To be deleted by the caller.
  529. (based on telnet's FormTlntSessKeyForCmd)
  530. Arguments:
  531. lpszKey - on success, contains the key name
  532. Return Value:
  533. TRUE - We completed successfully
  534. FALSE - otherwise
  535. --*/
  536. {
  537. WCHAR szPathName[MAX_PATH+1];
  538. WCHAR session_path[MAX_PATH*2];
  539. LPTSTR pSlash;
  540. wint_t ch;
  541. LPTSTR pBackSlash;
  542. DWORD length_required;
  543. //
  544. //
  545. //
  546. if( !GetModuleFileName( NULL, szPathName, MAX_PATH+1 ) )
  547. {
  548. return ( FALSE );
  549. }
  550. szPathName[MAX_PATH] = UNICODE_NULL;
  551. //
  552. // Nuke the trailing "sacsvr.exe"
  553. //
  554. pSlash = wcsrchr( szPathName, L'\\' );
  555. if( pSlash == NULL )
  556. {
  557. return ( FALSE );
  558. }
  559. else
  560. {
  561. *pSlash = L'\0';
  562. }
  563. //
  564. // Replace all '\\' with '_' This format is required for the console to
  565. // interpret the key.
  566. //
  567. ch = L'\\';
  568. pBackSlash = NULL;
  569. while ( 1 )
  570. {
  571. pBackSlash = wcschr( szPathName, ch );
  572. if( pBackSlash == NULL )
  573. {
  574. break;
  575. }
  576. else
  577. {
  578. *pBackSlash = L'_';
  579. }
  580. }
  581. //
  582. //
  583. //
  584. _snwprintf(session_path, MAX_PATH*2 - 1, L"%s_sacsess.exe", szPathName);
  585. session_path[MAX_PATH*2 - 1] = L'\0'; // snwprintf could return non-null terminated string, if the buffer size is an exact fit
  586. length_required = (DWORD)(wcslen( REG_CONSOLE_KEY ) + wcslen( session_path ) + 2);
  587. *lpszKey = malloc(length_required * sizeof(WCHAR));
  588. if( *lpszKey == NULL )
  589. {
  590. return( FALSE );
  591. }
  592. //
  593. //
  594. //
  595. _snwprintf(*lpszKey, length_required - 1, L"%s\\%s", REG_CONSOLE_KEY, session_path );
  596. (*lpszKey)[length_required - 1] = L'\0'; // snwprintf could return non-null terminated string, if the buffer size is an exact fit
  597. return ( TRUE );
  598. }
  599. BOOL
  600. HandleFarEastSpecificRegKeys(
  601. VOID
  602. )
  603. /*++
  604. Routine Description:
  605. If Japanese codepage, then we need to verify 3 registry settings for
  606. console fonts:
  607. HKEY_USERS\.DEFAULT\Console\FaceName :REG_SZ:lr SVbN
  608. where the FaceName is "MS gothic" written in Japanese full widthKana
  609. HKEY_USERS\.DEFAULT\Console\FontFamily:REG_DWORD:0x36
  610. HKEY_USERS\.DEFAULT\Console\C:_SFU_Telnet_sacsess.exe\FontFamily:REG_DWORD: 0x36
  611. where the "C:" part is the actual path to SFU installation
  612. (based on telnet's HandleFarEastSpecificRegKeys)
  613. Arguments:
  614. None
  615. Return Value:
  616. TRUE - We completed successfully
  617. FALSE - otherwise
  618. --*/
  619. {
  620. HKEY hk;
  621. DWORD dwFontSize = 0;
  622. const TCHAR szJAPFaceName[] = { 0xFF2D ,0xFF33 ,L' ' ,0x30B4 ,0x30B7 ,0x30C3 ,0x30AF ,L'\0' };
  623. const TCHAR szCHTFaceName[] = { 0x7D30 ,0x660E ,0x9AD4 ,L'\0'};
  624. const TCHAR szKORFaceName[] = { 0xAD74 ,0xB9BC ,0xCCB4 ,L'\0'};
  625. const TCHAR szCHSFaceName[] = { 0x65B0 ,0x5B8B ,0x4F53 ,L'\0' };
  626. TCHAR szFaceNameDef[256];
  627. DWORD dwCodePage = GetACP();
  628. DWORD dwFaceNameSize = 0;
  629. DWORD dwFontFamily = 54;
  630. DWORD dwFontWeight = 400;
  631. DWORD dwHistoryNoDup = 0;
  632. DWORD dwSize = 0;
  633. switch (dwCodePage)
  634. {
  635. case JAP_CODEPAGE:
  636. _tcscpy(szFaceNameDef, szJAPFaceName); //On JAP, set the FaceName to "MS Gothic"
  637. dwFontSize = JAP_FONTSIZE;
  638. break;
  639. case CHT_CODEPAGE:
  640. _tcscpy(szFaceNameDef, szCHTFaceName); //On CHT, set the FaceName to "MingLiU"
  641. dwFontSize = CHT_FONTSIZE;
  642. break;
  643. case KOR_CODEPAGE:
  644. _tcscpy(szFaceNameDef, szKORFaceName);//On KOR, set the FaceName to "GulimChe"
  645. dwFontSize = KOR_FONTSIZE;
  646. break;
  647. case CHS_CODEPAGE:
  648. _tcscpy(szFaceNameDef, szCHSFaceName);//On CHS, set the FaceName to "NSimSun"
  649. dwFontSize = CHS_FONTSIZE;
  650. break;
  651. default:
  652. _tcscpy(szFaceNameDef,L"\0");
  653. break;
  654. }
  655. dwFaceNameSize = (DWORD)(( _tcslen( szFaceNameDef ) + 1 ) * sizeof( TCHAR ));
  656. if( !RegOpenKeyEx( HKEY_USERS, REG_CONSOLE_KEY, 0, KEY_SET_VALUE, &hk ) )
  657. {
  658. DWORD dwVal;
  659. LPWSTR lpszKey;
  660. HKEY hk2;
  661. RegSetValueEx(
  662. hk,
  663. L"FaceName",
  664. 0,
  665. REG_SZ,
  666. (LPBYTE) szFaceNameDef,
  667. dwFaceNameSize
  668. );
  669. dwSize = sizeof( DWORD );
  670. SETREGISTRYDW( dwFontFamily, NULL, hk, L"FontFamily", dwVal,dwSize );
  671. lpszKey = NULL;
  672. if ( !FormSACSessKeyForCmd( &lpszKey ) ) {
  673. RegCloseKey( hk );
  674. return( FALSE );
  675. }
  676. hk2 = NULL;
  677. if ( RegCreateKey( HKEY_USERS, lpszKey, &hk2 ) ) {
  678. free(lpszKey);
  679. return( FALSE );
  680. }
  681. free(lpszKey);
  682. dwSize = sizeof( DWORD );
  683. SETREGISTRYDW( dwFontFamily, hk, hk2, L"FontFamily", dwVal, dwSize);
  684. SETREGISTRYDW( dwCodePage, hk, hk2, L"CodePage", dwVal, dwSize );
  685. SETREGISTRYDW( dwFontSize, hk, hk2, L"FontSize", dwVal, dwSize);
  686. SETREGISTRYDW( dwFontWeight, hk, hk2, L"FontWeight", dwVal, dwSize );
  687. SETREGISTRYDW( dwHistoryNoDup, hk, hk2, L"HistoryNoDup", dwVal, dwSize );
  688. RegSetValueEx(
  689. hk2,
  690. L"FaceName",
  691. 0,
  692. REG_SZ,
  693. (LPBYTE) szFaceNameDef,
  694. dwFaceNameSize
  695. );
  696. RegCloseKey( hk2 );
  697. RegCloseKey( hk );
  698. return( TRUE );
  699. }
  700. return ( FALSE );
  701. }
  702. BOOL
  703. InitializeGlobalObjects(
  704. VOID
  705. )
  706. /*++
  707. Routine Description:
  708. This routine performs init of the global settings
  709. needed by the service or will be needed by the session.
  710. Arguments:
  711. None
  712. Return Value:
  713. TRUE - We completed successfully
  714. FALSE - otherwise
  715. --*/
  716. {
  717. DWORD dwCodePage;
  718. BOOL bStatus;
  719. do {
  720. //
  721. // notify the SAC driver that we are ready to launch sessions
  722. //
  723. bStatus = InitSacCmd();
  724. if (! bStatus) {
  725. SvcDebugOut("RUN: Failed SAC init: %x\n", bStatus);
  726. break;
  727. }
  728. //
  729. // make sure we have the Console fonts set up properly for
  730. // far-east builds. We need to do this, or when we call
  731. // ReadConsoleOutput in sacsess, we will get back a malformed
  732. // screen frame buffer - it will not have properly constructed
  733. // double width jpn chars, for instance.
  734. //
  735. dwCodePage = GetACP();
  736. if ( dwCodePage == JAP_CODEPAGE ||
  737. dwCodePage == CHS_CODEPAGE ||
  738. dwCodePage == CHT_CODEPAGE ||
  739. dwCodePage == KOR_CODEPAGE ) {
  740. //
  741. // Fareast code page
  742. //
  743. bStatus = HandleFarEastSpecificRegKeys();
  744. if( !bStatus )
  745. {
  746. SvcDebugOut("RUN: Failed to handle FES init: %x\n", bStatus);
  747. break;
  748. }
  749. }
  750. } while ( FALSE );
  751. return bStatus;
  752. }
  753. BOOL
  754. Run(
  755. VOID
  756. )
  757. /*++
  758. Routine Description:
  759. This routine registers the service with the SAC driver
  760. and waits for messages from the SAC driver to launch SAC sessions.
  761. Arguments:
  762. None
  763. Return Value:
  764. TRUE - We completed successfully
  765. FALSE - otherwise
  766. --*/
  767. {
  768. BOOL Status;
  769. do {
  770. //
  771. //
  772. //
  773. Status = InitializeGlobalObjects();
  774. if (! Status) {
  775. SvcDebugOut("RUN: Failed init of global objects: %x\n", Status);
  776. break;
  777. }
  778. //
  779. //
  780. //
  781. Status = ListenerThread();
  782. if (! Status) {
  783. SvcDebugOut("RUN: Failed Listener: %x\n", Status);
  784. break;
  785. }
  786. } while (FALSE);
  787. return Status;
  788. }
  789. BOOL
  790. Stop(
  791. VOID
  792. )
  793. /*++
  794. Routine Description:
  795. Shutdown the service, which in this case implies
  796. that we unregister with the SAC driver so it knows
  797. we aren't listening anymore.
  798. Arguments:
  799. None
  800. Return Value:
  801. TRUE - We completed successfully
  802. FALSE - otherwise
  803. --*/
  804. {
  805. BOOL Status;
  806. Status = ShutdownSacCmd();
  807. return Status;
  808. }