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.

1463 lines
38 KiB

  1. #include "stdafx.h"
  2. #include <windns.h>
  3. #include "resource.h"
  4. #include "common.h"
  5. #include "remoteenv.h"
  6. #include "util.h"
  7. #include "balloon.h"
  8. #include <commctrl.h>
  9. #include <lm.h> // for NetXxx API
  10. #include <strsafe.h>
  11. BOOL EstablishNullSession(
  12. LPCTSTR Server, // in - server name
  13. BOOL bEstablish // in - TRUE=establish, FALSE=disconnect
  14. )
  15. {
  16. return EstablishSession(Server,_T(""),_T(""),_T(""),bEstablish);
  17. }
  18. BOOL EstablishSession(
  19. LPCTSTR Server, // in - server name
  20. LPTSTR Domain, // in - domain name for user credentials
  21. LPTSTR UserName, // in - username for credentials to use
  22. LPTSTR Password, // in - password for credentials
  23. BOOL bEstablish // in - TRUE=establish, FALSE=disconnect
  24. )
  25. {
  26. LPCTSTR szIpc = _T("\\IPC$");
  27. TCHAR RemoteResource[UNCLEN + 5 + 1]; // UNC len + \IPC$ + NULL
  28. DWORD cchServer;
  29. NET_API_STATUS nas;
  30. //
  31. // do not allow NULL or empty server name
  32. //
  33. if(Server == NULL || *Server == _T('\0'))
  34. {
  35. SetLastError(ERROR_INVALID_COMPUTERNAME);
  36. return FALSE;
  37. }
  38. cchServer = _tcslen( Server );
  39. if( Server[0] != _T('\\') && Server[1] != _T('\\'))
  40. {
  41. //
  42. // prepend slashes and NULL terminate
  43. //
  44. RemoteResource[0] = _T('\\');
  45. RemoteResource[1] = _T('\\');
  46. RemoteResource[2] = _T('\0');
  47. }
  48. else
  49. {
  50. cchServer -= 2; // drop slashes from count
  51. RemoteResource[0] = _T('\0');
  52. }
  53. if(cchServer > CNLEN)
  54. {
  55. SetLastError(ERROR_INVALID_COMPUTERNAME);
  56. return FALSE;
  57. }
  58. if (FAILED(StringCbCat(RemoteResource,sizeof(RemoteResource),Server)))
  59. {
  60. return FALSE;
  61. }
  62. if (FAILED(StringCbCat(RemoteResource,sizeof(RemoteResource),szIpc)))
  63. {
  64. return FALSE;
  65. }
  66. //
  67. // disconnect or connect to the resource, based on bEstablish
  68. //
  69. if(bEstablish)
  70. {
  71. USE_INFO_2 ui2;
  72. DWORD errParm;
  73. ZeroMemory(&ui2, sizeof(ui2));
  74. ui2.ui2_local = NULL;
  75. ui2.ui2_remote = RemoteResource;
  76. ui2.ui2_asg_type = USE_IPC;
  77. ui2.ui2_domainname = Domain;
  78. ui2.ui2_username = UserName;
  79. ui2.ui2_password = Password;
  80. // try establishing session for one minute
  81. // if computer is not accepting any more connections
  82. for (int i = 0; i < (60000 / 5000); i++)
  83. {
  84. nas = NetUseAdd(NULL, 2, (LPBYTE)&ui2, &errParm);
  85. if (nas != ERROR_REQ_NOT_ACCEP)
  86. {
  87. break;
  88. }
  89. Sleep(5000);
  90. }
  91. }
  92. else
  93. {
  94. nas = NetUseDel(NULL, RemoteResource, 0);
  95. }
  96. if( nas == NERR_Success )
  97. {
  98. return TRUE; // indicate success
  99. }
  100. SetLastError(nas);
  101. return FALSE;
  102. }
  103. BOOL IsValidMetabasePath(LPCTSTR lpszMDPath)
  104. {
  105. BOOL bReturn = FALSE;
  106. CString strNewPath;
  107. CString strRemainder;
  108. LPCTSTR lpPath = CMetabasePath::GetMachinePath(lpszMDPath, strNewPath, &strRemainder);
  109. if (lpPath && !strNewPath.IsEmpty())
  110. {
  111. if (0 == _tcscmp(strNewPath,_T("/LM")))
  112. {
  113. bReturn = TRUE;
  114. }
  115. }
  116. return bReturn;
  117. }
  118. BOOL IsRootVDir(IN LPCTSTR lpszMDPath)
  119. /*++
  120. Routine Description:
  121. Arguments:
  122. LPCTSTR lpszMDPath : Metabase path.
  123. Return Value:
  124. TRUE if the path is
  125. LM/W3SVC/1/ROOT
  126. FALSE otherwise.
  127. --*/
  128. {
  129. BOOL bReturn = FALSE;
  130. if (!lpszMDPath || !*lpszMDPath)
  131. {
  132. return bReturn;
  133. }
  134. CString strSiteNode;
  135. CString strRemainder;
  136. LPCTSTR lpPath = CMetabasePath::TruncatePath(3, lpszMDPath, strSiteNode, &strRemainder);
  137. if (lpPath && !strSiteNode.IsEmpty())
  138. {
  139. if (0 == strRemainder.CompareNoCase(_T("ROOT")))
  140. {
  141. return TRUE;
  142. }
  143. }
  144. return bReturn;
  145. }
  146. // Clean the metabase path
  147. // make sure it has no beginning / and no ending /
  148. BOOL CleanMetaPath(LPTSTR *pszPathToClean,DWORD *pcbPathToCleanSize)
  149. {
  150. BOOL bRet = FALSE;
  151. if (!pszPathToClean || !*pszPathToClean)
  152. {
  153. return FALSE;
  154. }
  155. __try
  156. {
  157. // loop thru the string and change all '\\' to '/'
  158. for (int i = 0; i < (int) _tcslen(*pszPathToClean); i++)
  159. {
  160. if ('\\' == (*pszPathToClean)[i])
  161. {
  162. (*pszPathToClean)[i] = '/';
  163. }
  164. }
  165. if (0 == _tcscmp(*pszPathToClean,_T("/")))
  166. {
  167. // if it's one single slash
  168. // then just return the slash.
  169. }
  170. else
  171. {
  172. // Check if the string ends with a '/'
  173. if ('/' == (*pszPathToClean)[_tcslen(*pszPathToClean) - 1])
  174. {
  175. // cut it off
  176. (*pszPathToClean)[_tcslen(*pszPathToClean) - 1] = '\0';
  177. }
  178. // Check if the starts with a '/'
  179. if ('/' == (*pszPathToClean)[0])
  180. {
  181. if ((*pszPathToClean)[1])
  182. {
  183. // Get rid of it
  184. StringCbCopy(*pszPathToClean, *pcbPathToCleanSize, &(*pszPathToClean)[1]);
  185. }
  186. }
  187. }
  188. bRet = TRUE;
  189. }
  190. __except (EXCEPTION_EXECUTE_HANDLER)
  191. {
  192. // lame
  193. }
  194. return bRet;
  195. }
  196. __inline int RECT_WIDTH(RECT rc) { return rc.right - rc.left; };
  197. __inline int RECT_HEIGHT(RECT rc) { return rc.bottom - rc.top; };
  198. __inline int RECT_WIDTH(const RECT* prc) { return prc->right - prc->left; };
  199. __inline int RECT_HEIGHT(const RECT* prc) { return prc->bottom - prc->top; };
  200. void CenterWindow(HWND hwndParent, HWND hwnd)
  201. {
  202. RECT rcParent, rc;
  203. if (!hwndParent)
  204. hwndParent = GetDesktopWindow();
  205. GetWindowRect(hwndParent, &rcParent);
  206. GetWindowRect(hwnd, &rc);
  207. int cx = RECT_WIDTH(rc);
  208. int cy = RECT_HEIGHT(rc);
  209. int left = rcParent.left + (RECT_WIDTH(rcParent) - cx) / 2;
  210. int top = rcParent.top + (RECT_HEIGHT(rcParent) - cy) / 2;
  211. // Make certain we don't cover the tray
  212. SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0);
  213. if (left < rc.left)
  214. left = rc.left;
  215. if (top < rc.top)
  216. top = rc.top;
  217. MoveWindow(hwnd, left, top, cx, cy, TRUE);
  218. }
  219. // NOTE: this function only handles limited cases, e.g., no ip address
  220. BOOL IsLocalComputer(IN LPCTSTR lpszComputer)
  221. {
  222. if (!lpszComputer || !*lpszComputer)
  223. {
  224. return TRUE;
  225. }
  226. if ( _tcslen(lpszComputer) > 2 && *lpszComputer == _T('\\') && *(lpszComputer + 1) == _T('\\') )
  227. {
  228. lpszComputer += 2;
  229. }
  230. BOOL bReturn = FALSE;
  231. DWORD dwErr = 0;
  232. TCHAR szBuffer[DNS_MAX_NAME_BUFFER_LENGTH];
  233. DWORD dwSize = DNS_MAX_NAME_BUFFER_LENGTH;
  234. // 1st: compare against local Netbios computer name
  235. if ( !GetComputerNameEx(ComputerNameNetBIOS, szBuffer, &dwSize) )
  236. {
  237. dwErr = GetLastError();
  238. }
  239. else
  240. {
  241. bReturn = (0 == lstrcmpi(szBuffer, lpszComputer));
  242. if (!bReturn)
  243. {
  244. // 2nd: compare against local Dns computer name
  245. dwSize = DNS_MAX_NAME_BUFFER_LENGTH;
  246. if (GetComputerNameEx(ComputerNameDnsFullyQualified, szBuffer, &dwSize))
  247. {
  248. bReturn = (0 == lstrcmpi(szBuffer, lpszComputer));
  249. }
  250. else
  251. {
  252. dwErr = GetLastError();
  253. }
  254. }
  255. }
  256. if (dwErr)
  257. {
  258. //TRACE(_T("IsLocalComputer dwErr = %x\n"), dwErr);
  259. }
  260. return bReturn;
  261. }
  262. void GetFullPathLocalOrRemote(
  263. IN LPCTSTR lpszMachineName,
  264. IN LPCTSTR lpszDir,
  265. OUT CString& cstrPath
  266. )
  267. {
  268. ASSERT(lpszDir && *lpszDir);
  269. if (IsLocalComputer(lpszMachineName))
  270. {
  271. cstrPath = lpszDir;
  272. }
  273. else
  274. {
  275. // Check if it's already pointing to a share...
  276. if (*lpszDir == _T('\\') || *(lpszDir + 1) == _T('\\'))
  277. {
  278. cstrPath = lpszDir;
  279. }
  280. else
  281. {
  282. if (*lpszMachineName != _T('\\') || *(lpszMachineName + 1) != _T('\\'))
  283. {
  284. cstrPath = _T("\\\\");
  285. cstrPath += lpszMachineName;
  286. }
  287. else
  288. {
  289. cstrPath = lpszMachineName;
  290. }
  291. cstrPath += _T("\\");
  292. cstrPath += lpszDir;
  293. int i = cstrPath.Find(_T(':'));
  294. ASSERT(-1 != i);
  295. if (i != -1)
  296. {
  297. cstrPath.SetAt(i, _T('$'));
  298. }
  299. }
  300. }
  301. }
  302. BOOL GetInetsrvPath(LPCTSTR szMachineName,LPTSTR szReturnedPath,DWORD cbReturnedPathSize)
  303. {
  304. BOOL bReturn = FALSE;
  305. TCHAR szTempPath[_MAX_PATH];
  306. ZeroMemory(szTempPath,sizeof(szTempPath));
  307. // Determine if we are doing this on the local machine
  308. // or to a remote machine.
  309. if (IsLocalComputer(szMachineName))
  310. {
  311. // Get the local system32 directory.
  312. if (_MAX_PATH >= GetSystemDirectory(szTempPath, _MAX_PATH))
  313. {
  314. StringCbCat(szTempPath,sizeof(szTempPath),_T("\\inetsrv"));
  315. }
  316. else
  317. {
  318. ZeroMemory(szTempPath,sizeof(szTempPath));
  319. }
  320. }
  321. else
  322. {
  323. // Do a ton of work just to do this environment variable for %windir% on
  324. // the remote machine.
  325. CString csNewFileSharePath = _T("");
  326. TCHAR szWindowsSystem32InetsrvDir[] = _T("%windir%\\system32\\inetsrv");
  327. CRemoteExpandEnvironmentStrings MyRemoteEnv;
  328. MyRemoteEnv.SetMachineName(szMachineName);
  329. //MyRemoteEnv.SetUserName(_T(""));
  330. //MyRemoteEnv.SetUserPassword(_T(""));
  331. LPTSTR UnexpandedString = NULL;
  332. LPTSTR ExpandedString = NULL;
  333. NET_API_STATUS ApiStatus = NO_ERROR;
  334. // Expand string, using remote environment if necessary.
  335. UnexpandedString = szWindowsSystem32InetsrvDir;
  336. ApiStatus = MyRemoteEnv.RemoteExpandEnvironmentStrings(UnexpandedString,&ExpandedString);
  337. if (NO_ERROR == ApiStatus)
  338. {
  339. GetFullPathLocalOrRemote(szMachineName,ExpandedString,csNewFileSharePath);
  340. StringCbCopy(szTempPath, sizeof(szTempPath), csNewFileSharePath);
  341. if (ExpandedString){LocalFree(ExpandedString);ExpandedString=NULL;}
  342. }
  343. else
  344. {
  345. ZeroMemory(szTempPath,sizeof(szTempPath));
  346. }
  347. }
  348. if (0 == _tcsicmp(szTempPath,_T("")))
  349. {
  350. ZeroMemory(szReturnedPath,sizeof(szReturnedPath));
  351. }
  352. else
  353. {
  354. StringCbCopy(szReturnedPath, cbReturnedPathSize, szTempPath);
  355. bReturn = TRUE;
  356. }
  357. return bReturn;
  358. }
  359. void AddFileExtIfNotExist(LPTSTR szPath, DWORD cbPathSize, LPCTSTR szExt)
  360. {
  361. TCHAR szFilename_ext_only[_MAX_EXT];
  362. _tsplitpath(szPath, NULL, NULL, NULL, szFilename_ext_only);
  363. if (0 == _tcscmp(szFilename_ext_only,_T("")))
  364. {
  365. if (szExt && 0 != _tcscmp(szExt,_T("")))
  366. {
  367. StringCbCat(szPath,cbPathSize,szExt);
  368. }
  369. }
  370. }
  371. void AddPath(LPTSTR szPath,DWORD cbPathSize, LPCTSTR szName )
  372. {
  373. LPTSTR p = szPath;
  374. // Find end of the string
  375. while (*p){p = _tcsinc(p);}
  376. // If no trailing backslash then add one
  377. if (*(_tcsdec(szPath, p)) != _T('\\'))
  378. {
  379. StringCbCat(szPath,cbPathSize,_T("\\"));
  380. }
  381. // if there are spaces precluding szName, then skip
  382. while ( *szName == ' ' ) szName = _tcsinc(szName);;
  383. // Add new name to existing path string
  384. StringCbCat(szPath,cbPathSize,szName);
  385. }
  386. BOOL BrowseForDir(LPTSTR strPath,LPTSTR strFile)
  387. {
  388. BOOL bReturn = FALSE;
  389. if (0 == _tcsicmp(strPath,_T("")))
  390. {
  391. ::GetCurrentDirectory(_MAX_PATH, strPath);
  392. }
  393. CFileDialog fileName(FALSE);
  394. fileName.m_ofn.Flags |= OFN_NOCHANGEDIR | OFN_OVERWRITEPROMPT;
  395. fileName.m_ofn.Flags |= OFN_NOREADONLYRETURN;
  396. // We need to disable hook to show new style of File Dialog
  397. fileName.m_ofn.Flags &= ~(OFN_ENABLEHOOK);
  398. LPTSTR strExt = _T("*.*");
  399. fileName.m_ofn.lpstrDefExt = strExt;
  400. fileName.m_ofn.lpstrFile = strFile;
  401. fileName.m_ofn.nMaxFile = _MAX_PATH;
  402. if (0 == _tcsicmp(strPath,_T("")))
  403. {
  404. fileName.m_ofn.lpstrInitialDir = NULL;
  405. }
  406. else
  407. {
  408. fileName.m_ofn.lpstrInitialDir = (LPCTSTR) strPath;
  409. }
  410. fileName.m_ofn.lpstrFilter = _T("");
  411. fileName.m_ofn.nFilterIndex = 0;
  412. //CThemeContextActivator activator;
  413. if (IDOK == fileName.DoModal())
  414. {
  415. bReturn = TRUE;
  416. CString strPrev;
  417. //GetDlgItemText(IDC_FILE_NAME, strPrev);
  418. /*
  419. if (strPrev.CompareNoCase(strFile) != 0)
  420. {
  421. SetDlgItemText(IDC_FILE_NAME, strFile);
  422. m_DoReplaceFile = TRUE;
  423. FileNameChanged();
  424. }
  425. */
  426. }
  427. return bReturn;
  428. }
  429. BOOL BrowseForFile(LPTSTR strPathIn,LPTSTR strPathOut,DWORD cbPathOut)
  430. {
  431. CString szFileExt;
  432. szFileExt.LoadString(_Module.GetResourceInstance(), IDS_XML_EXT);
  433. CString szFileView(_T("*.xml"));
  434. CString szFilter;
  435. szFilter.LoadString(_Module.GetResourceInstance(), IDS_EXPORT_FILTER);
  436. // replace '|'s in this string to null chars
  437. for (int i = 0; i < szFilter.GetLength(); i++)
  438. {
  439. if (szFilter[i] == L'|')
  440. szFilter.SetAt(i, L'\0');
  441. }
  442. CFileDialog* pFileDlg = new CFileDialog (
  443. TRUE, // use as open File
  444. szFileExt, // default extension
  445. szFileView, // preferred file name
  446. OFN_PATHMUSTEXIST,
  447. szFilter, // filter
  448. NULL);
  449. if (pFileDlg)
  450. {
  451. TCHAR szTempFileName[_MAX_PATH];
  452. //TCHAR szFileName_drive[_MAX_DRIVE];
  453. //TCHAR szFileName_dir[_MAX_DIR];
  454. TCHAR szFileName_fname[_MAX_FNAME];
  455. TCHAR szFileName_ext[_MAX_EXT];
  456. _tsplitpath(strPathIn, NULL, NULL, szFileName_fname, szFileName_ext);
  457. StringCbCopy(szTempFileName, sizeof(szTempFileName), szFileName_fname);
  458. StringCbCat(szTempFileName,sizeof(szTempFileName),szFileName_ext);
  459. pFileDlg->m_ofn.Flags |= OFN_FILEMUSTEXIST;
  460. // We need to disable hook to show new style of File Dialog
  461. //pFileDlg->m_ofn.Flags &= ~(OFN_ENABLEHOOK);
  462. if (0 == _tcsicmp(strPathIn,_T("")))
  463. {
  464. pFileDlg->m_ofn.lpstrInitialDir = NULL;
  465. }
  466. else
  467. {
  468. pFileDlg->m_ofn.lpstrInitialDir = (LPCTSTR) strPathIn;
  469. }
  470. pFileDlg->m_ofn.lpstrFile = szTempFileName;
  471. pFileDlg->m_ofn.nMaxFile = _MAX_PATH;
  472. //CThemeContextActivator activator;
  473. if ( IDOK == pFileDlg->DoModal () )
  474. {
  475. //
  476. // Retrieve the file and path
  477. //
  478. StringCbCopy(strPathOut,cbPathOut,szTempFileName);
  479. return TRUE;
  480. }
  481. }
  482. return FALSE;
  483. }
  484. // Calculate the size of a Multi-String in TCHAR, including the ending 2 '\0's.
  485. int GetMultiStrSize(LPTSTR p)
  486. {
  487. int c = 0;
  488. while (1)
  489. {
  490. if (*p)
  491. {
  492. p++;
  493. c++;
  494. }
  495. else
  496. {
  497. c++;
  498. if (*(p+1))
  499. {
  500. p++;
  501. }
  502. else
  503. {
  504. c++;
  505. break;
  506. }
  507. }
  508. }
  509. return c;
  510. }
  511. BOOL IsMultiSzPaired(LPCTSTR pMultiStr)
  512. {
  513. BOOL bPaired = FALSE;
  514. LPCTSTR pTempMultiStr = pMultiStr;
  515. BOOL bLeadEntry = TRUE;;
  516. while (1)
  517. {
  518. if (pTempMultiStr)
  519. {
  520. // This first value should be a metabase path.
  521. // let's not check it.
  522. //IISDebugOutput(_T("[%d] %s\r\n"),bLeadEntry,pTempMultiStr);
  523. // then increment until we hit another null.
  524. while (*pTempMultiStr)
  525. {
  526. pTempMultiStr++;
  527. }
  528. // check for the ending \0\0
  529. if ( *(pTempMultiStr+1) == NULL)
  530. {
  531. break;
  532. }
  533. else
  534. {
  535. if (bLeadEntry)
  536. {
  537. bLeadEntry = FALSE;
  538. }
  539. else
  540. {
  541. bLeadEntry = TRUE;
  542. }
  543. pTempMultiStr++;
  544. }
  545. if (FALSE == bLeadEntry)
  546. {
  547. // We hit the 2nd value.
  548. // this value should not be a metabase path
  549. // let's check if it is a metabase path.
  550. // if it's a metabase path then it's definetly not "paired"
  551. // (or it's a description with slashes in it...)
  552. if (!IsValidMetabasePath(pTempMultiStr))
  553. {
  554. bPaired = TRUE;
  555. break;
  556. }
  557. /*
  558. if (!_tcschr(pTempMultiStr, '/'))
  559. {
  560. bPaired = TRUE;
  561. break;
  562. }
  563. */
  564. }
  565. }
  566. }
  567. return bPaired;
  568. }
  569. // This walks the multi-sz and returns a pointer between
  570. // the last string of a multi-sz and the second terminating NULL
  571. LPCTSTR GetEndOfMultiSz(LPCTSTR szMultiSz)
  572. {
  573. LPCTSTR lpTemp = szMultiSz;
  574. do
  575. {
  576. lpTemp += wcslen(lpTemp);
  577. lpTemp++;
  578. } while (*lpTemp != L'\0');
  579. return(lpTemp);
  580. }
  581. void DumpStrInMultiStr(LPTSTR pMultiStr)
  582. {
  583. LPTSTR pTempMultiStr = pMultiStr;
  584. //IISDebugOutput(_T("DumpStrInMultiStr:start\r\n"));
  585. while (1)
  586. {
  587. if (pTempMultiStr)
  588. {
  589. // display value
  590. IISDebugOutput(_T(" %s\r\n"),pTempMultiStr);
  591. //wprintf(L" %s\r\n",pTempMultiStr);
  592. // then increment until we hit another null.
  593. while (*pTempMultiStr)
  594. {
  595. pTempMultiStr++;
  596. }
  597. // check for the ending \0\0
  598. if ( *(pTempMultiStr+1) == NULL)
  599. {
  600. break;
  601. }
  602. else
  603. {
  604. pTempMultiStr++;
  605. }
  606. }
  607. }
  608. //IISDebugOutput(_T("DumpStrInMultiStr: end\r\n"));
  609. return;
  610. }
  611. BOOL FindStrInMultiStr(LPTSTR pMultiStr, LPTSTR StrToFind)
  612. {
  613. BOOL bFound = FALSE;
  614. LPTSTR pTempMultiStr = pMultiStr;
  615. DWORD dwCharCount = 0;
  616. while (1)
  617. {
  618. if (pTempMultiStr)
  619. {
  620. // compare this value to the imput value
  621. if (0 == _tcsicmp((const TCHAR *) pTempMultiStr,StrToFind))
  622. {
  623. bFound = TRUE;
  624. break;
  625. }
  626. // then increment until we hit another null.
  627. while (*pTempMultiStr)
  628. {
  629. pTempMultiStr++;
  630. dwCharCount++;
  631. }
  632. // check for the ending \0\0
  633. if ( *(pTempMultiStr+1) == NULL)
  634. {
  635. break;
  636. }
  637. else
  638. {
  639. pTempMultiStr++;
  640. dwCharCount++;
  641. }
  642. // Check if we screwed up somehow and are in an infinite loop.
  643. // could happen if we don't find an ending \0\0
  644. if (dwCharCount > 32000)
  645. {
  646. break;
  647. }
  648. }
  649. }
  650. return bFound;
  651. }
  652. BOOL RemoveStrInMultiStr(LPTSTR pMultiStr, LPTSTR StrToFind)
  653. {
  654. BOOL bFound = FALSE;
  655. LPTSTR pTempMultiStr = pMultiStr;
  656. DWORD dwCharCount = 0;
  657. while (1)
  658. {
  659. if (pTempMultiStr)
  660. {
  661. // compare this value to the imput value
  662. if (0 == _tcsicmp((const TCHAR *) pTempMultiStr,StrToFind))
  663. {
  664. LPTSTR pLastDoubleNull = NULL;
  665. LPTSTR pBeginPath = pTempMultiStr;
  666. bFound = TRUE;
  667. // then increment until we hit another null.
  668. while (*pTempMultiStr)
  669. {
  670. pTempMultiStr++;
  671. }
  672. pTempMultiStr++;
  673. // Find the last double null.
  674. pLastDoubleNull = pTempMultiStr;
  675. if (*pLastDoubleNull)
  676. {
  677. while (1)
  678. {
  679. if (NULL == *pLastDoubleNull && NULL == *(pLastDoubleNull+1))
  680. {
  681. break;
  682. }
  683. pLastDoubleNull++;
  684. }
  685. pLastDoubleNull++;
  686. }
  687. // check if we are the last entry.
  688. if (pLastDoubleNull == pTempMultiStr)
  689. {
  690. // set everything to nulls
  691. memset(pBeginPath,0,(pLastDoubleNull-pBeginPath) * sizeof(TCHAR));
  692. }
  693. else
  694. {
  695. // move everything behind it to where we are.
  696. memmove(pBeginPath,pTempMultiStr, (pLastDoubleNull - pTempMultiStr) * sizeof(TCHAR));
  697. // and set everything behind that to nulls
  698. memset(pBeginPath + (pLastDoubleNull - pTempMultiStr),0,(pTempMultiStr-pBeginPath) * sizeof(TCHAR));
  699. }
  700. break;
  701. }
  702. // then increment until we hit another null.
  703. while (*pTempMultiStr)
  704. {
  705. pTempMultiStr++;
  706. dwCharCount++;
  707. }
  708. // check for the ending \0\0
  709. if ( *(pTempMultiStr+1) == NULL)
  710. {
  711. break;
  712. }
  713. else
  714. {
  715. pTempMultiStr++;
  716. dwCharCount++;
  717. }
  718. // Check if we screwed up somehow and are in an infinite loop.
  719. // could happen if we don't find an ending \0\0
  720. if (dwCharCount > 32000)
  721. {
  722. break;
  723. }
  724. }
  725. }
  726. return bFound;
  727. }
  728. BOOL IsFileExist(LPCTSTR szFile)
  729. {
  730. // Check if the file has expandable Environment strings
  731. LPTSTR pch = NULL;
  732. DWORD dwReturn = 0;
  733. pch = _tcschr( (LPTSTR) szFile, _T('%'));
  734. if (pch)
  735. {
  736. TCHAR szValue[_MAX_PATH];
  737. StringCbCopy(szValue, sizeof(szValue), szFile);
  738. if (!ExpandEnvironmentStrings( (LPCTSTR)szFile, szValue, sizeof(szValue)/sizeof(TCHAR)))
  739. {
  740. StringCbCopy(szValue, sizeof(szValue), szFile);
  741. }
  742. dwReturn = GetFileAttributes(szValue);
  743. if (INVALID_FILE_ATTRIBUTES == dwReturn)
  744. {
  745. return FALSE;
  746. }
  747. else
  748. {
  749. return TRUE;
  750. }
  751. }
  752. else
  753. {
  754. dwReturn = GetFileAttributes(szFile);
  755. if (INVALID_FILE_ATTRIBUTES == dwReturn)
  756. {
  757. // Check if it was because we don't have access...
  758. if (ERROR_LOGON_FAILURE == GetLastError())
  759. {
  760. IISDebugOutput(_T("IsFileExist failed,err=%d (logon failed)\r\n"),GetLastError());
  761. }
  762. return FALSE;
  763. }
  764. else
  765. {
  766. return TRUE;
  767. }
  768. }
  769. }
  770. BOOL IsFileExistRemote(LPCTSTR szMachineName,LPTSTR szFilePathToCheck,LPCTSTR szUserName,LPCTSTR szUserPassword)
  771. {
  772. BOOL bReturn = FALSE;
  773. TCHAR szTempPath[_MAX_PATH];
  774. ZeroMemory(szTempPath,sizeof(szTempPath));
  775. // Determine if we are doing this on the local machine
  776. // or to a remote machine.
  777. if (IsLocalComputer(szMachineName))
  778. {
  779. return IsFileExist(szFilePathToCheck);
  780. }
  781. else
  782. {
  783. // Do a ton of work just to do this environment variable for %windir% on
  784. // the remote machine.
  785. CString csNewFileSharePath = _T("");
  786. CRemoteExpandEnvironmentStrings MyRemoteEnv;
  787. MyRemoteEnv.SetMachineName(szMachineName);
  788. //MyRemoteEnv.SetUserName(szUserName);
  789. //MyRemoteEnv.SetUserPassword(szUserPassword);
  790. LPTSTR UnexpandedString = NULL;
  791. LPTSTR ExpandedString = NULL;
  792. NET_API_STATUS ApiStatus = NO_ERROR;
  793. // Expand string, using remote environment if necessary.
  794. UnexpandedString = szFilePathToCheck;
  795. ApiStatus = MyRemoteEnv.RemoteExpandEnvironmentStrings(UnexpandedString,&ExpandedString);
  796. if (NO_ERROR == ApiStatus)
  797. {
  798. GetFullPathLocalOrRemote(szMachineName,ExpandedString,csNewFileSharePath);
  799. if (!csNewFileSharePath.IsEmpty())
  800. {
  801. StringCbCopy(szTempPath, sizeof(szTempPath), csNewFileSharePath);
  802. }
  803. if (ExpandedString){LocalFree(ExpandedString);ExpandedString=NULL;}
  804. }
  805. }
  806. if (0 != _tcsicmp(szTempPath,_T("")))
  807. {
  808. // Check if the file exists...
  809. bReturn = IsFileExist(szTempPath);
  810. if (!bReturn)
  811. {
  812. if (ERROR_LOGON_FAILURE == GetLastError())
  813. {
  814. // try to net use to the share if the file doesn't exist...
  815. EstablishSession(szMachineName,_T(""),(LPTSTR) szUserName,(LPTSTR) szUserPassword,TRUE);
  816. bReturn = IsFileExist(szTempPath);
  817. EstablishSession(szMachineName,_T(""),_T(""),_T(""),FALSE);
  818. }
  819. }
  820. //IISDebugOutput(_T("IsFileExistRemote:%s,ret=%d\r\n"),szTempPath,bReturn);
  821. }
  822. return bReturn;
  823. }
  824. BOOL IsFileADirectory(LPCTSTR szFile)
  825. {
  826. // Check if the file has expandable Environment strings
  827. DWORD retCode = 0xFFFFFFFF;
  828. LPTSTR pch = NULL;
  829. pch = _tcschr( (LPTSTR) szFile, _T('%'));
  830. if (pch)
  831. {
  832. TCHAR szValue[_MAX_PATH];
  833. StringCbCopy(szValue, sizeof(szValue), szFile);
  834. if (!ExpandEnvironmentStrings( (LPCTSTR)szFile, szValue, sizeof(szValue)/sizeof(TCHAR)))
  835. {
  836. StringCbCopy(szValue, sizeof(szValue), szFile);
  837. }
  838. retCode = GetFileAttributes(szValue);
  839. }
  840. else
  841. {
  842. retCode = GetFileAttributes(szFile);
  843. }
  844. if (retCode & FILE_ATTRIBUTE_DIRECTORY)
  845. {
  846. return TRUE;
  847. }
  848. else
  849. {
  850. return FALSE;
  851. }
  852. }
  853. BOOL IsWebSitePath(IN LPCTSTR lpszMDPath)
  854. /*++
  855. Routine Description:
  856. Arguments:
  857. LPCTSTR lpszMDPath : Metabase path.
  858. Return Value:
  859. TRUE if the path is a w3svc/1 web site,
  860. FALSE otherwise.
  861. --*/
  862. {
  863. BOOL bReturn = FALSE;
  864. if (!lpszMDPath || !*lpszMDPath)
  865. {
  866. return bReturn;
  867. }
  868. CString strSiteNode;
  869. CString strRemainder;
  870. LPCTSTR lpPath = CMetabasePath::TruncatePath(3, lpszMDPath, strSiteNode, &strRemainder);
  871. if (lpPath && !strSiteNode.IsEmpty() && strRemainder.IsEmpty())
  872. {
  873. LPCTSTR lpPath2 = CMetabasePath::TruncatePath(2, lpPath, strSiteNode, &strRemainder);
  874. if (lpPath2){} // to get rid of warning level 4 compile
  875. if (_tcsicmp(strSiteNode,SZ_MBN_SEP_STR SZ_MBN_MACHINE SZ_MBN_SEP_STR SZ_MBN_WEB) == 0
  876. || _tcsicmp(strSiteNode,SZ_MBN_MACHINE SZ_MBN_SEP_STR SZ_MBN_WEB) == 0)
  877. {
  878. bReturn = TRUE;
  879. }
  880. }
  881. return bReturn;
  882. }
  883. BOOL IsFTPSitePath(IN LPCTSTR lpszMDPath)
  884. /*++
  885. Routine Description:
  886. Arguments:
  887. LPCTSTR lpszMDPath : Metabase path.
  888. Return Value:
  889. TRUE if the path is a msftpsvc/1 web site,
  890. FALSE otherwise.
  891. --*/
  892. {
  893. BOOL bReturn = FALSE;
  894. if (!lpszMDPath || !*lpszMDPath)
  895. {
  896. return bReturn;
  897. }
  898. CString strSiteNode;
  899. CString strRemainder;
  900. LPCTSTR lpPath = CMetabasePath::TruncatePath(3, lpszMDPath, strSiteNode, &strRemainder);
  901. if (lpPath && !strSiteNode.IsEmpty() && strRemainder.IsEmpty())
  902. {
  903. LPCTSTR lpPath2 = CMetabasePath::TruncatePath(2, lpPath, strSiteNode, &strRemainder);
  904. if (lpPath2){} // to get rid of warning level 4 compile
  905. if (_tcsicmp(strSiteNode,SZ_MBN_SEP_STR SZ_MBN_MACHINE SZ_MBN_SEP_STR SZ_MBN_FTP) == 0
  906. || _tcsicmp(strSiteNode,SZ_MBN_MACHINE SZ_MBN_SEP_STR SZ_MBN_FTP) == 0)
  907. {
  908. bReturn = TRUE;
  909. }
  910. }
  911. return bReturn;
  912. }
  913. BOOL IsAppPoolPath(IN LPCTSTR lpszMDPath)
  914. /*++
  915. Routine Description:
  916. Arguments:
  917. LPCTSTR lpszMDPath : Metabase path.
  918. Return Value:
  919. TRUE if the path is a app pool,
  920. FALSE otherwise.
  921. --*/
  922. {
  923. BOOL bReturn = FALSE;
  924. if (!lpszMDPath || !*lpszMDPath)
  925. {
  926. return bReturn;
  927. }
  928. CString strSiteNode;
  929. CString strRemainder;
  930. LPCTSTR lpPath = CMetabasePath::TruncatePath(3, lpszMDPath, strSiteNode, &strRemainder);
  931. if (lpPath && !strSiteNode.IsEmpty() && strRemainder.IsEmpty())
  932. {
  933. LPCTSTR lpPath2 = CMetabasePath::TruncatePath(2, lpPath, strSiteNode, &strRemainder);
  934. if (lpPath2){} // to get rid of warning level 4 compile
  935. if (_tcsicmp(strSiteNode,SZ_MBN_SEP_STR SZ_MBN_MACHINE SZ_MBN_SEP_STR SZ_MBN_APP_POOLS) == 0
  936. || _tcsicmp(strSiteNode,SZ_MBN_MACHINE SZ_MBN_SEP_STR SZ_MBN_APP_POOLS) == 0)
  937. {
  938. bReturn = TRUE;
  939. }
  940. }
  941. return bReturn;
  942. }
  943. BOOL IsWebSiteVDirPath(IN LPCTSTR lpszMDPath,IN BOOL bOkayToQueryMetabase)
  944. /*++
  945. Routine Description:
  946. Arguments:
  947. LPCTSTR lpszMDPath : Metabase path.
  948. Return Value:
  949. TRUE if the path is a w3svc/1/root/whatevers vdir,
  950. FALSE otherwise.
  951. --*/
  952. {
  953. BOOL bReturn = FALSE;
  954. if (!lpszMDPath || !*lpszMDPath)
  955. {
  956. return bReturn;
  957. }
  958. CString strSiteNode;
  959. CString strRemainder;
  960. LPCTSTR lpPath = CMetabasePath::TruncatePath(3, lpszMDPath, strSiteNode, &strRemainder);
  961. if (lpPath && !strSiteNode.IsEmpty())
  962. {
  963. LPCTSTR lpPath2 = CMetabasePath::TruncatePath(2, lpPath, strSiteNode, &strRemainder);
  964. if (lpPath2){} // to get rid of warning level 4 compile
  965. if (_tcsicmp(strSiteNode,SZ_MBN_SEP_STR SZ_MBN_MACHINE SZ_MBN_SEP_STR SZ_MBN_WEB) == 0
  966. || _tcsicmp(strSiteNode,SZ_MBN_MACHINE SZ_MBN_SEP_STR SZ_MBN_WEB) == 0)
  967. {
  968. // This is at least a lm/w3svc site
  969. // "lets now ask the metabase, if this is a VDir
  970. if (bOkayToQueryMetabase)
  971. {
  972. // query the metabase to see for sure...
  973. }
  974. bReturn = TRUE;
  975. }
  976. }
  977. return bReturn;
  978. }
  979. BOOL IsFTPSiteVDirPath(IN LPCTSTR lpszMDPath,IN BOOL bOkayToQueryMetabase)
  980. /*++
  981. Routine Description:
  982. Arguments:
  983. LPCTSTR lpszMDPath : Metabase path.
  984. Return Value:
  985. TRUE if the path is a msftpsvc/1/root/whatevers vdir,
  986. FALSE otherwise.
  987. --*/
  988. {
  989. BOOL bReturn = FALSE;
  990. if (!lpszMDPath || !*lpszMDPath)
  991. {
  992. return bReturn;
  993. }
  994. CString strSiteNode;
  995. CString strRemainder;
  996. LPCTSTR lpPath = CMetabasePath::TruncatePath(3, lpszMDPath, strSiteNode, &strRemainder);
  997. if (lpPath && !strSiteNode.IsEmpty())
  998. {
  999. LPCTSTR lpPath2 = CMetabasePath::TruncatePath(2, lpPath, strSiteNode, &strRemainder);
  1000. if (lpPath2){} // to get rid of warning level 4 compile
  1001. if (_tcsicmp(strSiteNode,SZ_MBN_SEP_STR SZ_MBN_MACHINE SZ_MBN_SEP_STR SZ_MBN_FTP) == 0
  1002. || _tcsicmp(strSiteNode,SZ_MBN_MACHINE SZ_MBN_SEP_STR SZ_MBN_FTP) == 0)
  1003. {
  1004. // This is at least a lm/msftpsvc site
  1005. // "lets now ask the metabase, if this is a VDir
  1006. if (bOkayToQueryMetabase)
  1007. {
  1008. // query the metabase to see for sure...
  1009. }
  1010. bReturn = TRUE;
  1011. }
  1012. }
  1013. return bReturn;
  1014. }
  1015. // Quick-'n'-dirty case-insensitive string hash function.
  1016. // Make sure that you follow up with _stricmp or _mbsicmp. You should
  1017. // also cache the length of strings and check those first. Caching
  1018. // an uppercase version of a string can help too.
  1019. // Again, apply HashScramble to the result if using with something other
  1020. // than LKRhash.
  1021. // Note: this is not really adequate for MBCS strings.
  1022. // Small prime number used as a multiplier in the supplied hash functions
  1023. const DWORD HASH_MULTIPLIER = 101;
  1024. # define HASH_MULTIPLY(dw) ((dw) * HASH_MULTIPLIER)
  1025. inline DWORD
  1026. HashStringNoCase(
  1027. TCHAR * psz,
  1028. DWORD dwHash = 0)
  1029. {
  1030. TCHAR * upsz = psz;
  1031. for ( ; *upsz; ++upsz)
  1032. dwHash = HASH_MULTIPLY(dwHash)
  1033. + (*upsz & 0xDF); // strip off lowercase bit
  1034. return dwHash;
  1035. }
  1036. static const DWORD DW_MAX_SITEID = INT_MAX;
  1037. DWORD GetUniqueSite(CString strMetabaseServerNode)
  1038. {
  1039. DWORD dwReturn = 0;
  1040. GUID guid;
  1041. TCHAR wszSiteId[20] = {0};
  1042. TCHAR wszBuffer[64];
  1043. CString strNewSitePath;
  1044. StringCbCopy(wszBuffer, sizeof(wszBuffer), _T("abcdefghijklmnopqrstuvwxyz1234567890"));
  1045. if (SUCCEEDED(::CoCreateGuid(&guid)))
  1046. {
  1047. VERIFY( StringFromGUID2( guid, wszBuffer, 64 ) != 0 );
  1048. }
  1049. // Create random string.
  1050. DWORD dwStart = ( HashStringNoCase(wszBuffer) % DW_MAX_SITEID ) + 1;
  1051. DWORD dwNrSitesTried = 0;
  1052. for(DWORD idx = dwStart;
  1053. dwNrSitesTried < DW_MAX_SITEID;
  1054. dwNrSitesTried++, idx = (idx % DW_MAX_SITEID) + 1)
  1055. {
  1056. dwReturn = idx;
  1057. _ultow(idx, wszSiteId, 10);
  1058. strNewSitePath = strMetabaseServerNode + _T("/") + wszSiteId;
  1059. if (!IsMetabaseWebSiteKeyExist(strNewSitePath))
  1060. {
  1061. break;
  1062. }
  1063. if (dwNrSitesTried > 100)
  1064. {
  1065. // if we can't find one in 100 tries
  1066. // there is something seriously wrong...
  1067. break;
  1068. }
  1069. }
  1070. return dwReturn;
  1071. }
  1072. BOOL IsMetabaseWebSiteKeyExistAuth(PCONNECTION_INFO pConnectionInfo,CString strMetabaseWebSite)
  1073. {
  1074. BOOL bRet = FALSE;
  1075. HRESULT hr = E_FAIL;
  1076. CString str = strMetabaseWebSite;
  1077. LPWSTR lpwstrTempPassword = NULL;
  1078. if (!pConnectionInfo)
  1079. {
  1080. return FALSE;
  1081. }
  1082. if (pConnectionInfo->pszUserPasswordEncrypted)
  1083. {
  1084. if (FAILED(DecryptMemoryPassword((LPWSTR) pConnectionInfo->pszUserPasswordEncrypted,&lpwstrTempPassword,pConnectionInfo->cbUserPasswordEncrypted)))
  1085. {
  1086. return FALSE;
  1087. }
  1088. }
  1089. CComAuthInfo auth(pConnectionInfo->pszMachineName,pConnectionInfo->pszUserName,lpwstrTempPassword);
  1090. CMetaKey key(&auth,str,METADATA_PERMISSION_READ);
  1091. hr = key.QueryResult();
  1092. if (key.Succeeded())
  1093. {
  1094. // i guess so.
  1095. bRet = TRUE;
  1096. goto IsMetabaseWebSiteKeyExistAuth_Exit;
  1097. }
  1098. IsMetabaseWebSiteKeyExistAuth_Exit:
  1099. if (lpwstrTempPassword)
  1100. {
  1101. // security percaution:Make sure to zero out memory that temporary password was used for.
  1102. SecureZeroMemory(lpwstrTempPassword,pConnectionInfo->cbUserPasswordEncrypted);
  1103. LocalFree(lpwstrTempPassword);
  1104. lpwstrTempPassword = NULL;
  1105. }
  1106. return bRet;
  1107. }
  1108. BOOL IsMetabaseWebSiteKeyExist(CString strMetabaseWebSite)
  1109. {
  1110. HRESULT hr = E_FAIL;
  1111. CString str = strMetabaseWebSite;
  1112. CComAuthInfo auth;
  1113. CMetaKey key(&auth,str,METADATA_PERMISSION_READ);
  1114. hr = key.QueryResult();
  1115. if (key.Succeeded())
  1116. {
  1117. // i guess so.
  1118. return TRUE;
  1119. }
  1120. return FALSE;
  1121. }
  1122. void AddEndingMetabaseSlashIfNeedTo(LPTSTR szDestinationString,DWORD cbDestinationString)
  1123. {
  1124. if (szDestinationString)
  1125. {
  1126. if ('/' != szDestinationString[_tcslen(szDestinationString) - 1])
  1127. {
  1128. __try
  1129. {
  1130. StringCbCat(szDestinationString,cbDestinationString,_T("/"));
  1131. }
  1132. __except (EXCEPTION_EXECUTE_HANDLER)
  1133. {
  1134. // lame
  1135. }
  1136. }
  1137. }
  1138. }
  1139. BOOL AnswerIsYes(HWND hDlg,UINT id,LPCTSTR file)
  1140. {
  1141. CString strFormat;
  1142. CString strMsg;
  1143. CString strCaption;
  1144. strCaption.LoadString(_Module.GetResourceInstance(), IDS_MSGBOX_CAPTION);
  1145. strFormat.LoadString(_Module.GetResourceInstance(), id);
  1146. strMsg.Format(strFormat, file);
  1147. return (IDYES == MessageBox(hDlg,strMsg,strCaption,MB_ICONEXCLAMATION | MB_YESNO));
  1148. }
  1149. /*
  1150. * Function : RemoveSpaces
  1151. * Copies a string removing leading and trailing spaces but allowing
  1152. * for long file names with internal spaces.
  1153. *
  1154. * Parameters :
  1155. * szPath - The output result
  1156. * szEdit - The input path
  1157. */
  1158. VOID RemoveSpaces(LPTSTR szPath, DWORD cbPathSize, LPTSTR szEdit)
  1159. {
  1160. LPTSTR szLastSpaceList;
  1161. while (*szEdit == TEXT(' ')) {
  1162. szEdit = CharNext(szEdit);
  1163. }
  1164. StringCbCopy(szPath, cbPathSize, szEdit);
  1165. for (szLastSpaceList = NULL;
  1166. *szPath != TEXT('\0');
  1167. szPath = CharNext(szPath)) {
  1168. if (*szPath == TEXT(' ')) {
  1169. if (szLastSpaceList == NULL) {
  1170. szLastSpaceList = szPath;
  1171. }
  1172. } else {
  1173. szLastSpaceList = NULL;
  1174. }
  1175. }
  1176. if (szLastSpaceList != NULL) {
  1177. *szLastSpaceList = TEXT('\0');
  1178. }
  1179. }
  1180. BOOL IsSpaces(LPCTSTR szPath)
  1181. {
  1182. BOOL bAllSpaces = TRUE;
  1183. // skip over leading spaces..
  1184. while (*szPath == TEXT(' '))
  1185. {
  1186. szPath = CharNext(szPath);
  1187. }
  1188. while (*szPath && *szPath != TEXT(' '))
  1189. {
  1190. bAllSpaces = FALSE;
  1191. szPath = CharNext(szPath);
  1192. }
  1193. return bAllSpaces;
  1194. }
  1195. HRESULT DumpProxyInfo(IUnknown * punk)
  1196. {
  1197. COAUTHINFO authinfo;
  1198. COAUTHINFO* pCoAuthInfo = &authinfo;
  1199. HRESULT hr = E_FAIL;
  1200. // in all cases, update the fields to reflect the actual state of
  1201. // security on the proxy
  1202. if (SUCCEEDED(hr = CoQueryProxyBlanket(punk,
  1203. &pCoAuthInfo->dwAuthnSvc,
  1204. &pCoAuthInfo->dwAuthzSvc,
  1205. &pCoAuthInfo->pwszServerPrincName,
  1206. &pCoAuthInfo->dwAuthnLevel,
  1207. &pCoAuthInfo->dwImpersonationLevel,
  1208. (RPC_AUTH_IDENTITY_HANDLE*) &pCoAuthInfo->pAuthIdentityData,
  1209. &pCoAuthInfo->dwCapabilities
  1210. )))
  1211. {
  1212. IISDebugOutput(_T("CoQueryProxyBlanket:dwAuthnSvc=%d,dwAuthzSvc=%d,pwszServerPrincName=%s,dwAuthnLevel=%d,dwImpersonationLevel=%d,pAuthIdentityData=%p,dwCapabilities=%d\r\n"),
  1213. pCoAuthInfo->dwAuthnSvc,
  1214. pCoAuthInfo->dwAuthzSvc,
  1215. pCoAuthInfo->pwszServerPrincName,
  1216. pCoAuthInfo->dwAuthnLevel,
  1217. pCoAuthInfo->dwImpersonationLevel,
  1218. pCoAuthInfo->pAuthIdentityData,
  1219. pCoAuthInfo->dwCapabilities
  1220. );
  1221. }
  1222. return hr;
  1223. }
  1224. void LaunchHelp(HWND hWndMain, DWORD_PTR dwWinHelpID)
  1225. {
  1226. DebugTraceHelp(dwWinHelpID);
  1227. CString sz;
  1228. TCHAR szHelpLocation[MAX_PATH+1];
  1229. sz.LoadString(_Module.GetResourceInstance(), IDS_HELPLOC_HELP);
  1230. if (ExpandEnvironmentStrings(sz, szHelpLocation, MAX_PATH))
  1231. {
  1232. WinHelp(hWndMain,szHelpLocation,HELP_CONTEXT,dwWinHelpID);
  1233. }
  1234. return;
  1235. }