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.

2050 lines
68 KiB

  1. /****************************************************************************/
  2. // tssdjet.cpp
  3. //
  4. // Terminal Server Session Directory Jet RPC component code.
  5. //
  6. // Copyright (C) 2000 Microsoft Corporation
  7. /****************************************************************************/
  8. #include <windows.h>
  9. #include <stdio.h>
  10. #include <process.h>
  11. #include <ole2.h>
  12. #include <objbase.h>
  13. #include <comdef.h>
  14. #include <winsta.h>
  15. #include <regapi.h>
  16. #include <winsock2.h>
  17. #include "tssdjet.h"
  18. #include "trace.h"
  19. #include "resource.h"
  20. #pragma warning (push, 4)
  21. /****************************************************************************/
  22. // Defines
  23. /****************************************************************************/
  24. #define REQUEST_UPDATE 1
  25. #define DONT_REQUEST_UPDATE 0
  26. /****************************************************************************/
  27. // Prototypes
  28. /****************************************************************************/
  29. INT_PTR CALLBACK CustomUIDlg(HWND, UINT, WPARAM, LPARAM);
  30. /****************************************************************************/
  31. // Globals
  32. /****************************************************************************/
  33. extern HINSTANCE g_hInstance;
  34. // The COM object counter (declared in server.cpp)
  35. extern long g_lObjects;
  36. // RPC binding string components - RPC over named pipes.
  37. const WCHAR *g_RPCUUID = L"aa177641-fc9b-41bd-80ff-f964a701596f";
  38. // From jetrpc.idl
  39. const WCHAR *g_RPCOptions = L"Security=Impersonation Dynamic False";
  40. const WCHAR *g_RPCProtocolSequence = L"ncacn_ip_tcp"; // RPC over TCP/IP
  41. const WCHAR *g_RPCRemoteEndpoint = L"\\pipe\\TSSD_Jet_RPC_Service";
  42. /****************************************************************************/
  43. // Static RPC Exception Filter structure and function, based on
  44. // I_RpcExceptionFilter in \nt\com\rpc\runtime\mtrt\clntapip.cxx.
  45. /****************************************************************************/
  46. // windows.h includes windef.h includes winnt.h, which defines some exceptions
  47. // but not others. ntstatus.h contains the two extra we want,
  48. // STATUS_POSSIBLE_DEADLOCK and STATUS_INSTRUCTION_MISALIGNMENT, but it would
  49. // be very difficult to get the right #includes in without a lot of trouble.
  50. #define STATUS_POSSIBLE_DEADLOCK 0xC0000194L
  51. #define STATUS_INSTRUCTION_MISALIGNMENT 0xC00000AAL
  52. const ULONG FatalExceptions[] =
  53. {
  54. STATUS_ACCESS_VIOLATION,
  55. STATUS_POSSIBLE_DEADLOCK,
  56. STATUS_INSTRUCTION_MISALIGNMENT,
  57. STATUS_DATATYPE_MISALIGNMENT,
  58. STATUS_PRIVILEGED_INSTRUCTION,
  59. STATUS_ILLEGAL_INSTRUCTION,
  60. STATUS_BREAKPOINT,
  61. STATUS_STACK_OVERFLOW
  62. };
  63. const int FATAL_EXCEPTIONS_ARRAY_SIZE = sizeof(FatalExceptions) /
  64. sizeof(FatalExceptions[0]);
  65. static int TSSDRpcExceptionFilter (unsigned long ExceptionCode)
  66. {
  67. int i;
  68. for (i = 0; i < FATAL_EXCEPTIONS_ARRAY_SIZE; i++) {
  69. if (ExceptionCode == FatalExceptions[i])
  70. return EXCEPTION_CONTINUE_SEARCH;
  71. }
  72. return EXCEPTION_EXECUTE_HANDLER;
  73. }
  74. /****************************************************************************/
  75. // MIDL_user_allocate
  76. // MIDL_user_free
  77. //
  78. // RPC-required allocation functions.
  79. /****************************************************************************/
  80. void __RPC_FAR * __RPC_USER MIDL_user_allocate(size_t Size)
  81. {
  82. return LocalAlloc(LMEM_FIXED, Size);
  83. }
  84. void __RPC_USER MIDL_user_free(void __RPC_FAR *p)
  85. {
  86. LocalFree(p);
  87. }
  88. /****************************************************************************/
  89. // CTSSessionDirectory::CTSSessionDirectory
  90. // CTSSessionDirectory::~CTSSessionDirectory
  91. //
  92. // Constructor and destructor
  93. /****************************************************************************/
  94. CTSSessionDirectory::CTSSessionDirectory() :
  95. m_RefCount(0), m_hRPCBinding(NULL), m_hSDServerDown(NULL),
  96. m_hTerminateRecovery(NULL), m_hRecoveryThread(NULL), m_RecoveryTid(0),
  97. m_LockInitializationSuccessful(FALSE), m_SDIsUp(FALSE), m_Flags(0)
  98. {
  99. InterlockedIncrement(&g_lObjects);
  100. m_StoreServerName[0] = L'\0';
  101. m_LocalServerAddress[0] = L'\0';
  102. m_ClusterName[0] = L'\0';
  103. m_fEnabled = 0;
  104. m_tchProvider[0] = 0;
  105. m_tchDataSource[0] = 0;
  106. m_tchUserId[0] = 0;
  107. m_tchPassword[0] = 0;
  108. m_sr.Valid = FALSE;
  109. // Recovery timeout should be configurable, but currently is not.
  110. // Time is in ms.
  111. m_RecoveryTimeout = 15000;
  112. if (InitializeSharedResource(&m_sr)) {
  113. m_LockInitializationSuccessful = TRUE;
  114. }
  115. else {
  116. ERR((TB, "Constructor: Failed to initialize shared resource"));
  117. }
  118. }
  119. CTSSessionDirectory::~CTSSessionDirectory()
  120. {
  121. // Clean up.
  122. if (m_LockInitializationSuccessful)
  123. Terminate();
  124. if (m_sr.Valid)
  125. FreeSharedResource(&m_sr);
  126. // Decrement the global COM object counter.
  127. InterlockedDecrement(&g_lObjects);
  128. }
  129. /****************************************************************************/
  130. // CTSSessionDirectory::QueryInterface
  131. //
  132. // Standard COM IUnknown function.
  133. /****************************************************************************/
  134. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::QueryInterface(
  135. REFIID riid,
  136. void **ppv)
  137. {
  138. if (riid == IID_IUnknown) {
  139. *ppv = (LPVOID)(IUnknown *)(ITSSessionDirectory *)this;
  140. }
  141. else if (riid == IID_ITSSessionDirectory) {
  142. *ppv = (LPVOID)(ITSSessionDirectory *)this;
  143. }
  144. else if (riid == IID_IExtendServerSettings) {
  145. *ppv = (LPVOID)(IExtendServerSettings *)this;
  146. }
  147. else if (riid == IID_ITSSessionDirectoryEx) {
  148. *ppv = (LPVOID)(ITSSessionDirectoryEx *)this;
  149. }
  150. else {
  151. ERR((TB,"QI: Unknown interface"));
  152. return E_NOINTERFACE;
  153. }
  154. ((IUnknown *)*ppv)->AddRef();
  155. return S_OK;
  156. }
  157. /****************************************************************************/
  158. // CTSSessionDirectory::AddRef
  159. //
  160. // Standard COM IUnknown function.
  161. /****************************************************************************/
  162. ULONG STDMETHODCALLTYPE CTSSessionDirectory::AddRef()
  163. {
  164. return InterlockedIncrement(&m_RefCount);
  165. }
  166. /****************************************************************************/
  167. // CTSSessionDirectory::Release
  168. //
  169. // Standard COM IUnknown function.
  170. /****************************************************************************/
  171. ULONG STDMETHODCALLTYPE CTSSessionDirectory::Release()
  172. {
  173. long lRef = InterlockedDecrement(&m_RefCount);
  174. if (lRef == 0)
  175. delete this;
  176. return lRef;
  177. }
  178. /****************************************************************************/
  179. // CTSSessionDirectory::Initialize
  180. //
  181. // ITSSessionDirectory function. Called soon after object instantiation to
  182. // initialize the directory. LocalServerAddress provides a text representation
  183. // of the local server's load balance IP address. This information should be
  184. // used as the server IP address in the session directory for client
  185. // redirection by other pool servers to this server. SessionDirectoryLocation,
  186. // SessionDirectoryClusterName, and SessionDirectoryAdditionalParams are
  187. // generic reg entries known to TermSrv which cover config info across any type
  188. // of session directory implementation. The contents of these strings are
  189. // designed to be parsed by the session directory providers.
  190. /****************************************************************************/
  191. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::Initialize(
  192. LPWSTR LocalServerAddress,
  193. LPWSTR StoreServerName,
  194. LPWSTR ClusterName,
  195. LPWSTR OpaqueSettings,
  196. DWORD Flags,
  197. DWORD (*repopfn)())
  198. {
  199. HRESULT hr;
  200. WCHAR *pBindingString = NULL;
  201. // Unreferenced parameter
  202. OpaqueSettings;
  203. if (m_LockInitializationSuccessful == FALSE) {
  204. hr = E_OUTOFMEMORY;
  205. goto ExitFunc;
  206. }
  207. ASSERT((LocalServerAddress != NULL),(TB,"Init: LocalServerAddr null!"));
  208. ASSERT((StoreServerName != NULL),(TB,"Init: StoreServerName null!"));
  209. ASSERT((ClusterName != NULL),(TB,"Init: ClusterName null!"));
  210. ASSERT((repopfn != NULL),(TB,"Init: repopfn null!"));
  211. // Don't allow blank session directory server name.
  212. if (StoreServerName[0] == '\0') {
  213. hr = E_INVALIDARG;
  214. goto ExitFunc;
  215. }
  216. // Copy off the server address, store server, and cluster name for later
  217. // use.
  218. wcsncpy(m_StoreServerName, StoreServerName,
  219. sizeof(m_StoreServerName) / sizeof(WCHAR) - 1);
  220. m_StoreServerName[sizeof(m_StoreServerName) / sizeof(WCHAR) - 1] = L'\0';
  221. wcsncpy(m_LocalServerAddress, LocalServerAddress,
  222. sizeof(m_LocalServerAddress) / sizeof(WCHAR) - 1);
  223. m_LocalServerAddress[sizeof(m_LocalServerAddress) / sizeof(WCHAR) - 1] =
  224. L'\0';
  225. wcsncpy(m_ClusterName, ClusterName,
  226. sizeof(m_ClusterName) / sizeof(WCHAR) - 1);
  227. m_ClusterName[sizeof(m_ClusterName) / sizeof(WCHAR) - 1] = L'\0';
  228. m_Flags = Flags;
  229. m_repopfn = repopfn;
  230. TRC1((TB,"Initialize: Svr addr=%S, StoreSvrName=%S, ClusterName=%S, "
  231. "OpaqueSettings=%S, repopfn = %p",
  232. m_LocalServerAddress, m_StoreServerName, m_ClusterName,
  233. OpaqueSettings, repopfn));
  234. // Connect to the Jet RPC server according to the server name provided.
  235. // We first create an RPC binding handle from a composed binding string.
  236. hr = RpcStringBindingCompose(/*(WCHAR *)g_RPCUUID,*/
  237. 0,
  238. (WCHAR *)g_RPCProtocolSequence, m_StoreServerName,
  239. /*(WCHAR *)g_RPCRemoteEndpoint, */
  240. 0,
  241. NULL, &pBindingString);
  242. if (hr == RPC_S_OK) {
  243. // Generate the RPC binding from the canonical RPC binding string.
  244. hr = RpcBindingFromStringBinding(pBindingString, &m_hRPCBinding);
  245. if (hr == RPC_S_OK) {
  246. hr = RpcBindingSetAuthInfo(m_hRPCBinding, 0,
  247. RPC_C_AUTHN_LEVEL_NONE, RPC_C_AUTHN_WINNT, 0, 0);
  248. if (hr != RPC_S_OK) {
  249. ERR((TB,"Init: Error %d in RpcBindingSetAuthInfo", hr));
  250. goto ExitFunc;
  251. }
  252. } else {
  253. ERR((TB,"Init: Error %d in RpcBindingFromStringBinding\n", hr));
  254. m_hRPCBinding = NULL;
  255. goto ExitFunc;
  256. }
  257. }
  258. else {
  259. ERR((TB,"Init: Error %d in RpcStringBindingCompose\n", hr));
  260. pBindingString = NULL;
  261. goto ExitFunc;
  262. }
  263. // Initialize recovery infrastructure
  264. // Initialize should not be called more than once.
  265. ASSERT((m_hSDServerDown == NULL),(TB, "Init: m_hSDServDown non-NULL!"));
  266. ASSERT((m_hRecoveryThread == NULL),(TB, "Init: m_hSDRecoveryThread "
  267. "non-NULL!"));
  268. ASSERT((m_hTerminateRecovery == NULL), (TB, "Init: m_hTerminateRecovery "
  269. "non-NULL!"));
  270. // Initially unsignaled
  271. m_hSDServerDown = CreateEvent(NULL, TRUE, FALSE, NULL);
  272. if (m_hSDServerDown == NULL) {
  273. ERR((TB, "Init: Failed to create event necessary for SD init, err = "
  274. "%d", GetLastError()));
  275. hr = E_FAIL;
  276. goto ExitFunc;
  277. }
  278. // Initially unsignaled, auto-reset.
  279. m_hTerminateRecovery = CreateEvent(NULL, FALSE, FALSE, NULL);
  280. if (m_hTerminateRecovery == NULL) {
  281. ERR((TB, "Init: Failed to create event necessary for SD init, err = "
  282. "%d", GetLastError()));
  283. hr = E_FAIL;
  284. goto ExitFunc;
  285. }
  286. m_hRecoveryThread = _beginthreadex(NULL, 0, RecoveryThread, (void *) this,
  287. 0, &m_RecoveryTid);
  288. if (m_hRecoveryThread == NULL) {
  289. ERR((TB, "Init: Failed to create recovery thread, errno = %d", errno));
  290. hr = E_FAIL;
  291. goto ExitFunc;
  292. }
  293. // Start up the session directory (by faking server down).
  294. StartupSD();
  295. ExitFunc:
  296. if (pBindingString != NULL)
  297. RpcStringFree(&pBindingString);
  298. return hr;
  299. }
  300. /****************************************************************************/
  301. // CTSSessionDirectory::Update
  302. //
  303. // ITSSessionDirectory function. Called whenever configuration settings change
  304. // on the terminal server. See Initialize for a description of the first four
  305. // arguments, the fifth, Result, is a flag of whether to request a refresh of
  306. // every session that should be in the session directory for this server after
  307. // this call completes.
  308. /****************************************************************************/
  309. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::Update(
  310. LPWSTR LocalServerAddress,
  311. LPWSTR StoreServerName,
  312. LPWSTR ClusterName,
  313. LPWSTR OpaqueSettings,
  314. DWORD Flags)
  315. {
  316. HRESULT hr = S_OK;
  317. ASSERT((LocalServerAddress != NULL),(TB,"Update: LocalServerAddr null!"));
  318. ASSERT((StoreServerName != NULL),(TB,"Update: StoreServerName null!"));
  319. ASSERT((ClusterName != NULL),(TB,"Update: ClusterName null!"));
  320. ASSERT((OpaqueSettings != NULL),(TB,"Update: OpaqueSettings null!"));
  321. // For update, we do not care about either LocalServerAddress or
  322. // OpaqueSettings. If the StoreServerName or ClusterName has changed, we
  323. // Terminate and then reinitialize.
  324. if ((_wcsnicmp(StoreServerName, m_StoreServerName, 64) != 0)
  325. || (_wcsnicmp(ClusterName, m_ClusterName, 64) != 0)
  326. || (Flags != m_Flags)) {
  327. // Terminate current connection.
  328. Terminate();
  329. // Initialize new connection.
  330. hr = Initialize(LocalServerAddress, StoreServerName, ClusterName,
  331. OpaqueSettings, Flags, m_repopfn);
  332. }
  333. return hr;
  334. }
  335. /****************************************************************************/
  336. // CTSSessionDirectory::GetUserDisconnectedSessions
  337. //
  338. // Called to perform a query against the session directory, to provide the
  339. // list of disconnected sessions for the provided username and domain.
  340. // Returns zero or more TSSD_DisconnectedSessionInfo blocks in SessionBuf.
  341. // *pNumSessionsReturned receives the number of blocks.
  342. /****************************************************************************/
  343. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::GetUserDisconnectedSessions(
  344. LPWSTR UserName,
  345. LPWSTR Domain,
  346. DWORD __RPC_FAR *pNumSessionsReturned,
  347. TSSD_DisconnectedSessionInfo __RPC_FAR SessionBuf[
  348. TSSD_MaxDisconnectedSessions])
  349. {
  350. DWORD NumSessions = 0;
  351. HRESULT hr;
  352. unsigned i;
  353. unsigned long RpcException;
  354. TSSD_DiscSessInfo *adsi = NULL;
  355. TRC2((TB,"GetUserDisconnectedSessions"));
  356. ASSERT((pNumSessionsReturned != NULL),(TB,"NULL pNumSess"));
  357. ASSERT((SessionBuf != NULL),(TB,"NULL SessionBuf"));
  358. // Make the RPC call.
  359. if (EnterSDRpc()) {
  360. RpcTryExcept {
  361. hr = TSSDRpcGetUserDisconnectedSessions(m_hRPCBinding, &m_hCI,
  362. UserName, Domain, &NumSessions, &adsi);
  363. }
  364. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  365. RpcException = RpcExceptionCode();
  366. ERR((TB,"GetUserDisc: RPC Exception %d\n", RpcException));
  367. // In case RPC messed with us.
  368. m_hCI = NULL;
  369. NumSessions = 0;
  370. adsi = NULL;
  371. hr = E_FAIL;
  372. }
  373. RpcEndExcept
  374. if (SUCCEEDED(hr)) {
  375. TRC1((TB,"GetUserDisc: RPC call returned %u records", NumSessions));
  376. // Loop through and fill out the session records.
  377. for (i = 0; i < NumSessions; i++) {
  378. // ServerAddress
  379. wcsncpy(SessionBuf[i].ServerAddress, adsi[i].ServerAddress,
  380. sizeof(SessionBuf[i].ServerAddress) /
  381. sizeof(WCHAR) - 1);
  382. SessionBuf[i].ServerAddress[sizeof(
  383. SessionBuf[i].ServerAddress) /
  384. sizeof(WCHAR) - 1] = L'\0';
  385. // SessionId, TSProtocol
  386. SessionBuf[i].SessionID = adsi[i].SessionID;
  387. SessionBuf[i].TSProtocol = adsi[i].TSProtocol;
  388. // ApplicationType
  389. wcsncpy(SessionBuf[i].ApplicationType, adsi[i].AppType,
  390. sizeof(SessionBuf[i].ApplicationType) /
  391. sizeof(WCHAR) - 1);
  392. SessionBuf[i].ApplicationType[sizeof(SessionBuf[i].
  393. ApplicationType) / sizeof(WCHAR) - 1] = L'\0';
  394. // Resolutionwidth, ResolutionHeight, ColorDepth, CreateTime,
  395. // DisconnectionTime.
  396. SessionBuf[i].ResolutionWidth = adsi[i].ResolutionWidth;
  397. SessionBuf[i].ResolutionHeight = adsi[i].ResolutionHeight;
  398. SessionBuf[i].ColorDepth = adsi[i].ColorDepth;
  399. SessionBuf[i].CreateTime.dwLowDateTime = adsi[i].CreateTimeLow;
  400. SessionBuf[i].CreateTime.dwHighDateTime =
  401. adsi[i].CreateTimeHigh;
  402. SessionBuf[i].DisconnectionTime.dwLowDateTime =
  403. adsi[i].DisconnectTimeLow;
  404. SessionBuf[i].DisconnectionTime.dwHighDateTime =
  405. adsi[i].DisconnectTimeHigh;
  406. // Free the memory allocated by the server.
  407. MIDL_user_free(adsi[i].ServerAddress);
  408. MIDL_user_free(adsi[i].AppType);
  409. }
  410. }
  411. else {
  412. ERR((TB,"GetUserDisc: Failed RPC call, hr=0x%X", hr));
  413. NotifySDServerDown();
  414. }
  415. LeaveSDRpc();
  416. }
  417. else {
  418. ERR((TB,"GetUserDisc: Session Directory is unreachable"));
  419. hr = E_FAIL;
  420. }
  421. MIDL_user_free(adsi);
  422. *pNumSessionsReturned = NumSessions;
  423. return hr;
  424. }
  425. /****************************************************************************/
  426. // CTSSessionDirectory::NotifyCreateLocalSession
  427. //
  428. // ITSSessionDirectory function. Called when a session is created to add the
  429. // session to the session directory. Note that other interface functions
  430. // access the session directory by either the username/domain or the
  431. // session ID; the directory schema should take this into account for
  432. // performance optimization.
  433. /****************************************************************************/
  434. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::NotifyCreateLocalSession(
  435. TSSD_CreateSessionInfo __RPC_FAR *pCreateInfo)
  436. {
  437. HRESULT hr;
  438. unsigned long RpcException;
  439. TRC2((TB,"NotifyCreateLocalSession, SessID=%u", pCreateInfo->SessionID));
  440. ASSERT((pCreateInfo != NULL),(TB,"NotifyCreate: NULL CreateInfo"));
  441. // Make the RPC call.
  442. if (EnterSDRpc()) {
  443. // Make the RPC call.
  444. RpcTryExcept {
  445. // Make the call.
  446. hr = TSSDRpcCreateSession(m_hRPCBinding, &m_hCI,
  447. pCreateInfo->UserName,
  448. pCreateInfo->Domain, pCreateInfo->SessionID,
  449. pCreateInfo->TSProtocol, pCreateInfo->ApplicationType,
  450. pCreateInfo->ResolutionWidth, pCreateInfo->ResolutionHeight,
  451. pCreateInfo->ColorDepth,
  452. pCreateInfo->CreateTime.dwLowDateTime,
  453. pCreateInfo->CreateTime.dwHighDateTime);
  454. }
  455. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  456. RpcException = RpcExceptionCode();
  457. ERR((TB,"NotifyCreate: RPC Exception %d\n", RpcException));
  458. hr = E_FAIL;
  459. }
  460. RpcEndExcept
  461. if (FAILED(hr)) {
  462. ERR((TB,"NotifyCreate: Failed RPC call, hr=0x%X", hr));
  463. NotifySDServerDown();
  464. }
  465. LeaveSDRpc();
  466. }
  467. else {
  468. ERR((TB,"NotifyCreate: Session directory is unreachable"));
  469. hr = E_FAIL;
  470. }
  471. return hr;
  472. }
  473. /****************************************************************************/
  474. // CTSSessionDirectory::NotifyDestroyLocalSession
  475. //
  476. // ITSSessionDirectory function. Removes a session from the session database.
  477. /****************************************************************************/
  478. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::NotifyDestroyLocalSession(
  479. DWORD SessionID)
  480. {
  481. HRESULT hr;
  482. unsigned long RpcException;
  483. TRC2((TB,"NotifyDestroyLocalSession, SessionID=%u", SessionID));
  484. // Make the RPC call.
  485. if (EnterSDRpc()) {
  486. RpcTryExcept {
  487. // Make the call.
  488. hr = TSSDRpcDeleteSession(m_hRPCBinding, &m_hCI, SessionID);
  489. }
  490. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  491. RpcException = RpcExceptionCode();
  492. ERR((TB,"NotifyDestroy: RPC Exception %d\n", RpcException));
  493. hr = E_FAIL;
  494. }
  495. RpcEndExcept
  496. if (FAILED(hr)) {
  497. ERR((TB,"NotifyDestroy: Failed RPC call, hr=0x%X", hr));
  498. NotifySDServerDown();
  499. }
  500. LeaveSDRpc();
  501. }
  502. else {
  503. ERR((TB,"NotifyDestroy: Session directory is unreachable"));
  504. hr = E_FAIL;
  505. }
  506. return hr;
  507. }
  508. /****************************************************************************/
  509. // CTSSessionDirectory::NotifyDisconnectLocalSession
  510. //
  511. // ITSSessionDirectory function. Changes the state of an existing session to
  512. // disconnected. The provided time should be returned in disconnected session
  513. // queries performed by any machine in the server pool.
  514. /****************************************************************************/
  515. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::NotifyDisconnectLocalSession(
  516. DWORD SessionID,
  517. FILETIME DiscTime)
  518. {
  519. HRESULT hr;
  520. unsigned long RpcException;
  521. TRC2((TB,"NotifyDisconnectLocalSession, SessionID=%u", SessionID));
  522. // Make the RPC call.
  523. if (EnterSDRpc()) {
  524. RpcTryExcept {
  525. // Make the call.
  526. hr = TSSDRpcSetSessionDisconnected(m_hRPCBinding, &m_hCI, SessionID,
  527. DiscTime.dwLowDateTime, DiscTime.dwHighDateTime);
  528. }
  529. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  530. RpcException = RpcExceptionCode();
  531. ERR((TB,"NotifyDisc: RPC Exception %d\n", RpcException));
  532. hr = E_FAIL;
  533. }
  534. RpcEndExcept
  535. if (FAILED(hr)) {
  536. ERR((TB,"NotifyDisc: RPC call failed, hr=0x%X", hr));
  537. NotifySDServerDown();
  538. }
  539. LeaveSDRpc();
  540. }
  541. else {
  542. ERR((TB,"NotifyDisc: Session directory is unreachable"));
  543. hr = E_FAIL;
  544. }
  545. return hr;
  546. }
  547. /****************************************************************************/
  548. // CTSSessionDirectory::NotifyReconnectLocalSession
  549. //
  550. // ITSSessionDirectory function. Changes the state of an existing session
  551. // from disconnected to connected.
  552. /****************************************************************************/
  553. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::NotifyReconnectLocalSession(
  554. TSSD_ReconnectSessionInfo __RPC_FAR *pReconnInfo)
  555. {
  556. HRESULT hr;
  557. unsigned long RpcException;
  558. TRC2((TB,"NotifyReconnectLocalSession, SessionID=%u",
  559. pReconnInfo->SessionID));
  560. // Make the RPC call.
  561. if (EnterSDRpc()) {
  562. RpcTryExcept {
  563. // Make the call.
  564. hr = TSSDRpcSetSessionReconnected(m_hRPCBinding, &m_hCI,
  565. pReconnInfo->SessionID, pReconnInfo->TSProtocol,
  566. pReconnInfo->ResolutionWidth, pReconnInfo->ResolutionHeight,
  567. pReconnInfo->ColorDepth);
  568. }
  569. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  570. RpcException = RpcExceptionCode();
  571. ERR((TB,"NotifyReconn: RPC Exception %d\n", RpcException));
  572. hr = E_FAIL;
  573. }
  574. RpcEndExcept
  575. if (FAILED(hr)) {
  576. ERR((TB,"NotifyReconn: RPC call failed, hr=0x%X", hr));
  577. NotifySDServerDown();
  578. }
  579. LeaveSDRpc();
  580. }
  581. else {
  582. ERR((TB,"NotifyReconn: Session directory is unreachable"));
  583. hr = E_FAIL;
  584. }
  585. return hr;
  586. }
  587. /****************************************************************************/
  588. // CTSSessionDirectory::NotifyReconnectPending
  589. //
  590. // ITSSessionDirectory function. Informs session directory that a reconnect
  591. // is pending soon because of a revectoring. Used by DIS to determine
  592. // when a server might have gone down. (DIS is the Directory Integrity
  593. // Service, which runs on the machine with the session directory.)
  594. //
  595. // This is a two-phase procedure--we first check the fields, and then we
  596. // add the timestamp only if there is no outstanding timestamp already (i.e.,
  597. // the two Almost-In-Time fields are 0). This prevents constant revectoring
  598. // from updating the timestamp fields, which would prevent the DIS from
  599. // figuring out that a server is down.
  600. //
  601. // These two steps are done in the stored procedure to make the operation
  602. // atomic.
  603. /****************************************************************************/
  604. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::NotifyReconnectPending(
  605. WCHAR *ServerName)
  606. {
  607. HRESULT hr;
  608. unsigned long RpcException;
  609. FILETIME ft;
  610. SYSTEMTIME st;
  611. TRC2((TB,"NotifyReconnectPending"));
  612. ASSERT((ServerName != NULL),(TB,"NotifyReconnectPending: NULL ServerName"));
  613. // Get the current system time.
  614. GetSystemTime(&st);
  615. SystemTimeToFileTime(&st, &ft);
  616. // Make the RPC call.
  617. if (EnterSDRpc()) {
  618. RpcTryExcept {
  619. // Make the call.
  620. hr = TSSDRpcSetServerReconnectPending(m_hRPCBinding, ServerName,
  621. ft.dwLowDateTime, ft.dwHighDateTime);
  622. }
  623. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  624. RpcException = RpcExceptionCode();
  625. ERR((TB,"NotifyReconnPending: RPC Exception %d\n", RpcException));
  626. hr = E_FAIL;
  627. }
  628. RpcEndExcept
  629. if (FAILED(hr)) {
  630. ERR((TB,"NotifyReconnPending: RPC call failed, hr=0x%X", hr));
  631. NotifySDServerDown();
  632. }
  633. LeaveSDRpc();
  634. }
  635. else {
  636. ERR((TB,"NotifyReconnPending: Session directory is unreachable"));
  637. hr = E_FAIL;
  638. }
  639. return hr;
  640. }
  641. /****************************************************************************/
  642. // CTSSessionDirectory::Repopulate
  643. //
  644. // This function is called by the recovery thread, and repopulates the session
  645. // directory with all sessions.
  646. //
  647. // Arguments: WinStationCount - # of winstations to repopulate
  648. // rsi - array of TSSD_RepopulateSessionInfo structs.
  649. //
  650. // Return value: HRESULT
  651. /****************************************************************************/
  652. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::Repopulate(DWORD WinStationCount,
  653. TSSD_RepopulateSessionInfo *rsi)
  654. {
  655. HRESULT hr = S_OK;
  656. unsigned long RpcException;
  657. ASSERT(((rsi != NULL) || (WinStationCount == 0)),(TB,"Repopulate: NULL "
  658. "rsi!"));
  659. RpcTryExcept {
  660. hr = TSSDRpcRepopulateAllSessions(m_hRPCBinding, &m_hCI,
  661. WinStationCount, (TSSD_RepopInfo *) rsi);
  662. if (FAILED(hr)) {
  663. ERR((TB, "Repop: RPC call failed, hr = 0x%X", hr));
  664. }
  665. }
  666. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  667. RpcException = RpcExceptionCode();
  668. ERR((TB, "Repop: RPC Exception %d\n", RpcException));
  669. hr = E_FAIL;
  670. }
  671. RpcEndExcept
  672. return hr;
  673. }
  674. /****************************************************************************/
  675. // Plug-in UI interface for TSCC
  676. /****************************************************************************/
  677. /****************************************************************************/
  678. // describes the name of this entry in server settings
  679. /****************************************************************************/
  680. STDMETHODIMP CTSSessionDirectory::GetAttributeName(
  681. /* out */ WCHAR *pwszAttribName)
  682. {
  683. TCHAR szAN[256];
  684. ASSERT((pwszAttribName != NULL),(TB,"NULL attrib ptr"));
  685. LoadString(g_hInstance, IDS_ATTRIBUTE_NAME, szAN, sizeof(szAN) /
  686. sizeof(TCHAR));
  687. lstrcpy(pwszAttribName, szAN);
  688. return S_OK;
  689. }
  690. /****************************************************************************/
  691. // for this component the attribute value indicates whether it is enabled
  692. /****************************************************************************/
  693. STDMETHODIMP CTSSessionDirectory::GetDisplayableValueName(
  694. /* out */WCHAR *pwszAttribValueName)
  695. {
  696. TCHAR szAvn[256];
  697. ASSERT((pwszAttribValueName != NULL),(TB,"NULL attrib ptr"));
  698. POLICY_TS_MACHINE gpolicy;
  699. RegGetMachinePolicy(&gpolicy);
  700. if (gpolicy.fPolicySessionDirectoryActive)
  701. m_fEnabled = gpolicy.SessionDirectoryActive;
  702. else
  703. m_fEnabled = IsSessionDirectoryEnabled();
  704. if (m_fEnabled)
  705. {
  706. LoadString(g_hInstance, IDS_ENABLE, szAvn, sizeof(szAvn) /
  707. sizeof(TCHAR));
  708. }
  709. else
  710. {
  711. LoadString(g_hInstance, IDS_DISABLE, szAvn, sizeof(szAvn) /
  712. sizeof(TCHAR));
  713. }
  714. lstrcpy(pwszAttribValueName, szAvn);
  715. return S_OK;
  716. }
  717. /****************************************************************************/
  718. // Provides custom UI
  719. /****************************************************************************/
  720. STDMETHODIMP CTSSessionDirectory::InvokeUI(/* in */ HWND hParent, /*out*/
  721. PDWORD pdwStatus)
  722. {
  723. WSADATA wsaData;
  724. if (WSAStartup(0x202, &wsaData) == 0)
  725. {
  726. INT_PTR iRet = DialogBoxParam(g_hInstance,
  727. MAKEINTRESOURCE(IDD_DIALOG_SDS),
  728. hParent,
  729. (DLGPROC)CustomUIDlg,
  730. (LPARAM)this
  731. );
  732. // TRC1((TB,"DialogBox returned 0x%x", iRet));
  733. // TRC1((TB,"Extended error = %lx", GetLastError()));
  734. *pdwStatus = (DWORD)iRet;
  735. WSACleanup();
  736. }
  737. else
  738. {
  739. *pdwStatus = WSAGetLastError();
  740. TRC1((TB,"WSAStartup failed with 0x%x", *pdwStatus));
  741. ErrorMessage(hParent, IDS_ERROR_TEXT3, *pdwStatus);
  742. return E_FAIL;
  743. }
  744. return S_OK;
  745. }
  746. /****************************************************************************/
  747. // Custom menu items -- must be freed by LocalFree
  748. // this is called everytime the user right clicks the listitem
  749. // so you can alter the settings (i.e. enable to disable and vice versa)
  750. /****************************************************************************/
  751. STDMETHODIMP CTSSessionDirectory::GetMenuItems(
  752. /* out */ int *pcbItems,
  753. /* out */ PMENUEXTENSION *pMex)
  754. {
  755. ASSERT((pcbItems != NULL),(TB,"NULL items ptr"));
  756. *pcbItems = 2;
  757. *pMex = (PMENUEXTENSION)LocalAlloc(LMEM_FIXED, *pcbItems *
  758. sizeof(MENUEXTENSION));
  759. if (*pMex != NULL)
  760. {
  761. LoadString(g_hInstance, IDS_PROPERTIES, (*pMex)[0].MenuItemName,
  762. sizeof((*pMex)[0].MenuItemName) / sizeof(WCHAR));
  763. LoadString(g_hInstance, IDS_DESCRIP_PROPS, (*pMex)[0].StatusBarText,
  764. sizeof((*pMex)[0].StatusBarText) / sizeof(WCHAR));
  765. // menu items id -- this id will be passed back to you in ExecMenuCmd
  766. (*pMex)[0].cmd = IDM_MENU_PROPS;
  767. // load string to display enable or disable
  768. if (m_fEnabled)
  769. {
  770. LoadString(g_hInstance, IDS_DISABLE, (*pMex)[1].MenuItemName,
  771. sizeof((*pMex)[1].MenuItemName) / sizeof(WCHAR));
  772. }
  773. else
  774. {
  775. LoadString(g_hInstance, IDS_ENABLE, (*pMex)[1].MenuItemName,
  776. sizeof((*pMex)[1].MenuItemName) / sizeof(WCHAR));
  777. }
  778. // acquire the description text for menu item
  779. LoadString(g_hInstance, IDS_DESCRIP_ENABLE, (*pMex)[1].StatusBarText,
  780. sizeof((*pMex)[1].StatusBarText) / sizeof(WCHAR));
  781. // menu items id -- this id will be passed back to you in ExecMenuCmd
  782. (*pMex)[1].cmd = IDM_MENU_ENABLE;
  783. return S_OK;
  784. }
  785. else
  786. {
  787. return E_OUTOFMEMORY;
  788. }
  789. }
  790. /****************************************************************************/
  791. // When the user selects a menu item the cmd id is passed to this component.
  792. // the provider (which is us)
  793. /****************************************************************************/
  794. STDMETHODIMP CTSSessionDirectory::ExecMenuCmd(
  795. /* in */ UINT cmd,
  796. /* in */ HWND hParent,
  797. /* out*/ PDWORD pdwStatus)
  798. {
  799. WSADATA wsaData;
  800. switch (cmd) {
  801. case IDM_MENU_ENABLE:
  802. m_fEnabled = m_fEnabled ? 0 : 1;
  803. TRC1((TB,"%ws was selected", m_fEnabled ? L"Disable" : L"Enable"));
  804. if (SetSessionDirectoryEnabledState(m_fEnabled) == ERROR_SUCCESS)
  805. {
  806. *pdwStatus = UPDATE_TERMSRV_SESSDIR;
  807. }
  808. break;
  809. case IDM_MENU_PROPS:
  810. if (WSAStartup(0x202, &wsaData) == 0)
  811. {
  812. INT_PTR iRet = DialogBoxParam(g_hInstance,
  813. MAKEINTRESOURCE(IDD_DIALOG_SDS),
  814. hParent,
  815. (DLGPROC)CustomUIDlg,
  816. (LPARAM)this);
  817. *pdwStatus = (DWORD)iRet;
  818. WSACleanup();
  819. }
  820. else
  821. {
  822. *pdwStatus = WSAGetLastError();
  823. TRC1((TB,"WSAStartup failed with 0x%x", *pdwStatus));
  824. ErrorMessage(hParent, IDS_ERROR_TEXT3, *pdwStatus);
  825. return E_FAIL;
  826. }
  827. }
  828. return S_OK;
  829. }
  830. /****************************************************************************/
  831. // Tscc provides a default help menu item, when selected this method is called
  832. // if we want tscc to handle (or provide) help return any value other than zero
  833. // for those u can't follow logic return zero if you're handling help.
  834. /****************************************************************************/
  835. STDMETHODIMP CTSSessionDirectory::OnHelp(/* out */ int *piRet)
  836. {
  837. ASSERT((piRet != NULL),(TB,"NULL ret ptr"));
  838. *piRet = 0;
  839. return S_OK;
  840. }
  841. /****************************************************************************/
  842. // CheckSessionDirectorySetting returns a bool
  843. /****************************************************************************/
  844. BOOL CTSSessionDirectory::CheckSessionDirectorySetting(WCHAR *Setting)
  845. {
  846. LONG lRet;
  847. HKEY hKey;
  848. DWORD dwEnabled = 0;
  849. DWORD dwSize = sizeof(DWORD);
  850. lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  851. REG_CONTROL_TSERVER,
  852. 0,
  853. KEY_READ,
  854. &hKey);
  855. if (lRet == ERROR_SUCCESS)
  856. {
  857. lRet = RegQueryValueEx(hKey,
  858. Setting,
  859. NULL,
  860. NULL,
  861. (LPBYTE)&dwEnabled,
  862. &dwSize);
  863. RegCloseKey(hKey);
  864. }
  865. return (BOOL)dwEnabled;
  866. }
  867. /****************************************************************************/
  868. // IsSessionDirectoryEnabled returns a bool
  869. /****************************************************************************/
  870. BOOL CTSSessionDirectory::IsSessionDirectoryEnabled()
  871. {
  872. return CheckSessionDirectorySetting(REG_TS_SESSDIRACTIVE);
  873. }
  874. /****************************************************************************/
  875. // IsSessionDirectoryEnabled returns a bool
  876. /****************************************************************************/
  877. BOOL CTSSessionDirectory::IsSessionDirectoryExposeServerIPEnabled()
  878. {
  879. return CheckSessionDirectorySetting(REG_TS_SESSDIR_EXPOSE_SERVER_ADDR);
  880. }
  881. /****************************************************************************/
  882. // SetSessionDirectoryState - sets "Setting" regkey to bVal
  883. /****************************************************************************/
  884. DWORD CTSSessionDirectory::SetSessionDirectoryState(WCHAR *Setting, BOOL bVal)
  885. {
  886. LONG lRet;
  887. HKEY hKey;
  888. DWORD dwSize = sizeof(DWORD);
  889. lRet = RegOpenKeyEx(
  890. HKEY_LOCAL_MACHINE,
  891. REG_CONTROL_TSERVER,
  892. 0,
  893. KEY_WRITE,
  894. &hKey);
  895. if (lRet == ERROR_SUCCESS)
  896. {
  897. lRet = RegSetValueEx(hKey,
  898. Setting,
  899. 0,
  900. REG_DWORD,
  901. (LPBYTE)&bVal,
  902. dwSize);
  903. RegCloseKey(hKey);
  904. }
  905. else
  906. {
  907. ErrorMessage(NULL, IDS_ERROR_TEXT3, (DWORD)lRet);
  908. }
  909. return (DWORD)lRet;
  910. }
  911. /****************************************************************************/
  912. // SetSessionDirectoryEnabledState - sets SessionDirectoryActive regkey to bVal
  913. /****************************************************************************/
  914. DWORD CTSSessionDirectory::SetSessionDirectoryEnabledState(BOOL bVal)
  915. {
  916. return SetSessionDirectoryState(REG_TS_SESSDIRACTIVE, bVal);
  917. }
  918. /****************************************************************************/
  919. // SetSessionDirectoryExposeIPState - sets SessionDirectoryExposeServerIP
  920. // regkey to bVal
  921. /****************************************************************************/
  922. DWORD CTSSessionDirectory::SetSessionDirectoryExposeIPState(BOOL bVal)
  923. {
  924. return SetSessionDirectoryState(REG_TS_SESSDIR_EXPOSE_SERVER_ADDR, bVal);
  925. }
  926. /****************************************************************************/
  927. // ErrorMessage --
  928. /****************************************************************************/
  929. void CTSSessionDirectory::ErrorMessage(HWND hwnd, UINT res, DWORD dwStatus)
  930. {
  931. TCHAR tchTitle[64];
  932. TCHAR tchText[64];
  933. TCHAR tchErrorMessage[256];
  934. LPTSTR pBuffer = NULL;
  935. // report error
  936. ::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
  937. FORMAT_MESSAGE_FROM_SYSTEM,
  938. NULL, //ignored
  939. (DWORD)dwStatus, //message ID
  940. MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL), //message language
  941. (LPTSTR)&pBuffer, //address of buffer pointer
  942. 0, //minimum buffer size
  943. NULL);
  944. LoadString(g_hInstance, IDS_ERROR_TITLE, tchTitle, sizeof(tchTitle) /
  945. sizeof(TCHAR));
  946. LoadString(g_hInstance, res, tchText, sizeof(tchText) / sizeof(TCHAR));
  947. wsprintf(tchErrorMessage, tchText, pBuffer);
  948. ::MessageBox(hwnd, tchErrorMessage, tchTitle, MB_OK | MB_ICONINFORMATION);
  949. }
  950. /****************************************************************************/
  951. // CTSSessionDirectory::RecoveryThread
  952. //
  953. // Static helper function. The SDPtr passed in is a pointer to this for
  954. // when _beginthreadex is called during init. RecoveryThread simply calls
  955. // the real recovery function, which is RecoveryThreadEx.
  956. /****************************************************************************/
  957. unsigned __stdcall CTSSessionDirectory::RecoveryThread(void *SDPtr) {
  958. ((CTSSessionDirectory *)SDPtr)->RecoveryThreadEx();
  959. return 0;
  960. }
  961. /****************************************************************************/
  962. // CTSSessionDirectory::RecoveryThreadEx
  963. //
  964. // Recovery thread for tssdjet recovery. Sits around and waits for the
  965. // server to go down. When the server fails, it wakes up, sets a variable
  966. // indicating that the server is unreachable, and then tries to reestablish
  967. // a connection with the server. Meanwhile, further calls to the session
  968. // directory simply fail without delay.
  969. //
  970. // When the session directory finally comes back up, the recovery thread
  971. // temporarily halts session directory updates while repopulating the database.
  972. // If all goes well, it cleans up and goes back to sleep. If all doesn't go
  973. // well, it tries again.
  974. //
  975. // The recovery thread terminates if it fails a wait, or if m_hTerminateRecovery
  976. // is set.
  977. /****************************************************************************/
  978. VOID CTSSessionDirectory::RecoveryThreadEx()
  979. {
  980. DWORD err;
  981. BOOL bErr;
  982. CONST HANDLE lpHandles[] = { m_hSDServerDown, m_hTerminateRecovery };
  983. for ( ; ; ) {
  984. // Wait forever until there is a problem with the session directory,
  985. // or until we are told to shut down.
  986. err = WaitForMultipleObjects(2, lpHandles, FALSE, INFINITE);
  987. switch (err) {
  988. case WAIT_OBJECT_0: // m_hSDServerDown
  989. // SD Server Down--go through recovery.
  990. break;
  991. case WAIT_OBJECT_0 + 1: // m_hTerminateRecovery
  992. // We're quitting.
  993. return;
  994. default:
  995. // This is unexpected. Assert on checked builds. On free,
  996. // just return.
  997. ASSERT(((err == WAIT_OBJECT_0) || (err == WAIT_OBJECT_0 + 1)),
  998. (TB, "RecoveryThreadEx: Unexpected value from Wait!"));
  999. return;
  1000. }
  1001. // Wait for all pending SD Rpcs to complete, and make all further
  1002. // EnterSDRpc's return FALSE until we're back up. Note that if there
  1003. // is a failure in recovery that this can be called more than once.
  1004. DisableSDRpcs();
  1005. // This function loops and tries to reestablish a connection with the
  1006. // session directory. When it thinks it has one, it returns.
  1007. // If it returns nonzero, though, that means it was terminated or
  1008. // an error occurred in the wait, so terminate recovery.
  1009. if (ReestablishSessionDirectoryConnection() != 0)
  1010. return;
  1011. // Now we have (theoretically) a session directory connection.
  1012. // Update the session directory. Nonzero on failure.
  1013. err = RequestSessDirUpdate();
  1014. if (err != 0) {
  1015. // Keep trying, so serverdown event stays signaled.
  1016. continue;
  1017. }
  1018. // Everything is good now. Clean up and wait for the next failure.
  1019. bErr = ResetEvent(m_hSDServerDown);
  1020. EnableSDRpcs();
  1021. }
  1022. }
  1023. /****************************************************************************/
  1024. // StartupSD
  1025. //
  1026. // Initiates a connection by signaling to the recovery thread that the server
  1027. // is down.
  1028. /****************************************************************************/
  1029. void CTSSessionDirectory::StartupSD()
  1030. {
  1031. if (SetEvent(m_hSDServerDown) == FALSE) {
  1032. ERR((TB, "StartupSD: SetEvent failed. GetLastError=%d",
  1033. GetLastError()));
  1034. }
  1035. }
  1036. /****************************************************************************/
  1037. // NotifySDServerDown
  1038. //
  1039. // Tells the recovery thread that the server is down.
  1040. /****************************************************************************/
  1041. void CTSSessionDirectory::NotifySDServerDown()
  1042. {
  1043. if (SetEvent(m_hSDServerDown) == FALSE) {
  1044. ERR((TB, "NotifySDServerDown: SetEvent failed. GetLastError=%d",
  1045. GetLastError()));
  1046. }
  1047. }
  1048. /****************************************************************************/
  1049. // EnterSDRpc
  1050. //
  1051. // This function returns whether it is OK to make an RPC right now. It handles
  1052. // not letting anyone make an RPC call if RPCs are disabled, and also, if anyone
  1053. // is able to make an RPC, it ensures they will be able to do so until they call
  1054. // LeaveSDRpc.
  1055. //
  1056. // Return value:
  1057. // true - if OK to make RPC call, in which case you must call LeaveSDRpc when
  1058. // you are done.
  1059. // false - if not OK. You must not call LeaveSDRpc.
  1060. //
  1061. /****************************************************************************/
  1062. boolean CTSSessionDirectory::EnterSDRpc()
  1063. {
  1064. AcquireResourceShared(&m_sr);
  1065. if (m_SDIsUp) {
  1066. return TRUE;
  1067. }
  1068. else {
  1069. ReleaseResourceShared(&m_sr);
  1070. return FALSE;
  1071. }
  1072. }
  1073. /****************************************************************************/
  1074. // LeaveSDRpc
  1075. //
  1076. // If you were able to EnterSDRpc (i.e., it returned true), you must call this
  1077. // function when you are done with your Rpc call no matter what happened.
  1078. /****************************************************************************/
  1079. void CTSSessionDirectory::LeaveSDRpc()
  1080. {
  1081. ReleaseResourceShared(&m_sr);
  1082. }
  1083. /****************************************************************************/
  1084. // DisableSDRpcs
  1085. //
  1086. // Prevent new EnterSDRpcs from returning true, and then wait for all pending
  1087. // EnterSDRpcs to be matched by their LeaveSDRpc's.
  1088. /****************************************************************************/
  1089. void CTSSessionDirectory::DisableSDRpcs()
  1090. {
  1091. //
  1092. // First, set the flag that the SD is up to FALSE, preventing further Rpcs.
  1093. // Then, we grab the resource exclusive and release it right afterwards--
  1094. // this forces us to wait until all RPCs we're already in have completed.
  1095. //
  1096. (void) InterlockedExchange(&m_SDIsUp, FALSE);
  1097. AcquireResourceExclusive(&m_sr);
  1098. ReleaseResourceExclusive(&m_sr);
  1099. }
  1100. /****************************************************************************/
  1101. // EnableSDRpcs
  1102. //
  1103. // Enable EnterSDRpcs to return true once again.
  1104. /****************************************************************************/
  1105. void CTSSessionDirectory::EnableSDRpcs()
  1106. {
  1107. ASSERT((VerifyNoSharedAccess(&m_sr)),(TB,"EnableSDRpcs called but "
  1108. "shouldn't be when there are shared readers."));
  1109. (void) InterlockedExchange(&m_SDIsUp, TRUE);
  1110. }
  1111. /****************************************************************************/
  1112. // RequestSessDirUpdate
  1113. //
  1114. // Requests that termsrv update the session directory using the batchupdate
  1115. // interface.
  1116. //
  1117. // This function needs to know whether the update succeeded and return 0 on
  1118. // success, nonzero on failure.
  1119. /****************************************************************************/
  1120. DWORD CTSSessionDirectory::RequestSessDirUpdate()
  1121. {
  1122. return (*m_repopfn)();
  1123. }
  1124. /****************************************************************************/
  1125. // ReestablishSessionDirectoryConnection
  1126. //
  1127. // This function loops and tries to reestablish a connection with the
  1128. // session directory. When it has one, it returns.
  1129. //
  1130. // Return value: 0 if normal exit, nonzero if terminated by TerminateRecovery
  1131. // event.
  1132. /****************************************************************************/
  1133. DWORD CTSSessionDirectory::ReestablishSessionDirectoryConnection()
  1134. {
  1135. HRESULT hr;
  1136. unsigned long RpcException;
  1137. DWORD err;
  1138. for ( ; ; ) {
  1139. // Execute ServerOnline.
  1140. RpcTryExcept {
  1141. hr = TSSDRpcServerOnline(m_hRPCBinding, m_ClusterName, &m_hCI,
  1142. m_Flags);
  1143. }
  1144. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  1145. m_hCI = NULL;
  1146. RpcException = RpcExceptionCode();
  1147. hr = E_FAIL;
  1148. }
  1149. RpcEndExcept
  1150. if (SUCCEEDED(hr)) {
  1151. RpcTryExcept {
  1152. hr = TSSDRpcUpdateConfigurationSetting(m_hRPCBinding, &m_hCI,
  1153. SDCONFIG_SERVER_ADDRESS,
  1154. (DWORD) (wcslen(m_LocalServerAddress) + 1) *
  1155. sizeof(WCHAR), (PBYTE) m_LocalServerAddress);
  1156. }
  1157. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  1158. m_hCI = NULL;
  1159. RpcException = RpcExceptionCode();
  1160. hr = E_FAIL;
  1161. }
  1162. RpcEndExcept
  1163. if (SUCCEEDED(hr))
  1164. return 0;
  1165. }
  1166. err = WaitForSingleObject(m_hTerminateRecovery, m_RecoveryTimeout);
  1167. if (err != WAIT_TIMEOUT) {
  1168. // It was not a timeout, it better be our terminate recovery event.
  1169. ASSERT((err == WAIT_OBJECT_0),(TB, "ReestSessDirConn: Unexpected "
  1170. "value returned from wait"));
  1171. // If it was not our event, we want to keep going through
  1172. // this loop so this thread does not terminate.
  1173. if (err == WAIT_OBJECT_0)
  1174. return 1;
  1175. }
  1176. }
  1177. }
  1178. /****************************************************************************/
  1179. // CTSSessionDirectory::Terminate
  1180. //
  1181. // Helper function called by the destructor and by Update when switching to
  1182. // another server. Frees RPC binding, events, and recovery thread.
  1183. /****************************************************************************/
  1184. void CTSSessionDirectory::Terminate()
  1185. {
  1186. HRESULT rc = S_OK;
  1187. unsigned long RpcException;
  1188. BOOL ConnectionMaybeUp;
  1189. // Terminate recovery.
  1190. if (m_hRecoveryThread != NULL) {
  1191. SetEvent(m_hTerminateRecovery);
  1192. WaitForSingleObject((HANDLE) m_hRecoveryThread, INFINITE);
  1193. m_hRecoveryThread = NULL;
  1194. }
  1195. ConnectionMaybeUp = EnterSDRpc();
  1196. if (ConnectionMaybeUp)
  1197. LeaveSDRpc();
  1198. // Wait for current Rpcs to complete (if any), disable new ones.
  1199. DisableSDRpcs();
  1200. // If we think there is a connection, disconnect it.
  1201. if (ConnectionMaybeUp) {
  1202. RpcTryExcept {
  1203. rc = TSSDRpcServerOffline(m_hRPCBinding, &m_hCI);
  1204. }
  1205. RpcExcept(TSSDRpcExceptionFilter(RpcExceptionCode())) {
  1206. RpcException = RpcExceptionCode();
  1207. ERR((TB,"Term: RPC Exception %d\n", RpcException));
  1208. rc = E_FAIL;
  1209. }
  1210. RpcEndExcept
  1211. if (FAILED(rc)) {
  1212. ERR((TB,"Term: SvrOffline failed, lasterr=0x%X", GetLastError()));
  1213. }
  1214. }
  1215. // Clean up.
  1216. if (m_hRPCBinding != NULL) {
  1217. RpcBindingFree(&m_hRPCBinding);
  1218. m_hRPCBinding = NULL;
  1219. }
  1220. if (m_hSDServerDown != NULL) {
  1221. CloseHandle(m_hSDServerDown);
  1222. m_hSDServerDown = NULL;
  1223. }
  1224. if (m_hTerminateRecovery != NULL) {
  1225. CloseHandle(m_hTerminateRecovery);
  1226. m_hTerminateRecovery = NULL;
  1227. }
  1228. if (m_sr.Valid == TRUE) {
  1229. // We clean up only in the destructor, because we may initialize again.
  1230. // On check builds verify that no one is currently accessing.
  1231. ASSERT((VerifyNoSharedAccess(&m_sr)), (TB, "Terminate: Shared readers"
  1232. " exist!"));
  1233. }
  1234. }
  1235. /****************************************************************************/
  1236. // CTSSessionDirectory::GetLoadBalanceInfo
  1237. //
  1238. // Based on the server address, generate load balance info to send to the client
  1239. /****************************************************************************/
  1240. HRESULT STDMETHODCALLTYPE CTSSessionDirectory::GetLoadBalanceInfo(
  1241. LPWSTR ServerAddress,
  1242. BSTR* LBInfo)
  1243. {
  1244. HRESULT hr = S_OK;
  1245. // This is for test only
  1246. //WCHAR lbInfo[MAX_PATH];
  1247. //wcscpy(lbInfo, L"load balance info");
  1248. *LBInfo = NULL;
  1249. TRC2((TB,"GetLoadBalanceInfo"));
  1250. if (ServerAddress) {
  1251. //
  1252. // "Cookie: msts=4294967295.65535.0000" + CR + LF + NULL, on 8-byte
  1253. // boundary is 40 bytes.
  1254. //
  1255. // The format of the cookie for F5 is, for an IP of 1.2.3.4
  1256. // using port 3389, Cookie: msts=67305985.15629.0000 + CR + LF + NULL.
  1257. //
  1258. #define TEMPLATE_STRING_LENGTH 40
  1259. #define SERVER_ADDRESS_LENGTH 64
  1260. char CookieTemplate[TEMPLATE_STRING_LENGTH];
  1261. char AnsiServerAddress[SERVER_ADDRESS_LENGTH];
  1262. unsigned long NumericalServerAddr = 0;
  1263. int retval;
  1264. // Compute integer for the server address.
  1265. // First, get ServerAddress as an ANSI string.
  1266. retval = WideCharToMultiByte(CP_ACP, 0, ServerAddress, -1,
  1267. AnsiServerAddress, SERVER_ADDRESS_LENGTH, NULL, NULL);
  1268. if (retval == 0) {
  1269. TRC2((TB, "GetLoadBalanceInfo WideCharToMB failed %d",
  1270. GetLastError()));
  1271. return E_INVALIDARG;
  1272. }
  1273. // Now, use inet_addr to turn into an unsigned long.
  1274. NumericalServerAddr = inet_addr(AnsiServerAddress);
  1275. if (NumericalServerAddr == INADDR_NONE) {
  1276. TRC2((TB, "GetLoadBalanceInfo inet_addr failed"));
  1277. return E_INVALIDARG;
  1278. }
  1279. // Compute the total cookie string. 0x3d0d is 3389 in correct byte
  1280. // order. We need to change this to whatever the port number has been
  1281. // configured to.
  1282. sprintf(CookieTemplate, "Cookie: msts=%u.%u.0000\r\n",
  1283. NumericalServerAddr, 0x3d0d);
  1284. // Generate returned BSTR.
  1285. *LBInfo = SysAllocStringByteLen((LPCSTR)CookieTemplate,
  1286. (UINT) strlen(CookieTemplate));
  1287. if (*LBInfo) {
  1288. TRC2((TB,"GetLoadBalanceInfo: okay"));
  1289. hr = S_OK;
  1290. }
  1291. else {
  1292. TRC2((TB,"GetLoadBalanceInfo: failed"));
  1293. hr = E_OUTOFMEMORY;
  1294. }
  1295. }
  1296. else {
  1297. TRC2((TB,"GetLoadBalanceInfo: failed"));
  1298. hr = E_FAIL;
  1299. }
  1300. return hr;
  1301. }
  1302. /****************************************************************************/
  1303. // IsServerNameValid
  1304. //
  1305. // This function tries to ping the server name to determine if its a valid
  1306. // entry
  1307. //
  1308. // Return value: FALSE if we cannot ping.
  1309. // event.
  1310. /****************************************************************************/
  1311. BOOL IsServerNameValid(wchar_t * pwszName)
  1312. {
  1313. HCURSOR hCursor = NULL;
  1314. long inaddr;
  1315. char szAnsiServerName[256];
  1316. struct hostent *hostp = NULL;
  1317. BOOL bRet;
  1318. if (pwszName == NULL || pwszName[0] == '\0')
  1319. {
  1320. bRet = FALSE;
  1321. }
  1322. else
  1323. {
  1324. hCursor = SetCursor(LoadCursor(NULL, MAKEINTRESOURCE(IDC_WAIT)));
  1325. // some winsock apis does accept wides.
  1326. WideCharToMultiByte(CP_ACP,
  1327. 0,
  1328. pwszName,
  1329. -1,
  1330. szAnsiServerName,
  1331. sizeof(szAnsiServerName),
  1332. NULL,
  1333. NULL);
  1334. // check ip format return true do a dns lookup.
  1335. if ((inaddr = inet_addr(szAnsiServerName)) == INADDR_NONE)
  1336. {
  1337. hostp = gethostbyname(szAnsiServerName);
  1338. if (hostp != NULL)
  1339. {
  1340. bRet = TRUE;
  1341. }
  1342. else
  1343. {
  1344. // Neither dotted, not name.
  1345. bRet = FALSE;
  1346. }
  1347. }
  1348. else
  1349. {
  1350. // Is dotted.
  1351. bRet = TRUE;
  1352. }
  1353. SetCursor(hCursor);
  1354. }
  1355. return bRet;
  1356. }
  1357. BOOL OnHelp(HWND hwnd, LPHELPINFO lphi)
  1358. {
  1359. UNREFERENCED_PARAMETER(hwnd);
  1360. TCHAR tchHelpFile[MAX_PATH];
  1361. //
  1362. // For the information to winhelp api
  1363. //
  1364. if (IsBadReadPtr(lphi, sizeof(HELPINFO)))
  1365. {
  1366. return FALSE;
  1367. }
  1368. if (lphi->iCtrlId <= -1)
  1369. {
  1370. return FALSE;
  1371. }
  1372. LoadString(g_hInstance, IDS_HELPFILE, tchHelpFile,
  1373. sizeof (tchHelpFile) / sizeof(TCHAR));
  1374. ULONG_PTR rgdw[2];
  1375. rgdw[0] = (ULONG_PTR)lphi->iCtrlId;
  1376. rgdw[1] = (ULONG_PTR)lphi->dwContextId;
  1377. WinHelp((HWND) lphi->hItemHandle, tchHelpFile, HELP_WM_HELP,
  1378. (ULONG_PTR) &rgdw);
  1379. return TRUE;
  1380. }
  1381. /****************************************************************************/
  1382. // Custom UI msg handler dealt with here
  1383. /****************************************************************************/
  1384. INT_PTR CALLBACK CustomUIDlg(HWND hwnd, UINT umsg, WPARAM wp, LPARAM lp)
  1385. {
  1386. static BOOL s_fServerNameChanged;
  1387. static BOOL s_fClusterNameChanged;
  1388. static BOOL s_fPreviousButtonState;
  1389. static BOOL s_fPreviousExposeIPState;
  1390. CTSSessionDirectory *pCTssd;
  1391. POLICY_TS_MACHINE gpolicy;
  1392. switch(umsg)
  1393. {
  1394. case WM_INITDIALOG:
  1395. {
  1396. BOOL bEnable = FALSE;
  1397. BOOL bExposeIP = FALSE;
  1398. pCTssd = (CTSSessionDirectory *)lp;
  1399. SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)pCTssd);
  1400. SendMessage(GetDlgItem(hwnd, IDC_EDIT_SERVERNAME),
  1401. EM_LIMITTEXT,
  1402. (WPARAM)64,
  1403. 0);
  1404. SendMessage(GetDlgItem(hwnd, IDC_EDIT_CLUSTERNAME),
  1405. EM_LIMITTEXT,
  1406. (WPARAM)64,
  1407. 0);
  1408. SendMessage(GetDlgItem(hwnd, IDC_EDIT_ACCOUNTNAME),
  1409. EM_LIMITTEXT,
  1410. (WPARAM)64,
  1411. 0);
  1412. SendMessage(GetDlgItem(hwnd, IDC_EDIT_PASSWORD),
  1413. EM_LIMITTEXT,
  1414. (WPARAM)64,
  1415. 0);
  1416. HICON hIcon;
  1417. hIcon = (HICON)LoadImage(
  1418. g_hInstance,
  1419. MAKEINTRESOURCE(IDI_SMALLWARN),
  1420. IMAGE_ICON,
  1421. 0,
  1422. 0,
  1423. 0);
  1424. // TRC1((TB, "CustomUIDlg - LoadImage returned 0x%p",hIcon));
  1425. SendMessage(
  1426. GetDlgItem(hwnd, IDC_WARNING_ICON),
  1427. STM_SETICON,
  1428. (WPARAM)hIcon,
  1429. (LPARAM)0
  1430. );
  1431. LONG lRet;
  1432. HKEY hKey;
  1433. DWORD cbData = 256;
  1434. TCHAR szString[256];
  1435. RegGetMachinePolicy(&gpolicy);
  1436. lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1437. REG_TS_CLUSTERSETTINGS,
  1438. 0,
  1439. KEY_READ | KEY_WRITE,
  1440. &hKey);
  1441. if (lRet == ERROR_SUCCESS)
  1442. {
  1443. lRet = RegQueryValueEx(hKey,
  1444. REG_TS_CLUSTER_STORESERVERNAME,
  1445. NULL,
  1446. NULL,
  1447. (LPBYTE)szString,
  1448. &cbData);
  1449. if (lRet == ERROR_SUCCESS)
  1450. {
  1451. SetWindowText(GetDlgItem(hwnd, IDC_EDIT_SERVERNAME),
  1452. szString);
  1453. }
  1454. cbData = 256;
  1455. lRet = RegQueryValueEx(hKey,
  1456. REG_TS_CLUSTER_CLUSTERNAME,
  1457. NULL,
  1458. NULL,
  1459. (LPBYTE)szString,
  1460. &cbData);
  1461. if (lRet == ERROR_SUCCESS)
  1462. {
  1463. SetWindowText(GetDlgItem(hwnd, IDC_EDIT_CLUSTERNAME),
  1464. szString);
  1465. }
  1466. RegCloseKey(hKey);
  1467. }
  1468. else
  1469. {
  1470. if (pCTssd != NULL)
  1471. {
  1472. pCTssd->ErrorMessage(hwnd, IDS_ERROR_TEXT, (DWORD)lRet);
  1473. }
  1474. EndDialog(hwnd, lRet);
  1475. }
  1476. if (gpolicy.fPolicySessionDirectoryActive)
  1477. {
  1478. bEnable = gpolicy.SessionDirectoryActive;
  1479. EnableWindow(GetDlgItem(hwnd, IDC_CHECK_ENABLE), FALSE);
  1480. }
  1481. else
  1482. {
  1483. if (pCTssd != NULL)
  1484. bEnable = pCTssd->IsSessionDirectoryEnabled();
  1485. }
  1486. s_fPreviousButtonState = bEnable;
  1487. CheckDlgButton(hwnd, IDC_CHECK_ENABLE, bEnable);
  1488. if (gpolicy.fPolicySessionDirectoryLocation)
  1489. {
  1490. SetWindowText(GetDlgItem(hwnd, IDC_EDIT_SERVERNAME),
  1491. gpolicy.SessionDirectoryLocation);
  1492. EnableWindow(GetDlgItem(hwnd, IDC_EDIT_SERVERNAME), FALSE);
  1493. EnableWindow(GetDlgItem(hwnd, IDC_STATIC_STORENAME), FALSE);
  1494. }
  1495. else
  1496. {
  1497. EnableWindow(GetDlgItem(hwnd, IDC_EDIT_SERVERNAME), bEnable);
  1498. EnableWindow(GetDlgItem(hwnd, IDC_STATIC_STORENAME), bEnable);
  1499. }
  1500. if (gpolicy.fPolicySessionDirectoryClusterName != 0)
  1501. {
  1502. SetWindowText(GetDlgItem(hwnd, IDC_EDIT_CLUSTERNAME),
  1503. gpolicy.SessionDirectoryClusterName);
  1504. EnableWindow(GetDlgItem(hwnd, IDC_EDIT_CLUSTERNAME), FALSE);
  1505. EnableWindow(GetDlgItem(hwnd, IDC_STATIC_CLUSTERNAME),FALSE);
  1506. }
  1507. else
  1508. {
  1509. EnableWindow(GetDlgItem(hwnd, IDC_EDIT_CLUSTERNAME), bEnable);
  1510. EnableWindow(GetDlgItem(hwnd, IDC_STATIC_CLUSTERNAME),bEnable);
  1511. }
  1512. if (gpolicy.fPolicySessionDirectoryExposeServerIP != 0)
  1513. {
  1514. CheckDlgButton(hwnd, IDC_CHECK_EXPOSEIP, TRUE);
  1515. EnableWindow(GetDlgItem(hwnd, IDC_CHECK_EXPOSEIP), FALSE);
  1516. s_fPreviousExposeIPState = TRUE;
  1517. }
  1518. else
  1519. {
  1520. if (pCTssd != NULL)
  1521. {
  1522. bExposeIP =
  1523. pCTssd->IsSessionDirectoryExposeServerIPEnabled();
  1524. }
  1525. CheckDlgButton(hwnd, IDC_CHECK_EXPOSEIP, bExposeIP ?
  1526. BST_CHECKED : BST_UNCHECKED);
  1527. EnableWindow(GetDlgItem(hwnd, IDC_CHECK_EXPOSEIP), bEnable);
  1528. s_fPreviousExposeIPState = bExposeIP;
  1529. }
  1530. s_fServerNameChanged = FALSE;
  1531. s_fClusterNameChanged = FALSE;
  1532. }
  1533. break;
  1534. case WM_HELP:
  1535. OnHelp(hwnd, (LPHELPINFO)lp);
  1536. break;
  1537. case WM_COMMAND:
  1538. if (LOWORD(wp) == IDCANCEL)
  1539. {
  1540. EndDialog(hwnd, 0);
  1541. }
  1542. else if (LOWORD(wp) == IDOK)
  1543. {
  1544. BOOL bEnabled;
  1545. BOOL bExposeIP;
  1546. DWORD dwRetStatus = 0;
  1547. pCTssd = (CTSSessionDirectory *) GetWindowLongPtr(hwnd,
  1548. DWLP_USER);
  1549. bEnabled = (IsDlgButtonChecked(hwnd, IDC_CHECK_ENABLE) ==
  1550. BST_CHECKED);
  1551. bExposeIP = (IsDlgButtonChecked(hwnd, IDC_CHECK_EXPOSEIP) ==
  1552. BST_CHECKED);
  1553. if (bEnabled != s_fPreviousButtonState)
  1554. {
  1555. DWORD dwStatus;
  1556. dwStatus = pCTssd->SetSessionDirectoryEnabledState(
  1557. bEnabled);
  1558. if (dwStatus != ERROR_SUCCESS)
  1559. {
  1560. return 0;
  1561. }
  1562. dwRetStatus = UPDATE_TERMSRV_SESSDIR;
  1563. }
  1564. if (bExposeIP != s_fPreviousExposeIPState)
  1565. {
  1566. DWORD dwStatus;
  1567. dwStatus = pCTssd->SetSessionDirectoryExposeIPState(
  1568. bExposeIP);
  1569. if (dwStatus != ERROR_SUCCESS)
  1570. {
  1571. return 0;
  1572. }
  1573. dwRetStatus = UPDATE_TERMSRV_SESSDIR;
  1574. }
  1575. if (s_fServerNameChanged || s_fClusterNameChanged)
  1576. {
  1577. HKEY hKey;
  1578. LONG lRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  1579. REG_TS_CLUSTERSETTINGS,
  1580. 0,
  1581. KEY_READ | KEY_WRITE,
  1582. &hKey);
  1583. if (lRet == ERROR_SUCCESS)
  1584. {
  1585. TCHAR szName[64];
  1586. if (s_fServerNameChanged)
  1587. {
  1588. if (GetWindowText(GetDlgItem(hwnd,
  1589. IDC_EDIT_SERVERNAME), szName,
  1590. sizeof(szName) / sizeof(TCHAR)) != 0)
  1591. {
  1592. if (!IsServerNameValid(szName))
  1593. {
  1594. int nRet;
  1595. TCHAR szError[256];
  1596. TCHAR szTitle[80];
  1597. TRC1((TB,"Server name was not valid"));
  1598. LoadString(g_hInstance,
  1599. IDS_ERROR_SDIRLOC,
  1600. szError,
  1601. sizeof(szError)/sizeof(TCHAR));
  1602. LoadString(g_hInstance,
  1603. IDS_ERROR_TITLE,
  1604. szTitle,
  1605. sizeof(szTitle)/sizeof(TCHAR));
  1606. nRet = MessageBox(hwnd, szError, szTitle,
  1607. MB_YESNO | MB_ICONWARNING);
  1608. if (nRet == IDNO)
  1609. {
  1610. SetFocus(GetDlgItem(hwnd,
  1611. IDC_EDIT_SERVERNAME));
  1612. return 0;
  1613. }
  1614. }
  1615. }
  1616. else {
  1617. // Blank name not allowed if session directory
  1618. // is enabled. This code will not be run if the
  1619. // checkbox is disabled because when it is
  1620. // disabled the static flags get set to
  1621. // disabled.
  1622. TCHAR szError[256];
  1623. TCHAR szTitle[80];
  1624. LoadString(g_hInstance, IDS_ERROR_TITLE,
  1625. szTitle, sizeof(szTitle) /
  1626. sizeof(TCHAR));
  1627. LoadString(g_hInstance, IDS_ERROR_SDIREMPTY,
  1628. szError, sizeof(szError) /
  1629. sizeof(TCHAR));
  1630. MessageBox(hwnd, szError, szTitle,
  1631. MB_OK | MB_ICONWARNING);
  1632. SetFocus(GetDlgItem(hwnd,
  1633. IDC_EDIT_SERVERNAME));
  1634. return 0;
  1635. }
  1636. RegSetValueEx(hKey,
  1637. REG_TS_CLUSTER_STORESERVERNAME,
  1638. 0,
  1639. REG_SZ,
  1640. (CONST LPBYTE) szName,
  1641. sizeof(szName) - sizeof(TCHAR));
  1642. }
  1643. if (s_fClusterNameChanged)
  1644. {
  1645. GetWindowText(GetDlgItem(hwnd,
  1646. IDC_EDIT_CLUSTERNAME), szName,
  1647. sizeof(szName) / sizeof(TCHAR));
  1648. RegSetValueEx(hKey,
  1649. REG_TS_CLUSTER_CLUSTERNAME,
  1650. 0,
  1651. REG_SZ,
  1652. (CONST LPBYTE) szName,
  1653. sizeof(szName) - sizeof(TCHAR));
  1654. }
  1655. RegCloseKey(hKey);
  1656. }
  1657. else
  1658. {
  1659. pCTssd->ErrorMessage(hwnd, IDS_ERROR_TEXT2,
  1660. (DWORD) lRet);
  1661. return 0;
  1662. }
  1663. dwRetStatus = UPDATE_TERMSRV_SESSDIR;
  1664. }
  1665. EndDialog(hwnd, dwRetStatus);
  1666. }
  1667. else
  1668. {
  1669. switch(HIWORD(wp))
  1670. {
  1671. case EN_CHANGE:
  1672. if (LOWORD(wp) == IDC_EDIT_SERVERNAME)
  1673. {
  1674. s_fServerNameChanged = TRUE;
  1675. }
  1676. else if (LOWORD(wp) == IDC_EDIT_CLUSTERNAME)
  1677. {
  1678. s_fClusterNameChanged = TRUE;
  1679. }
  1680. break;
  1681. case BN_CLICKED:
  1682. if (LOWORD(wp) == IDC_CHECK_ENABLE)
  1683. {
  1684. BOOL bEnable;
  1685. bEnable = (IsDlgButtonChecked(hwnd, IDC_CHECK_ENABLE) ==
  1686. BST_CHECKED ? TRUE : FALSE);
  1687. // set flags
  1688. s_fServerNameChanged = bEnable;
  1689. s_fClusterNameChanged = bEnable;
  1690. RegGetMachinePolicy(&gpolicy);
  1691. if (gpolicy.fPolicySessionDirectoryLocation)
  1692. {
  1693. EnableWindow(GetDlgItem(hwnd, IDC_EDIT_SERVERNAME), FALSE);
  1694. EnableWindow(GetDlgItem(hwnd, IDC_STATIC_STORENAME), FALSE);
  1695. }
  1696. else
  1697. {
  1698. EnableWindow(GetDlgItem(hwnd, IDC_EDIT_SERVERNAME), bEnable);
  1699. EnableWindow(GetDlgItem(hwnd, IDC_STATIC_STORENAME), bEnable);
  1700. }
  1701. if (gpolicy.fPolicySessionDirectoryClusterName)
  1702. {
  1703. EnableWindow(GetDlgItem(hwnd, IDC_EDIT_CLUSTERNAME), FALSE);
  1704. EnableWindow(GetDlgItem(hwnd, IDC_STATIC_CLUSTERNAME),FALSE);
  1705. }
  1706. else
  1707. {
  1708. EnableWindow(GetDlgItem(hwnd, IDC_EDIT_CLUSTERNAME), bEnable);
  1709. EnableWindow(GetDlgItem(hwnd, IDC_STATIC_CLUSTERNAME),bEnable);
  1710. }
  1711. EnableWindow(GetDlgItem(hwnd, IDC_CHECK_EXPOSEIP),
  1712. bEnable);
  1713. }
  1714. break;
  1715. }
  1716. }
  1717. break;
  1718. }
  1719. return 0;
  1720. }
  1721. #pragma warning (pop)