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.

700 lines
19 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1999 - 1999
  6. //
  7. // File: objpick.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. // objpick.cpp: implementation of the CGetUser class and the
  11. // CGetComputer class using the object picker
  12. //
  13. //////////////////////////////////////////////////////////////////////
  14. #include "stdafx.h"
  15. #include "objpick.h"
  16. #include <iads.h>
  17. #include <iadsp.h> // IADsPathname
  18. #include <objsel.h>
  19. #include <adshlp.h>
  20. #include "objplus.h"
  21. #include "ipaddres.h"
  22. #ifdef _DEBUG
  23. #undef THIS_FILE
  24. static char THIS_FILE[]=__FILE__;
  25. #define new DEBUG_NEW
  26. #endif
  27. #define BREAK_ON_FAIL_HRESULT(hr) \
  28. if (FAILED(hr)) { Trace2("line %u err 0x%x\n", __LINE__, hr); break; }
  29. UINT g_cfDsObjectPicker = RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST);
  30. HRESULT InitObjectPickerForGroups(IDsObjectPicker *pDsObjectPicker, BOOL fMultiselect);
  31. HRESULT InitObjectPickerForComputers(IDsObjectPicker *pDsObjectPicker);
  32. DWORD ObjPickGetHostName(DWORD dwIpAddr, CString & strHostName);
  33. DWORD ObjPickNameOrIpToHostname(CString & strNameOrIp, CString & strHostName);
  34. //////////////////////////////////////////////////////////////////////
  35. // CGetUsers Class
  36. //////////////////////////////////////////////////////////////////////
  37. //////////////////////////////////////////////////////////////////////
  38. // Construction/Destruction
  39. //////////////////////////////////////////////////////////////////////
  40. void
  41. FormatName(LPCTSTR pszFullName, LPCTSTR pszDomainName, CString & strDisplay)
  42. {
  43. strDisplay.Format(_T("%s (%s)"), pszFullName, pszDomainName);
  44. }
  45. CGetUsers::CGetUsers(BOOL fMultiselect)
  46. {
  47. m_fMultiselect = fMultiselect;
  48. }
  49. CGetUsers::~CGetUsers()
  50. {
  51. }
  52. BOOL
  53. CGetUsers::GetUsers(HWND hwndParent)
  54. {
  55. HRESULT hr = S_OK;
  56. IDsObjectPicker * pDsObjectPicker = NULL;
  57. IDataObject * pdo = NULL;
  58. BOOL fSuccess = TRUE;
  59. hr = CoInitialize(NULL);
  60. if (FAILED(hr))
  61. return FALSE;
  62. do
  63. {
  64. //
  65. // Create an instance of the object picker. The implementation in
  66. // objsel.dll is apartment model.
  67. //
  68. hr = CoCreateInstance(CLSID_DsObjectPicker,
  69. NULL,
  70. CLSCTX_INPROC_SERVER,
  71. IID_IDsObjectPicker,
  72. (void **) &pDsObjectPicker);
  73. BREAK_ON_FAIL_HRESULT(hr);
  74. hr = InitObjectPickerForGroups(pDsObjectPicker, m_fMultiselect);
  75. //
  76. // Invoke the modal dialog.
  77. //
  78. hr = pDsObjectPicker->InvokeDialog(hwndParent, &pdo);
  79. BREAK_ON_FAIL_HRESULT(hr);
  80. //
  81. // If the user hit Cancel, hr == S_FALSE
  82. //
  83. if (hr == S_FALSE)
  84. {
  85. Trace0("User canceled object picker dialog\n");
  86. fSuccess = FALSE;
  87. break;
  88. }
  89. //
  90. // Process the user's selections
  91. //
  92. Assert(pdo);
  93. ProcessSelectedObjects(pdo);
  94. pdo->Release();
  95. pdo = NULL;
  96. } while (0);
  97. if (pDsObjectPicker)
  98. {
  99. pDsObjectPicker->Release();
  100. }
  101. CoUninitialize();
  102. if (FAILED(hr))
  103. fSuccess = FALSE;
  104. return fSuccess;
  105. }
  106. void
  107. CGetUsers::ProcessSelectedObjects(IDataObject *pdo)
  108. {
  109. HRESULT hr = S_OK;
  110. STGMEDIUM stgmedium =
  111. {
  112. TYMED_HGLOBAL,
  113. NULL,
  114. NULL
  115. };
  116. FORMATETC formatetc =
  117. {
  118. (CLIPFORMAT)g_cfDsObjectPicker,
  119. NULL,
  120. DVASPECT_CONTENT,
  121. -1,
  122. TYMED_HGLOBAL
  123. };
  124. BOOL fGotStgMedium = FALSE;
  125. do
  126. {
  127. hr = pdo->GetData(&formatetc, &stgmedium);
  128. BREAK_ON_FAIL_HRESULT(hr);
  129. fGotStgMedium = TRUE;
  130. PDS_SELECTION_LIST pDsSelList =
  131. (PDS_SELECTION_LIST) GlobalLock(stgmedium.hGlobal);
  132. if (!pDsSelList)
  133. {
  134. Trace1("GlobalLock error %u\n", GetLastError());
  135. break;
  136. }
  137. // create the path name thing
  138. CComPtr<IADsPathname> spIADsPathname;
  139. hr = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER,
  140. IID_IADsPathname, (PVOID *)&spIADsPathname);
  141. BREAK_ON_FAIL_HRESULT(hr);
  142. hr = spIADsPathname->SetDisplayType( ADS_DISPLAY_VALUE_ONLY );
  143. for (UINT nCount = 0; nCount < pDsSelList->cItems; nCount++)
  144. {
  145. DS_SELECTION * pDsSel = &(pDsSelList->aDsSelection[nCount]);
  146. Assert(NULL != pDsSel);
  147. LPWSTR pwzADsPath = pDsSel->pwzADsPath;
  148. Assert( NULL != pwzADsPath );
  149. hr = spIADsPathname->Set( pwzADsPath, ADS_SETTYPE_FULL );
  150. if (FAILED(hr))
  151. continue;
  152. long lnNumPathElements = 0;
  153. hr = spIADsPathname->GetNumElements( &lnNumPathElements );
  154. if (FAILED(hr))
  155. continue;
  156. CComBSTR sbstrUser, sbstrDomain;
  157. hr = spIADsPathname->GetElement( 0, &sbstrUser );
  158. if (FAILED(hr))
  159. continue;
  160. switch (lnNumPathElements)
  161. {
  162. case 1:
  163. hr = spIADsPathname->Retrieve( ADS_FORMAT_SERVER, &sbstrDomain );
  164. break;
  165. case 2: // nt4, nt5 domain
  166. case 3: // local domain
  167. hr = spIADsPathname->GetElement( 1, &sbstrDomain );
  168. break;
  169. default:
  170. Assert(FALSE);
  171. hr = E_FAIL;
  172. }
  173. if (FAILED(hr))
  174. continue;
  175. CUserInfo userTemp;
  176. CString strDomain;
  177. strDomain = sbstrDomain;
  178. strDomain.MakeUpper();
  179. if (pDsSel->pvarFetchedAttributes[0].vt == VT_EMPTY)
  180. userTemp.m_strFullName = pDsSel->pwzName;
  181. else
  182. userTemp.m_strFullName = V_BSTR(&(pDsSel->pvarFetchedAttributes[0]));
  183. userTemp.m_strName.Format(L"%s\\%s", strDomain, sbstrUser);
  184. Add(userTemp);
  185. }
  186. GlobalUnlock(stgmedium.hGlobal);
  187. } while (0);
  188. if (fGotStgMedium)
  189. {
  190. ReleaseStgMedium(&stgmedium);
  191. }
  192. }
  193. //////////////////////////////////////////////////////////////////////
  194. // CGetComputer Class
  195. //////////////////////////////////////////////////////////////////////
  196. //////////////////////////////////////////////////////////////////////
  197. // Construction/Destruction
  198. //////////////////////////////////////////////////////////////////////
  199. CGetComputer::CGetComputer()
  200. {
  201. }
  202. CGetComputer::~CGetComputer()
  203. {
  204. }
  205. BOOL
  206. CGetComputer::GetComputer(HWND hwndParent)
  207. {
  208. HRESULT hr = S_OK;
  209. IDsObjectPicker * pDsObjectPicker = NULL;
  210. IDataObject * pdo = NULL;
  211. BOOL fSuccess = TRUE;
  212. hr = CoInitialize(NULL);
  213. if (FAILED(hr))
  214. return FALSE;
  215. do
  216. {
  217. //
  218. // Create an instance of the object picker. The implementation in
  219. // objsel.dll is apartment model.
  220. //
  221. hr = CoCreateInstance(CLSID_DsObjectPicker,
  222. NULL,
  223. CLSCTX_INPROC_SERVER,
  224. IID_IDsObjectPicker,
  225. (void **) &pDsObjectPicker);
  226. BREAK_ON_FAIL_HRESULT(hr);
  227. //
  228. // Reinitialize the object picker to choose computers
  229. //
  230. hr = InitObjectPickerForComputers(pDsObjectPicker);
  231. BREAK_ON_FAIL_HRESULT(hr);
  232. //
  233. // Now pick a computer
  234. //
  235. hr = pDsObjectPicker->InvokeDialog(hwndParent, &pdo);
  236. BREAK_ON_FAIL_HRESULT(hr);
  237. //
  238. // If the user hit Cancel, hr == S_FALSE
  239. //
  240. if (hr == S_FALSE)
  241. {
  242. Trace0("User canceled object picker dialog\n");
  243. fSuccess = FALSE;
  244. break;
  245. }
  246. Assert(pdo);
  247. ProcessSelectedObjects(pdo);
  248. pdo->Release();
  249. pdo = NULL;
  250. } while (0);
  251. if (pDsObjectPicker)
  252. {
  253. pDsObjectPicker->Release();
  254. }
  255. CoUninitialize();
  256. if (FAILED(hr))
  257. fSuccess = FALSE;
  258. return fSuccess;
  259. }
  260. void
  261. CGetComputer::ProcessSelectedObjects(IDataObject *pdo)
  262. {
  263. HRESULT hr = S_OK;
  264. STGMEDIUM stgmedium =
  265. {
  266. TYMED_HGLOBAL,
  267. NULL,
  268. NULL
  269. };
  270. FORMATETC formatetc =
  271. {
  272. (CLIPFORMAT)g_cfDsObjectPicker,
  273. NULL,
  274. DVASPECT_CONTENT,
  275. -1,
  276. TYMED_HGLOBAL
  277. };
  278. BOOL fGotStgMedium = FALSE;
  279. do
  280. {
  281. hr = pdo->GetData(&formatetc, &stgmedium);
  282. BREAK_ON_FAIL_HRESULT(hr);
  283. fGotStgMedium = TRUE;
  284. PDS_SELECTION_LIST pDsSelList =
  285. (PDS_SELECTION_LIST) GlobalLock(stgmedium.hGlobal);
  286. if (!pDsSelList)
  287. {
  288. Trace1("GlobalLock error %u\n", GetLastError());
  289. break;
  290. }
  291. CString strTemp = pDsSelList->aDsSelection[0].pwzName;
  292. if (strTemp.Left(2) == _T("\\\\"))
  293. strTemp = pDsSelList->aDsSelection[0].pwzName[2];
  294. if (ERROR_SUCCESS != ObjPickNameOrIpToHostname(strTemp, m_strComputerName))
  295. {
  296. //we use the name from the object picker if we failed to convert it into hostname
  297. m_strComputerName = strTemp;
  298. }
  299. GlobalUnlock(stgmedium.hGlobal);
  300. } while (0);
  301. if (fGotStgMedium)
  302. {
  303. ReleaseStgMedium(&stgmedium);
  304. }
  305. }
  306. //+--------------------------------------------------------------------------
  307. //
  308. // Function: InitObjectPickerForGroups
  309. //
  310. // Synopsis: Call IDsObjectPicker::Initialize with arguments that will
  311. // set it to allow the user to pick one or more groups.
  312. //
  313. // Arguments: [pDsObjectPicker] - object picker interface instance
  314. //
  315. // Returns: Result of calling IDsObjectPicker::Initialize.
  316. //
  317. // History: 10-14-1998 DavidMun Created
  318. //
  319. //---------------------------------------------------------------------------
  320. HRESULT
  321. InitObjectPickerForGroups(IDsObjectPicker *pDsObjectPicker, BOOL fMultiselect)
  322. {
  323. //
  324. // Prepare to initialize the object picker.
  325. // Set up the array of scope initializer structures.
  326. //
  327. static const int SCOPE_INIT_COUNT = 5;
  328. DSOP_SCOPE_INIT_INFO aScopeInit[SCOPE_INIT_COUNT];
  329. ZeroMemory(aScopeInit, sizeof(DSOP_SCOPE_INIT_INFO) * SCOPE_INIT_COUNT);
  330. //
  331. // Target computer scope. This adds a "Look In" entry for the
  332. // target computer. Computer scopes are always treated as
  333. // downlevel (i.e., they use the WinNT provider).
  334. //
  335. aScopeInit[0].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  336. aScopeInit[0].flType = DSOP_SCOPE_TYPE_TARGET_COMPUTER;
  337. aScopeInit[0].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE | DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT | DSOP_SCOPE_FLAG_WANT_DOWNLEVEL_BUILTIN_PATH;
  338. aScopeInit[0].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_USERS | DSOP_DOWNLEVEL_FILTER_NETWORK_SERVICE;
  339. //
  340. // The domain to which the target computer is joined. Note we're
  341. // combining two scope types into flType here for convenience.
  342. //
  343. aScopeInit[1].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  344. aScopeInit[1].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN
  345. | DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN;
  346. aScopeInit[1].FilterFlags.Uplevel.flNativeModeOnly = DSOP_FILTER_USERS;
  347. aScopeInit[1].FilterFlags.Uplevel.flMixedModeOnly = DSOP_FILTER_USERS;
  348. aScopeInit[1].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_USERS;
  349. aScopeInit[1].flScope = DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT;
  350. //
  351. // The domains in the same forest (enterprise) as the domain to which
  352. // the target machine is joined. Note these can only be DS-aware
  353. //
  354. aScopeInit[2].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  355. aScopeInit[2].flType = DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN;
  356. aScopeInit[2].FilterFlags.Uplevel.flNativeModeOnly = DSOP_FILTER_USERS;
  357. aScopeInit[2].FilterFlags.Uplevel.flMixedModeOnly = DSOP_FILTER_USERS;
  358. aScopeInit[2].flScope = DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT;
  359. //
  360. // Domains external to the enterprise but trusted directly by the
  361. // domain to which the target machine is joined.
  362. //
  363. // If the target machine is joined to an NT4 domain, only the
  364. // external downlevel domain scope applies, and it will cause
  365. // all domains trusted by the joined domain to appear.
  366. //
  367. aScopeInit[3].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  368. aScopeInit[3].flType =
  369. DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN
  370. | DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN;
  371. aScopeInit[3].FilterFlags.Uplevel.flNativeModeOnly = DSOP_FILTER_USERS;
  372. aScopeInit[3].FilterFlags.Uplevel.flMixedModeOnly = DSOP_FILTER_USERS;
  373. aScopeInit[3].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_USERS;
  374. aScopeInit[3].flScope = DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT;
  375. //
  376. // The Global Catalog
  377. //
  378. aScopeInit[4].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  379. aScopeInit[4].flScope = DSOP_SCOPE_FLAG_WANT_PROVIDER_WINNT;
  380. aScopeInit[4].flType = DSOP_SCOPE_TYPE_GLOBAL_CATALOG;
  381. // Only native mode applies to gc scope.
  382. aScopeInit[4].FilterFlags.Uplevel.flNativeModeOnly = DSOP_FILTER_USERS;
  383. //
  384. // Put the scope init array into the object picker init array
  385. //
  386. DSOP_INIT_INFO InitInfo;
  387. ZeroMemory(&InitInfo, sizeof(InitInfo));
  388. InitInfo.cbSize = sizeof(InitInfo);
  389. //
  390. // The pwzTargetComputer member allows the object picker to be
  391. // retargetted to a different computer. It will behave as if it
  392. // were being run ON THAT COMPUTER.
  393. //
  394. InitInfo.pwzTargetComputer = NULL; // NULL == local machine
  395. InitInfo.cDsScopeInfos = SCOPE_INIT_COUNT;
  396. InitInfo.aDsScopeInfos = aScopeInit;
  397. InitInfo.flOptions = (fMultiselect) ? DSOP_FLAG_MULTISELECT : 0;
  398. LPCTSTR attrs[] = {_T("FullName")};
  399. InitInfo.cAttributesToFetch = 1;
  400. InitInfo.apwzAttributeNames = attrs;
  401. //
  402. // Note object picker makes its own copy of InitInfo. Also note
  403. // that Initialize may be called multiple times, last call wins.
  404. //
  405. HRESULT hr = pDsObjectPicker->Initialize(&InitInfo);
  406. if (FAILED(hr))
  407. {
  408. ULONG i;
  409. for (i = 0; i < SCOPE_INIT_COUNT; i++)
  410. {
  411. if (FAILED(InitInfo.aDsScopeInfos[i].hr))
  412. {
  413. printf("Initialization failed because of scope %u\n", i);
  414. }
  415. }
  416. }
  417. return hr;
  418. }
  419. //+--------------------------------------------------------------------------
  420. //
  421. // Function: InitObjectPickerForComputers
  422. //
  423. // Synopsis: Call IDsObjectPicker::Initialize with arguments that will
  424. // set it to allow the user to pick a single computer object.
  425. //
  426. // Arguments: [pDsObjectPicker] - object picker interface instance
  427. //
  428. // Returns: Result of calling IDsObjectPicker::Initialize.
  429. //
  430. // History: 10-14-1998 DavidMun Created
  431. //
  432. //---------------------------------------------------------------------------
  433. HRESULT
  434. InitObjectPickerForComputers(IDsObjectPicker *pDsObjectPicker)
  435. {
  436. //
  437. // Prepare to initialize the object picker.
  438. // Set up the array of scope initializer structures.
  439. //
  440. static const int SCOPE_INIT_COUNT = 2;
  441. DSOP_SCOPE_INIT_INFO aScopeInit[SCOPE_INIT_COUNT];
  442. ZeroMemory(aScopeInit, sizeof(DSOP_SCOPE_INIT_INFO) * SCOPE_INIT_COUNT);
  443. //
  444. // Build a scope init struct for everything except the joined domain.
  445. //
  446. aScopeInit[0].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  447. aScopeInit[0].flType = DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN
  448. | DSOP_SCOPE_TYPE_GLOBAL_CATALOG
  449. | DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN
  450. | DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN
  451. | DSOP_SCOPE_TYPE_WORKGROUP
  452. | DSOP_SCOPE_TYPE_USER_ENTERED_UPLEVEL_SCOPE
  453. | DSOP_SCOPE_TYPE_USER_ENTERED_DOWNLEVEL_SCOPE;
  454. aScopeInit[0].FilterFlags.Uplevel.flBothModes =
  455. DSOP_FILTER_COMPUTERS;
  456. aScopeInit[0].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_COMPUTERS;
  457. //
  458. // scope for the joined domain, make it the default
  459. //
  460. aScopeInit[1].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  461. aScopeInit[1].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN
  462. | DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN;
  463. aScopeInit[1].FilterFlags.Uplevel.flBothModes =
  464. DSOP_FILTER_COMPUTERS;
  465. aScopeInit[1].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_COMPUTERS;
  466. aScopeInit[1].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE;
  467. //
  468. // Put the scope init array into the object picker init array
  469. //
  470. DSOP_INIT_INFO InitInfo;
  471. ZeroMemory(&InitInfo, sizeof(InitInfo));
  472. InitInfo.cbSize = sizeof(InitInfo);
  473. InitInfo.pwzTargetComputer = NULL; // NULL == local machine
  474. InitInfo.cDsScopeInfos = SCOPE_INIT_COUNT;
  475. InitInfo.aDsScopeInfos = aScopeInit;
  476. //
  477. // Note object picker makes its own copy of InitInfo. Also note
  478. // that Initialize may be called multiple times, last call wins.
  479. //
  480. return pDsObjectPicker->Initialize(&InitInfo);
  481. }
  482. //Use WinSock to the host name based on the ip address
  483. DWORD
  484. ObjPickGetHostName
  485. (
  486. DWORD dwIpAddr,
  487. CString & strHostName
  488. )
  489. {
  490. CString strName;
  491. //
  492. // Call the Winsock API to get host name information.
  493. //
  494. strHostName.Empty();
  495. u_long ulAddrInNetOrder = ::htonl( (u_long) dwIpAddr ) ;
  496. HOSTENT * pHostInfo = ::gethostbyaddr( (CHAR *) & ulAddrInNetOrder,
  497. sizeof ulAddrInNetOrder,
  498. PF_INET ) ;
  499. if ( pHostInfo == NULL )
  500. {
  501. return ::WSAGetLastError();
  502. }
  503. // copy the name
  504. LPTSTR pBuf = strName.GetBuffer(256);
  505. ZeroMemory(pBuf, 256);
  506. ::MultiByteToWideChar(CP_ACP,
  507. MB_PRECOMPOSED,
  508. pHostInfo->h_name,
  509. -1,
  510. pBuf,
  511. 256);
  512. strName.ReleaseBuffer();
  513. strName.MakeUpper();
  514. int nDot = strName.Find(_T("."));
  515. if (nDot != -1)
  516. strHostName = strName.Left(nDot);
  517. else
  518. strHostName = strName;
  519. return NOERROR;
  520. }
  521. //Convert any valid name of a machine (IP address, NetBios name or fully qualified DNS name)
  522. //to the host name
  523. DWORD ObjPickNameOrIpToHostname(CString & strNameOrIp, CString & strHostName)
  524. {
  525. DWORD dwErr = ERROR_SUCCESS;
  526. CString strTemp;
  527. CIpAddress ia(strNameOrIp);
  528. if (ia.IsValid())
  529. {
  530. dwErr = ObjPickGetHostName((LONG)ia, strTemp);
  531. }
  532. else
  533. {
  534. // just want the host name
  535. int nDot = strNameOrIp.Find('.');
  536. if (nDot != -1)
  537. {
  538. strTemp = strNameOrIp.Left(nDot);
  539. }
  540. else
  541. {
  542. strTemp = strNameOrIp;
  543. }
  544. }
  545. if (ERROR_SUCCESS == dwErr)
  546. {
  547. strHostName = strTemp;
  548. }
  549. return dwErr;
  550. }