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.

2015 lines
63 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // File: common.cpp
  4. //
  5. // Module: CMSTP.EXE
  6. //
  7. // Synopsis: This source file contains functions common to several
  8. // different aspects of the CM profile installer (install,
  9. // uninstall, migration).
  10. //
  11. // Copyright (c) 1997-1999 Microsoft Corporation
  12. //
  13. // Author: quintinb Created 11/18/97
  14. //
  15. //+----------------------------------------------------------------------------
  16. #include "cmmaster.h"
  17. //
  18. // for GetPhoneBookPath
  19. //
  20. #include "getpbk.cpp"
  21. //
  22. // For GetAllUsersCmDir
  23. //
  24. #include "allcmdir.cpp"
  25. //
  26. // Need the definition for CM_PBK_FILTER_PREFIX
  27. //
  28. #include "cmdefs.h"
  29. //
  30. // Include the Connections folder specific headers
  31. //
  32. //#include "shlobjp.h"
  33. //#include <objbase.h> // needed for initing guids
  34. //#include <initguid.h> // DON'T CHANGE the ORDER of these header files unless you know what you are doing
  35. //#include <oleguid.h> // IID_IDataObject
  36. //#include <shlguid.h> // IID_IShellFolder
  37. //+----------------------------------------------------------------------------
  38. //
  39. // Function: GetHiddenPhoneBookPath
  40. //
  41. // Synopsis: This function returns the path for the hidden RAS pbk to contain
  42. // the PPP connectoid of a double dial connection. Before returing
  43. // it checks to see if the phonebook exists or not. If the phonebook
  44. // doesn't exist then it returns FALSE. If the function returns
  45. // TRUE the path allocated and stored in *ppszPhonebook must be
  46. // freed using CmFree.
  47. //
  48. // Arguments: LPCTSTR pszProfileDir - full path to the profile directory (dir where cmp resides)
  49. // LPTSTR* ppszPhonebook - pointer to hold the allocated path
  50. //
  51. // Returns: BOOL - TRUE if the phonebook path can be constructed and the
  52. // phonebook file exists.
  53. //
  54. // History: quintinb Created Header 04/14/00
  55. //
  56. //+----------------------------------------------------------------------------
  57. BOOL GetHiddenPhoneBookPath(LPCTSTR pszProfileDir, LPTSTR* ppszPhonebook)
  58. {
  59. // REVIEW: quintinb 12-18-00
  60. // This function returns the wrong path for the Hidden phonebook. This file is
  61. // now named _CMPhone (no .pbk extension) and is now located in the same directory
  62. // as the rasphone.pbk file on NT4, NT5, and whistler. Note that this function isn't
  63. // used on Win9x. Since the problem was low priority (leaving a few K file on the users
  64. // harddrive at uninstall time, we punted the problem as it was close to Beta2 lockdown.
  65. //
  66. BOOL bReturn = FALSE;
  67. if (pszProfileDir && ppszPhonebook && (TEXT('\0') != pszProfileDir[0]))
  68. {
  69. LPTSTR pszPathToReturn = NULL;
  70. *ppszPhonebook = (LPTSTR) CmMalloc((lstrlen(pszProfileDir) + lstrlen(CM_PBK_FILTER_PREFIX) + 13) * sizeof(TCHAR));
  71. MYDBGASSERT(*ppszPhonebook);
  72. if (*ppszPhonebook)
  73. {
  74. //
  75. // Note -- The connection enumerator (netman) will filter out notifications
  76. // for connections created in files with the CM_PBK_FILTER_PREFIX in their
  77. // name.
  78. //
  79. wsprintf(*ppszPhonebook, TEXT("%s\\%sphone.pbk"), pszProfileDir, CM_PBK_FILTER_PREFIX);
  80. bReturn = FileExists(*ppszPhonebook);
  81. }
  82. }
  83. if (FALSE == bReturn)
  84. {
  85. CmFree(*ppszPhonebook);
  86. *ppszPhonebook = NULL;
  87. }
  88. return bReturn;
  89. }
  90. //+----------------------------------------------------------------------------
  91. //
  92. // Function: RemoveShowIconFromRunPostSetupCommands
  93. //
  94. // Synopsis: This function removes showicon.exe from the RunPostSetupCommands
  95. // section of old 1.0 Infs.
  96. //
  97. // Arguments: LPCTSTR szInfFile - the inf file to remove showicon.exe from
  98. //
  99. // Returns: Nothing
  100. //
  101. // History: quintinb Created Header 10/22/98
  102. //
  103. //+----------------------------------------------------------------------------
  104. void RemoveShowIconFromRunPostSetupCommands(LPCTSTR szInfFile)
  105. {
  106. DWORD dwSize = 1024;
  107. DWORD dwSizeNeeded = 1024;
  108. TCHAR* pszBuffer = NULL;
  109. TCHAR* pszNewBuffer = NULL;
  110. const TCHAR* const c_pszRunPostSetupCommandsSection = TEXT("RunPostSetupCommandsSection");
  111. const TCHAR* const c_pszShowIcon = TEXT("showicon.exe");
  112. pszBuffer = (TCHAR*)CmMalloc(sizeof(TCHAR)*dwSize);
  113. if (NULL == pszBuffer)
  114. {
  115. CMASSERTMSG(FALSE, TEXT("RemoveShowIconFromRunPostSetupCommands -- CmMalloc returned a NULL pointer."));
  116. goto exit;
  117. }
  118. dwSizeNeeded = GetPrivateProfileSection(c_pszRunPostSetupCommandsSection, pszBuffer,
  119. dwSize, szInfFile);
  120. while((dwSizeNeeded + 2) == dwSize)
  121. {
  122. //
  123. // the buffer isn't big enough, try again.
  124. //
  125. dwSize += 1024;
  126. MYDBGASSERT(dwSize <= 32*1024); // 32767 is the max size on Win95
  127. CmFree(pszBuffer);
  128. pszBuffer = (TCHAR*)CmMalloc(sizeof(TCHAR)*dwSize);
  129. if (NULL == pszBuffer)
  130. {
  131. CMASSERTMSG(FALSE, TEXT("RemoveShowIconFromRunPostSetupCommands -- CmMalloc returned a NULL pointer."));
  132. goto exit;
  133. }
  134. dwSizeNeeded = GetPrivateProfileSection(c_pszRunPostSetupCommandsSection,
  135. pszBuffer, dwSize, szInfFile);
  136. }
  137. //
  138. // Search the Buffer to find and remove and occurences of showicon.exe
  139. //
  140. if (0 != dwSizeNeeded)
  141. {
  142. //
  143. // Allocate a new buffer of the same size.
  144. //
  145. pszNewBuffer = (TCHAR*)CmMalloc(sizeof(TCHAR)*dwSize);
  146. if (NULL == pszNewBuffer)
  147. {
  148. CMASSERTMSG(FALSE, TEXT("RemoveShowIconFromRunPostSetupCommands -- CmMalloc returned a NULL pointer."));
  149. goto exit;
  150. }
  151. //
  152. // Use Temp pointers to walk the buffers
  153. //
  154. TCHAR *pszNewBufferTemp = pszNewBuffer;
  155. TCHAR *pszBufferTemp = pszBuffer;
  156. while (TEXT('\0') != pszBufferTemp[0])
  157. {
  158. //
  159. // If the string isn't showicon.exe then go ahead and copy it to the new
  160. // buffer. Otherwise, don't.
  161. //
  162. if (0 != lstrcmpi(c_pszShowIcon, pszBufferTemp))
  163. {
  164. lstrcpy(pszNewBufferTemp, pszBufferTemp);
  165. pszNewBufferTemp = pszNewBufferTemp + (lstrlen(pszNewBufferTemp) + 1)*sizeof(TCHAR);
  166. }
  167. pszBufferTemp = pszBufferTemp + (lstrlen(pszBufferTemp) + 1)*sizeof(TCHAR);
  168. }
  169. //
  170. // Erase the current Section and then rewrite it with the new section
  171. //
  172. MYVERIFY(0 != WritePrivateProfileSection(c_pszRunPostSetupCommandsSection,
  173. NULL, szInfFile));
  174. MYVERIFY(0 != WritePrivateProfileSection(c_pszRunPostSetupCommandsSection,
  175. pszNewBuffer, szInfFile));
  176. }
  177. exit:
  178. CmFree(pszBuffer);
  179. CmFree(pszNewBuffer);
  180. }
  181. //+----------------------------------------------------------------------------
  182. //
  183. // Function: HrRegDeleteKeyTree
  184. //
  185. // Synopsis: Deletes an entire registry hive.
  186. //
  187. // Arguments: hkeyParent [in] Handle to open key where the desired key resides.
  188. // szRemoveKey [in] Name of key to delete.
  189. //
  190. // Returns: HRESULT HrRegDeleteKeyTree -
  191. //
  192. // History: danielwe 25 Feb 1997
  193. // borrowed and modified -- quintinb -- 4-2-98
  194. //
  195. //+----------------------------------------------------------------------------
  196. HRESULT HrRegDeleteKeyTree (HKEY hkeyParent, LPCTSTR szRemoveKey)
  197. {
  198. LONG lResult;
  199. HRESULT hr;
  200. MYDBGASSERT(hkeyParent);
  201. MYDBGASSERT(szRemoveKey);
  202. // Open the key we want to remove
  203. HKEY hkeyRemove;
  204. lResult = RegOpenKeyEx(hkeyParent, szRemoveKey, 0, KEY_ALL_ACCESS,
  205. &hkeyRemove);
  206. hr = HRESULT_FROM_WIN32 (lResult);
  207. if (SUCCEEDED(hr))
  208. {
  209. TCHAR szValueName [MAX_PATH+1];
  210. DWORD cchBuffSize = MAX_PATH;
  211. FILETIME ft;
  212. // Enum the keys children, and remove those sub-trees
  213. while (ERROR_NO_MORE_ITEMS != (lResult = RegEnumKeyEx(hkeyRemove,
  214. 0,
  215. szValueName,
  216. &cchBuffSize,
  217. NULL,
  218. NULL,
  219. NULL,
  220. &ft)))
  221. {
  222. MYVERIFY(SUCCEEDED(HrRegDeleteKeyTree (hkeyRemove, szValueName)));
  223. cchBuffSize = MAX_PATH;
  224. }
  225. MYVERIFY(ERROR_SUCCESS == RegCloseKey (hkeyRemove));
  226. if ((ERROR_SUCCESS == lResult) || (ERROR_NO_MORE_ITEMS == lResult))
  227. {
  228. lResult = RegDeleteKey(hkeyParent, szRemoveKey);
  229. }
  230. hr = HRESULT_FROM_WIN32 (lResult);
  231. }
  232. return hr;
  233. }
  234. //+----------------------------------------------------------------------------
  235. //
  236. // Function: RemovePhonebookEntry
  237. //
  238. // Synopsis: This function loads RAS dynamically and then deletes the specified
  239. // connectoids. It will either delete only the connectoid exactly
  240. // specified by the phonebook and entry name (bMatchSimilarEntries == FALSE)
  241. // or it will enumerate all entries in the phonebook and delete any
  242. // entry that matches the first lstrlen(pszEntryName) chars of the given
  243. // connectoid name (thus deleting backup and tunnel connectoids). Note
  244. // that on NT5 we must set the <> parameter of the connectoid to "" so
  245. // that the RasCustomDeleteEntryNotify will not get called and thus have
  246. // cmstp.exe /u launched on the connection.
  247. //
  248. // Arguments: LPTSTR pszEntryName - the long service name of the profile to delete
  249. // LPTSTR pszPhonebook - the full path to the pbk file to delete entries from
  250. // BOOL bMatchSimilarEntries - whether the function should delete similarly
  251. // named connectoids or only the exact connectoid
  252. // specified.
  253. //
  254. // Returns: BOOL - returns TRUE if the function was successful, FALSE otherwise
  255. //
  256. // History: quintinb 7/14/98 Created
  257. // quintinb 7/27/99 rewrote to include deleting a single connectoid or
  258. // enumerating to delete all similarly named connectoids
  259. //
  260. //+----------------------------------------------------------------------------
  261. BOOL RemovePhonebookEntry(LPCTSTR pszEntryName, LPTSTR pszPhonebook, BOOL bMatchSimilarEntries)
  262. {
  263. pfnRasDeleteEntrySpec pfnDeleteEntry;
  264. pfnRasEnumEntriesSpec pfnEnumEntries;
  265. pfnRasSetEntryPropertiesSpec pfnSetEntryProperties;
  266. pfnRasSetCredentialsSpec pfnSetCredentials;
  267. DWORD dwStructSize;
  268. DWORD dwSize;
  269. DWORD dwNum;
  270. DWORD dwRet;
  271. DWORD dwIdx;
  272. DWORD dwLen;
  273. CPlatform plat;
  274. BOOL bReturn = FALSE;
  275. BOOL bExit;
  276. TCHAR szTemp[MAX_PATH+1];
  277. RASENTRYNAME* pRasEntries = NULL;
  278. RASENTRYNAME* pCurrentRasEntry = NULL;
  279. //
  280. // Check Inputs
  281. //
  282. MYDBGASSERT(NULL != pszEntryName);
  283. MYDBGASSERT((NULL == pszPhonebook) || (TEXT('\0') != pszPhonebook[0]));
  284. if ((NULL == pszEntryName) || ((NULL != pszPhonebook) && (TEXT('\0') == pszPhonebook[0])))
  285. {
  286. CMTRACE(TEXT("RemovePhonebookEntry -- Invalid Parameter passed in."));
  287. goto exit;
  288. }
  289. //
  290. // Get Function Pointers for the Ras Apis that we need
  291. //
  292. if(!GetRasApis(&pfnDeleteEntry, &pfnEnumEntries, &pfnSetEntryProperties, NULL, NULL,
  293. (plat.IsAtLeastNT5() ? &pfnSetCredentials : NULL)))
  294. {
  295. CMTRACE(TEXT("RemovePhonebookEntry -- Unable to get RAS apis."));
  296. bReturn = FALSE;
  297. goto exit;
  298. }
  299. //
  300. // Setup the Structure Sizes correctly
  301. //
  302. if (plat.IsAtLeastNT5())
  303. {
  304. dwStructSize = sizeof(RASENTRYNAME_V500);
  305. }
  306. else
  307. {
  308. dwStructSize = sizeof(RASENTRYNAME);
  309. }
  310. //
  311. // Init the Size to one struct and dwNum to zero entries
  312. //
  313. bExit = FALSE;
  314. dwSize = dwStructSize*1;
  315. dwNum = 0;
  316. do
  317. {
  318. pRasEntries = (RASENTRYNAME*)CmMalloc(dwSize);
  319. if (NULL == pRasEntries)
  320. {
  321. CMASSERTMSG(FALSE, TEXT("RemovePhonebookEntry -- CmMalloc returned a NULL pointer."));
  322. goto exit;
  323. }
  324. //
  325. // Set the struct size
  326. //
  327. pRasEntries->dwSize = dwStructSize;
  328. dwRet = (pfnEnumEntries)(NULL, pszPhonebook, (RASENTRYNAME*)pRasEntries, &dwSize, &dwNum);
  329. //
  330. // Check the return code from RasEnumEntries
  331. //
  332. if (ERROR_BUFFER_TOO_SMALL == dwRet)
  333. {
  334. CMTRACE1(TEXT("RemovePhonebookEntry -- RasEnumEntries said our buffer was too small, New Size=%u"), dwNum*dwStructSize);
  335. CmFree(pRasEntries);
  336. dwSize = dwStructSize * dwNum;
  337. dwNum = 0;
  338. }
  339. else if (ERROR_SUCCESS == dwRet)
  340. {
  341. CMTRACE1(TEXT("RemovePhonebookEntry -- RasEnumEntries successful, %u entries enumerated."), dwNum);
  342. bExit = TRUE;
  343. }
  344. else
  345. {
  346. CMTRACE1(TEXT("RemovePhonebookEntry -- RasEnumEntries Failed, dwRet == %u"), dwRet);
  347. goto exit;
  348. }
  349. } while (!bExit);
  350. //
  351. // At this point we should have entries to process, if not then we will exit here. Otherwise
  352. // we will look for matches and then delete any we find.
  353. //
  354. dwLen = lstrlen(pszEntryName) + 1; // get the length of the Entry Name
  355. bReturn = TRUE; // assume everything is okay at this point.
  356. //
  357. // okay now we are ready to perform the deletions
  358. //
  359. pCurrentRasEntry = pRasEntries;
  360. for (dwIdx=0; dwIdx < dwNum; dwIdx++)
  361. {
  362. CMTRACE2(TEXT("\tRemovePhonebookEntry -- RasEnumEntries returned %s in %s"), pCurrentRasEntry->szEntryName, MYDBGSTR(pszPhonebook));
  363. if (bMatchSimilarEntries)
  364. {
  365. //
  366. // Match entries that have the first lstrlen(pszEntryName) chars
  367. // the same.
  368. //
  369. lstrcpyn(szTemp, pCurrentRasEntry->szEntryName, dwLen);
  370. }
  371. else
  372. {
  373. //
  374. // Only match exact entries.
  375. //
  376. lstrcpy(szTemp, pCurrentRasEntry->szEntryName);
  377. }
  378. if (0 == lstrcmp(szTemp, pszEntryName))
  379. {
  380. //
  381. // We have an entry that starts with the Long Service Name, so delete it. Note
  382. // that if this is NT5 then we need to clear the szCustomDialDll param of the
  383. // connectoid so we don't get called again on the RasCustomDeleteNotify entry
  384. // point
  385. //
  386. if (plat.IsAtLeastNT5())
  387. {
  388. //
  389. // On NT5, we also want to make sure we clean up any credentials associated with this
  390. // connectoid. We do that by calling RasSetCredentials
  391. //
  392. RASCREDENTIALSA RasCreds = {0};
  393. RasCreds.dwSize = sizeof(RASCREDENTIALSA);
  394. RasCreds.dwMask = RASCM_UserName | RASCM_Password | RASCM_Domain;
  395. dwRet = (pfnSetCredentials)(pszPhonebook, pCurrentRasEntry->szEntryName, &RasCreds, TRUE); // TRUE == fClearCredentials
  396. MYDBGASSERT(ERROR_SUCCESS == dwRet);
  397. RASENTRY_V500 RasEntryV5 = {0};
  398. RasEntryV5.dwSize = sizeof(RASENTRY_V500);
  399. RasEntryV5.dwType = RASET_Internet;
  400. // RasEntryV5.szCustomDialDll[0] = TEXT('\0'); -- already zero-ed
  401. dwRet = ((pfnSetEntryProperties)(pszPhonebook, pCurrentRasEntry->szEntryName,
  402. (RASENTRY*)&RasEntryV5, RasEntryV5.dwSize, NULL, 0));
  403. if (ERROR_SUCCESS != dwRet)
  404. {
  405. CMTRACE3(TEXT("\t\tRemovePhonebookEntry -- RasSetEntryProperties failed on entry %s in %s, dwRet = %u"), pCurrentRasEntry->szEntryName, MYDBGSTR(pszPhonebook), dwRet);
  406. bReturn = FALSE;
  407. continue; // don't try to delete the entry it might cause a re-launch problem
  408. }
  409. else
  410. {
  411. CMTRACE2(TEXT("\t\tRemovePhonebookEntry -- Clearing CustomDialDll setting with RasSetEntryProperties on entry %s in %s"), pCurrentRasEntry->szEntryName, MYDBGSTR(pszPhonebook));
  412. }
  413. }
  414. dwRet = (pfnDeleteEntry)(pszPhonebook, pCurrentRasEntry->szEntryName);
  415. if (ERROR_SUCCESS != dwRet)
  416. {
  417. CMTRACE3(TEXT("\t\tRemovePhonebookEntry -- RasDeleteEntry failed on entry %s in %s, dwRet = %u"), pCurrentRasEntry->szEntryName, MYDBGSTR(pszPhonebook), dwRet);
  418. bReturn = FALSE; // set return to FALSE but continue trying to delete entries
  419. }
  420. else
  421. {
  422. CMTRACE2(TEXT("\t\tRemovePhonebookEntry -- Deleted entry %s in %s"), pCurrentRasEntry->szEntryName, MYDBGSTR(pszPhonebook));
  423. }
  424. }
  425. //
  426. // Increment to next RasEntryName struct, note we have to do this manually since
  427. // the sizeof(RASENTRYNAME) is wrong for NT5 structs.
  428. //
  429. pCurrentRasEntry = (RASENTRYNAME*)((BYTE*)pCurrentRasEntry + dwStructSize);
  430. }
  431. exit:
  432. CmFree(pRasEntries);
  433. return bReturn;
  434. }
  435. //+----------------------------------------------------------------------------
  436. //
  437. // Function: DeleteNT5ShortcutFromPathAndName
  438. //
  439. // Synopsis: This function deletes the link specified by the CSIDL (see SHGetSpecialFolderLocation),
  440. // and the profilename. Used before installing a profile to make
  441. // sure we don't get duplicate links.
  442. //
  443. // Arguments: LPCTSTR szProfileName - string that holds the profilename
  444. // int nFolder - the CSIDL identifier of the folder that holds the
  445. // link to delete
  446. //
  447. // Returns: Nothing
  448. //
  449. // History: quintinb Created 5/26/98
  450. //
  451. //+----------------------------------------------------------------------------
  452. void DeleteNT5ShortcutFromPathAndName(HINSTANCE hInstance, LPCTSTR szProfileName, int nFolder)
  453. {
  454. TCHAR szFolderDir[MAX_PATH+1];
  455. if (SUCCEEDED(GetNT5FolderPath(nFolder, szFolderDir)))
  456. {
  457. //
  458. // Now add \Shortcut to %LongServiceName% to the end of path
  459. //
  460. TCHAR szCleanString[MAX_PATH+1];
  461. TCHAR szShortCutPreface[MAX_PATH+1];
  462. ZeroMemory(szCleanString, sizeof(szCleanString));
  463. MYVERIFY(0 != LoadString(hInstance, IDS_SHORTCUT_TO, szShortCutPreface, MAX_PATH));
  464. MYVERIFY(CELEMS(szCleanString) > (UINT)wsprintf(szCleanString, TEXT("%s\\%s %s.lnk"), szFolderDir, szShortCutPreface, szProfileName));
  465. if (SetFileAttributes(szCleanString, FILE_ATTRIBUTE_NORMAL))
  466. {
  467. SHFILEOPSTRUCT fOpStruct;
  468. ZeroMemory(&fOpStruct, sizeof(fOpStruct));
  469. fOpStruct.wFunc = FO_DELETE;
  470. fOpStruct.pFrom = szCleanString;
  471. fOpStruct.fFlags = FOF_SILENT | FOF_NOCONFIRMATION;
  472. //
  473. // The shell32.dll on Win95 doesn't contain the SHFileOperationW function. Thus if we compile
  474. // this Unicode we must revisit this code and dynamically link to it.
  475. //
  476. MYVERIFY(0 == SHFileOperation(&fOpStruct));
  477. }
  478. }
  479. }
  480. //+----------------------------------------------------------------------------
  481. //
  482. // Function: CreateNT5ProfileShortcut
  483. //
  484. // Synopsis: This function uses private APIs in NetShell.dll to create a desktop
  485. // shortcut to the specified connections.
  486. //
  487. // Arguments: LPTSTR pszProfileName - Name of the Connection to look for
  488. // LPTSTR pszPhoneBook - Full path to the pbk that the connection resides in
  489. // BOOL bAllUsers - TRUE if looking for an All Users connection
  490. //
  491. // Returns: HRESULT - returns normal hr codes
  492. //
  493. // History: quintinb Created 5/5/98
  494. // quintinb Updated to use Netshell APIs 2/17/99
  495. //
  496. //+----------------------------------------------------------------------------
  497. HRESULT CreateNT5ProfileShortcut(LPCTSTR pszProfileName, LPCTSTR pszPhoneBook, BOOL bAllUsers)
  498. {
  499. HRESULT hr = E_FAIL;
  500. pfnCreateShortcutSpec pfnCreateShortcut = NULL;
  501. pfnRasGetEntryPropertiesSpec pfnGetEntryProperties = NULL;
  502. //
  503. // Check Inputs
  504. //
  505. if ((NULL == pszProfileName) || (TEXT('\0') == pszProfileName[0]) ||
  506. (NULL != pszPhoneBook && TEXT('\0') == pszPhoneBook[0]))
  507. {
  508. //
  509. // Then they passed in an invalid string argument, thus return invalid arg. Note
  510. // that pszPhoneBook can be NULL but that if it isn't NULL it cannot be empty.
  511. //
  512. return E_INVALIDARG;
  513. }
  514. //
  515. // First Find the GUID of the connection
  516. //
  517. if (!GetRasApis(NULL, NULL, NULL, NULL, &pfnGetEntryProperties, NULL))
  518. {
  519. return E_UNEXPECTED;
  520. }
  521. DWORD dwRes;
  522. DWORD dwSize;
  523. LPRASENTRY_V500 pRasEntry = NULL;
  524. pRasEntry = (LPRASENTRY_V500)CmMalloc(sizeof(RASENTRY_V500));
  525. if (NULL != pRasEntry)
  526. {
  527. ZeroMemory(pRasEntry, sizeof(RASENTRY_V500));
  528. pRasEntry->dwSize = sizeof(RASENTRY_V500);
  529. dwSize = sizeof(RASENTRY_V500);
  530. dwRes = (pfnGetEntryProperties)(pszPhoneBook, pszProfileName, (LPRASENTRY)pRasEntry, &dwSize, NULL, NULL);
  531. if (0 == dwRes)
  532. {
  533. //
  534. // Then we were able to get the RasEntry, load the NetShell API
  535. // and call HrCreateShortcut
  536. //
  537. pfnSHGetSpecialFolderPathWSpec pfnSHGetSpecialFolderPathW;
  538. if(GetShell32Apis(NULL, &pfnSHGetSpecialFolderPathW))
  539. {
  540. WCHAR szwPath[MAX_PATH+1];
  541. hr = (pfnSHGetSpecialFolderPathW)(NULL, szwPath,
  542. bAllUsers ? CSIDL_COMMON_DESKTOPDIRECTORY : CSIDL_DESKTOPDIRECTORY, FALSE);
  543. if (SUCCEEDED(hr) && GetNetShellApis(NULL, &pfnCreateShortcut, NULL))
  544. {
  545. hr = (pfnCreateShortcut)(pRasEntry->guidId, szwPath);
  546. }
  547. }
  548. }
  549. else
  550. {
  551. CMTRACE1(TEXT("CreateNT5ProfileShortcut -- RasGetEntryProperties returned %u"), dwRes);
  552. CMASSERTMSG(FALSE, TEXT("Unable to find the connection for which the shortcut was requested in the RAS pbk."));
  553. return HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
  554. }
  555. CmFree(pRasEntry);
  556. }
  557. return hr;
  558. }
  559. //+----------------------------------------------------------------------------
  560. //
  561. // Function: WriteCmPhonebookEntry
  562. //
  563. // Synopsis: This function creates an NT5 phonebook entry for a CM connection.
  564. // .
  565. // The function sets:
  566. // - the szAutoDialDll to cmdial32.dll
  567. // - the modem name and device type
  568. // - the type to RASET_Inernet.
  569. //
  570. // Arguments: LPCTSTR szLongServiceName - Name of the Connectoid to be created
  571. // LPCTSTR szFullPathtoPBK - full path to the pbk to put the connectoid in, if NULL
  572. // the system phonebook is used.
  573. // LPCTSTR pszCmsFile - The full path of the referencing .CMS for the profile
  574. //
  575. // Returns: BOOL - TRUE on success
  576. //
  577. // History: 05/05/98 - quintinb - Created Header
  578. // ??/??/?? - henryt - Modified to work on multiple platforms. added modem stuff.
  579. // 01/12/99 - nickball - Replaced fDoDirectConnect with szCmsFile. Handled no modem case.
  580. //
  581. //+----------------------------------------------------------------------------
  582. BOOL WriteCmPhonebookEntry(LPCTSTR szLongServiceName,
  583. LPCTSTR szFullPathtoPBK,
  584. LPCTSTR pszCmsFile)
  585. {
  586. pfnRasSetEntryPropertiesSpec pfnSetEntryProperties;
  587. DWORD dwRet = 1;
  588. CPlatform plat;
  589. RASENTRY *pRasEntry = NULL;
  590. BOOL bReturn = FALSE;
  591. DWORD dwReturn;
  592. BOOL fSupportDialup;
  593. BOOL fSupportDirect;
  594. BOOL fDoDirectConnect;
  595. BOOL fSeekVpn;
  596. const TCHAR* const c_pszOne = TEXT("1");
  597. MYDBGASSERT(szLongServiceName);
  598. MYDBGASSERT(pszCmsFile);
  599. if (NULL == szLongServiceName || NULL == pszCmsFile)
  600. {
  601. return FALSE;
  602. }
  603. CMTRACE2(TEXT("WriteCmPhonebookEntry() - szLongServiceName is %s, szFullPathtoPBK is %s"), szLongServiceName, szFullPathtoPBK ? szFullPathtoPBK : TEXT("<NULL>"));
  604. if (!GetRasApis(NULL, NULL, &pfnSetEntryProperties, NULL, NULL, NULL))
  605. {
  606. return FALSE;
  607. }
  608. //
  609. // alloc RASENTRY properly
  610. //
  611. if (plat.IsAtLeastNT5())
  612. {
  613. RASENTRY_V500 *pRasEntryV500 = (RASENTRY_V500 *)CmMalloc(sizeof(RASENTRY_V500));
  614. if (!pRasEntryV500)
  615. {
  616. CMTRACE(TEXT("WriteCmPhonebookEntry failed to alloc mem"));
  617. goto exit;
  618. }
  619. ZeroMemory(pRasEntryV500, sizeof(RASENTRY_V500));
  620. pRasEntryV500->dwSize = sizeof(RASENTRY_V500);
  621. pRasEntryV500->dwType = RASET_Internet;
  622. pRasEntry = (RASENTRY *)pRasEntryV500;
  623. }
  624. else
  625. {
  626. pRasEntry = (RASENTRY *)CmMalloc(sizeof(RASENTRY));
  627. if (!pRasEntry)
  628. {
  629. CMTRACE(TEXT("WriteCmPhonebookEntry failed to alloc mem"));
  630. goto exit;
  631. }
  632. pRasEntry->dwSize = sizeof(RASENTRY);
  633. }
  634. //
  635. // Update the RAS entry with our DLL name for AutoDial and CustomDial
  636. // Note: NT5 gets CustomDial only, no AutoDial and AutoDialFunc.
  637. //
  638. if (plat.IsAtLeastNT5())
  639. {
  640. //
  641. // Use the machine independent %windir%\system32\cmdial32.dll on NT5
  642. //
  643. lstrcpy(((RASENTRY_V500 *)pRasEntry)->szCustomDialDll, c_pszCmDialPath);
  644. }
  645. else
  646. {
  647. TCHAR szSystemDirectory[MAX_PATH+1];
  648. //
  649. // Specify _InetDialHandler@16 as the entry point used for AutoDial.
  650. //
  651. lstrcpy(pRasEntry->szAutodialFunc, c_pszInetDialHandler);
  652. //
  653. // Get the system directory path
  654. //
  655. if (0 == GetSystemDirectory(szSystemDirectory, CELEMS(szSystemDirectory)))
  656. {
  657. goto exit;
  658. }
  659. UINT uCount = (UINT)wsprintf(pRasEntry->szAutodialDll, TEXT("%s\\cmdial32.dll"), szSystemDirectory);
  660. MYDBGASSERT(uCount < CELEMS(pRasEntry->szAutodialDll));
  661. }
  662. if (plat.IsWin9x())
  663. {
  664. //
  665. // Win9x requires these to be set
  666. //
  667. pRasEntry->dwFramingProtocol = RASFP_Ppp;
  668. pRasEntry->dwCountryID = 1;
  669. pRasEntry->dwCountryCode = 1;
  670. //lstrcpy(pRasEntry->szAreaCode, TEXT("425"));
  671. lstrcpy(pRasEntry->szLocalPhoneNumber, TEXT("default"));
  672. }
  673. //
  674. // Is the profile configured to first use Direct Connect
  675. //
  676. fSupportDialup = GetPrivateProfileInt(c_pszCmSection, c_pszCmEntryDialup, 1, pszCmsFile);
  677. fSupportDirect = GetPrivateProfileInt (c_pszCmSection, c_pszCmEntryDirect, 1, pszCmsFile);
  678. fDoDirectConnect = ((fSupportDialup && fSupportDirect &&
  679. GetPrivateProfileInt(c_pszCmSection, c_pszCmEntryConnectionType, 0, pszCmsFile)) ||
  680. (!fSupportDialup));
  681. fSeekVpn = fDoDirectConnect;
  682. //
  683. // First try dial-up if appropriate
  684. //
  685. if (!fDoDirectConnect && !PickModem(pRasEntry->szDeviceType, pRasEntry->szDeviceName, FALSE))
  686. {
  687. CMTRACE(TEXT("*******Failed to pick a dial-up device!!!!"));
  688. //
  689. // If direct capable, try to find a VPN device
  690. //
  691. fSeekVpn = fSupportDirect;
  692. }
  693. //
  694. // If seeking a VPN device
  695. //
  696. if (fSeekVpn)
  697. {
  698. if (!PickModem(pRasEntry->szDeviceType, pRasEntry->szDeviceName, TRUE))
  699. {
  700. CMTRACE(TEXT("*******Failed to pick a VPN device!!!!"));
  701. }
  702. else
  703. {
  704. //
  705. // Found VPN device, set default type as appropriate
  706. //
  707. if (!fDoDirectConnect)
  708. {
  709. CFileNameParts CmsParts(pszCmsFile);
  710. TCHAR szCmpFile[MAX_PATH+1];
  711. MYVERIFY(CELEMS(szCmpFile) > (UINT)wsprintf(szCmpFile, TEXT("%s%s"), CmsParts.m_Drive,
  712. CmsParts.m_Dir));
  713. szCmpFile[lstrlen(szCmpFile) - 1] = TEXT('\0');
  714. lstrcat(szCmpFile, c_pszCmpExt);
  715. WritePrivateProfileString(c_pszCmSection, c_pszCmEntryConnectionType, c_pszOne, szCmpFile);
  716. }
  717. }
  718. }
  719. //
  720. // No device??? Use last resort for dial-up on NT5
  721. //
  722. if (plat.IsAtLeastNT5() && !pRasEntry->szDeviceType[0])
  723. {
  724. lstrcpy(pRasEntry->szDeviceType, RASDT_Modem);
  725. lstrcpy(pRasEntry->szDeviceName, TEXT("Unavailable device ()"));
  726. CMTRACE2(TEXT("*******Writing szDeviceType - %s and szDeviceName %s"),
  727. pRasEntry->szDeviceType, pRasEntry->szDeviceName);
  728. }
  729. //
  730. // Zero is the success return value from RasSetEntryProperties
  731. //
  732. dwReturn = ((pfnSetEntryProperties)(szFullPathtoPBK, szLongServiceName,
  733. pRasEntry, pRasEntry->dwSize, NULL, 0));
  734. if (ERROR_SUCCESS == dwReturn)
  735. {
  736. bReturn = TRUE;
  737. }
  738. CMTRACE1(TEXT("WriteCmPhonebookEntry() - RasSetEntryProperties failed with error %d"), dwReturn);
  739. exit:
  740. CmFree(pRasEntry);
  741. return bReturn;
  742. }
  743. //+----------------------------------------------------------------------------
  744. //
  745. // Function: GetRasModems
  746. //
  747. // Synopsis: get a list of modem devices from RAS
  748. //
  749. // Arguments: pprdiRasDevInfo Ras device info list
  750. // pdwCnt modem count
  751. //
  752. // Returns: TRUE, if a list is obtained
  753. //
  754. //+----------------------------------------------------------------------------
  755. BOOL GetRasModems(
  756. LPRASDEVINFO *pprdiRasDevInfo,
  757. LPDWORD pdwCnt
  758. )
  759. {
  760. DWORD dwLen;
  761. DWORD dwRes;
  762. DWORD dwCnt;
  763. pfnRasEnumDevicesSpec pfnEnumDevices;
  764. if (pprdiRasDevInfo)
  765. {
  766. *pprdiRasDevInfo = NULL;
  767. }
  768. if (pdwCnt)
  769. {
  770. *pdwCnt = 0;
  771. }
  772. if (!GetRasApis(NULL, NULL, NULL, &pfnEnumDevices, NULL, NULL))
  773. {
  774. return FALSE;
  775. }
  776. dwLen = 0;
  777. dwRes = pfnEnumDevices(NULL, &dwLen, &dwCnt);
  778. CMTRACE3(TEXT("GetRasModems() RasEnumDevices(NULL,pdwLen,&dwCnt) returns %u, dwLen=%u, dwCnt=%u."), dwRes, dwLen, dwCnt);
  779. if ((dwRes != ERROR_SUCCESS) && (dwRes != ERROR_BUFFER_TOO_SMALL) ||
  780. (dwLen < sizeof(**pprdiRasDevInfo)))
  781. {
  782. return FALSE;
  783. }
  784. *pprdiRasDevInfo = (LPRASDEVINFO) CmMalloc(__max(dwLen, sizeof(**pprdiRasDevInfo)));
  785. if (*pprdiRasDevInfo)
  786. {
  787. (*pprdiRasDevInfo)->dwSize = sizeof(**pprdiRasDevInfo);
  788. dwRes = pfnEnumDevices(*pprdiRasDevInfo, &dwLen, &dwCnt);
  789. CMTRACE3(TEXT("GetRasModems() RasEnumDevices(NULL,pdwLen,&dwCnt) returns %u, dwLen=%u, dwCnt=%u."), dwRes, dwLen, dwCnt);
  790. if (dwRes != ERROR_SUCCESS)
  791. {
  792. CmFree(*pprdiRasDevInfo);
  793. *pprdiRasDevInfo = NULL;
  794. return FALSE;
  795. }
  796. if (pdwCnt)
  797. {
  798. *pdwCnt = dwCnt;
  799. }
  800. }
  801. else
  802. {
  803. CMASSERTMSG(FALSE, TEXT("GetRasModems -- CmMalloc returned a NULL pointer for *pprdiRasDevInfo."));
  804. return FALSE;
  805. }
  806. return TRUE;
  807. }
  808. //+----------------------------------------------------------------------------
  809. //
  810. // Function: PickModem
  811. //
  812. // Synopsis: Pick a default modem
  813. //
  814. // Arguments: OUT pszDeviceType, the device type if not NULL
  815. // OUT pszDeviceName, the device name if not NULL
  816. // OUT fUseVpnDevice Use VPN device or not
  817. //
  818. // Returns: TRUE, is modem is found
  819. //
  820. //+----------------------------------------------------------------------------
  821. BOOL PickModem(
  822. LPTSTR pszDeviceType,
  823. LPTSTR pszDeviceName,
  824. BOOL fUseVpnDevice
  825. )
  826. {
  827. LPRASDEVINFO prdiModems;
  828. DWORD dwCnt;
  829. DWORD dwIdx;
  830. //
  831. // First, get a list of modems from RAS
  832. //
  833. if (!GetRasModems(&prdiModems, &dwCnt) || dwCnt == 0)
  834. {
  835. return FALSE;
  836. }
  837. //
  838. // find the first device and use it by default.
  839. // Use VPN device if it's a VPN connection.
  840. //
  841. for (dwIdx=0; dwIdx<dwCnt; dwIdx++)
  842. {
  843. if (fUseVpnDevice && !lstrcmpi(prdiModems[dwIdx].szDeviceType, RASDT_Vpn) ||
  844. !fUseVpnDevice && (!lstrcmpi(prdiModems[dwIdx].szDeviceType, RASDT_Isdn) ||
  845. !lstrcmpi(prdiModems[dwIdx].szDeviceType, RASDT_Modem)))
  846. {
  847. break;
  848. }
  849. }
  850. //
  851. // If we have a match, fill device name and device type
  852. //
  853. if (dwIdx < dwCnt)
  854. {
  855. if (pszDeviceType)
  856. {
  857. lstrcpy(pszDeviceType, prdiModems[dwIdx].szDeviceType);
  858. }
  859. if (pszDeviceName)
  860. {
  861. lstrcpy(pszDeviceName, prdiModems[dwIdx].szDeviceName);
  862. }
  863. }
  864. CmFree(prdiModems);
  865. return (dwIdx < dwCnt);
  866. }
  867. //+----------------------------------------------------------------------------
  868. //
  869. // Function: IsMemberOfGroup
  870. //
  871. // Synopsis: This function return TRUE if the current user is a member of
  872. // the passed and FALSE passed in Group RID.
  873. //
  874. // Arguments: DWORD dwGroupRID -- the RID of the group to check membership of
  875. // BOOL bUseBuiltinDomainRid -- whether the SECURITY_BUILTIN_DOMAIN_RID
  876. // RID should be used to build the Group
  877. // SID
  878. //
  879. // Returns: BOOL - TRUE if the user is a member of the specified group
  880. //
  881. // History: quintinb Shamelessly stolen from MSDN 02/19/98
  882. // quintinb Reworked and renamed 06/18/99
  883. // to apply to more than just Admins
  884. // quintinb Rewrote to use CheckTokenMemberShip 08/18/99
  885. // since the MSDN method was no longer
  886. // correct on NT5 -- 389229
  887. //
  888. //+----------------------------------------------------------------------------
  889. BOOL IsMemberOfGroup(DWORD dwGroupRID, BOOL bUseBuiltinDomainRid)
  890. {
  891. CPlatform cmplat;
  892. PSID psidGroup = NULL;
  893. SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
  894. BOOL bSuccess = FALSE;
  895. if (FALSE == cmplat.IsAtLeastNT5())
  896. {
  897. CMASSERTMSG(FALSE, TEXT("IsMemberOfGroup -- Trying to use an NT5 only function on a downlevel platform."));
  898. return FALSE;
  899. }
  900. //
  901. // Make a SID for the Group we are checking for, Note that we if we need the Built
  902. // in Domain RID (for Groups like Administrators, PowerUsers, Users, etc)
  903. // then we will have two entries to pass to AllocateAndInitializeSid. Otherwise,
  904. // (for groups like Authenticated Users) we will only have one.
  905. //
  906. BYTE byNum;
  907. DWORD dwFirstRID;
  908. DWORD dwSecondRID;
  909. if (bUseBuiltinDomainRid)
  910. {
  911. byNum = 2;
  912. dwFirstRID = SECURITY_BUILTIN_DOMAIN_RID;
  913. dwSecondRID = dwGroupRID;
  914. }
  915. else
  916. {
  917. byNum = 1;
  918. dwFirstRID = dwGroupRID;
  919. dwSecondRID = 0;
  920. }
  921. if (AllocateAndInitializeSid(&siaNtAuthority, byNum, dwFirstRID, dwSecondRID,
  922. 0, 0, 0, 0, 0, 0, &psidGroup))
  923. {
  924. //
  925. // Now we need to dynamically load the CheckTokenMemberShip API from
  926. // advapi32.dll since it is a Win2k only API.
  927. //
  928. HMODULE hAdvapi = GetModuleHandle(TEXT("advapi32.dll"));
  929. if (hAdvapi)
  930. {
  931. typedef BOOL (WINAPI *pfnCheckTokenMembershipSpec)(HANDLE, PSID, PBOOL);
  932. pfnCheckTokenMembershipSpec pfnCheckTokenMembership;
  933. pfnCheckTokenMembership = (pfnCheckTokenMembershipSpec)GetProcAddress(hAdvapi, "CheckTokenMembership");
  934. if (pfnCheckTokenMembership)
  935. {
  936. //
  937. // Check to see if the user is actually a member of the group in question
  938. //
  939. if (!(pfnCheckTokenMembership)(NULL, psidGroup, &bSuccess))
  940. {
  941. bSuccess = FALSE;
  942. CMASSERTMSG(FALSE, TEXT("CheckTokenMemberShip Failed."));
  943. }
  944. }
  945. else
  946. {
  947. CMASSERTMSG(FALSE, TEXT("IsMemberOfGroup -- GetProcAddress failed for CheckTokenMemberShip"));
  948. }
  949. }
  950. else
  951. {
  952. CMASSERTMSG(FALSE, TEXT("IsMemberOfGroup -- Unable to get the module handle for advapi32.dll"));
  953. }
  954. FreeSid (psidGroup);
  955. }
  956. return bSuccess;
  957. }
  958. //+----------------------------------------------------------------------------
  959. //
  960. // Function: IsAdmin
  961. //
  962. // Synopsis: Check to see if the user is a member of the Administrators group
  963. // or not.
  964. //
  965. // Arguments: None
  966. //
  967. // Returns: BOOL - TRUE if the current user is an Administrator
  968. //
  969. // History: quintinb Created Header 8/18/99
  970. //
  971. //+----------------------------------------------------------------------------
  972. BOOL IsAdmin(void)
  973. {
  974. return IsMemberOfGroup(DOMAIN_ALIAS_RID_ADMINS, TRUE); // TRUE == bUseBuiltinDomainRid
  975. }
  976. //+----------------------------------------------------------------------------
  977. //
  978. // Function: IsAuthenticatedUser
  979. //
  980. // Synopsis: Check to see if the current user is a member of the
  981. // Authenticated Users group.
  982. //
  983. // Arguments: None
  984. //
  985. // Returns: BOOL - TRUE if the current user is a member of the
  986. // Authenticated Users group.
  987. //
  988. // History: quintinb Created Header 8/18/99
  989. //
  990. //+----------------------------------------------------------------------------
  991. BOOL IsAuthenticatedUser(void)
  992. {
  993. return IsMemberOfGroup(SECURITY_AUTHENTICATED_USER_RID, FALSE); // FALSE == bUseBuiltinDomainRid
  994. }
  995. //+----------------------------------------------------------------------------
  996. //
  997. // Function: GetNT5FolderPath
  998. //
  999. // Synopsis: Get the folder path on NT5
  1000. // Since cmstp.exe is launched in netman by CreateProcessAsUser
  1001. // SHGetSpecialFolderPath does not work. We have to call
  1002. // SHGetFolderPath with an access token.
  1003. //
  1004. // Arguments: int nFolder - Value specifying the folder for which to retrieve
  1005. // the location.
  1006. // OUT LPTSTR lpszPath - Address of a character buffer that receives
  1007. // the drive and path of the specified folder. This
  1008. // buffer must be at least MAX_PATH characters in size.
  1009. //
  1010. // Returns: HRESULT -
  1011. //
  1012. // History: fengsun Created Header 6/18/98
  1013. // quintinb modified to use GetShell32Apis 11-22-98
  1014. //
  1015. //+----------------------------------------------------------------------------
  1016. HRESULT GetNT5FolderPath(int nFolder, OUT LPTSTR lpszPath)
  1017. {
  1018. MYDBGASSERT(lpszPath);
  1019. pfnSHGetFolderPathSpec pfnSHGetFolderPath;
  1020. //
  1021. // Call shell32.dll-->SHGetFolderPath, which takes a token.
  1022. //
  1023. if(!GetShell32Apis(&pfnSHGetFolderPath, NULL))
  1024. {
  1025. CMASSERTMSG(FALSE, TEXT("Failed to load shell32.dll or ShGetFolderPath"));
  1026. return E_UNEXPECTED;
  1027. }
  1028. //
  1029. // Get the current process token
  1030. //
  1031. HANDLE hToken; // The token of the process, to be passed to SHGetFolderPath
  1032. if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
  1033. {
  1034. CMASSERTMSG(FALSE, TEXT("OpenThreadToken failed"));
  1035. return E_UNEXPECTED;
  1036. }
  1037. HRESULT hr = pfnSHGetFolderPath(NULL, nFolder, hToken, 0, lpszPath);
  1038. MYVERIFY(0 != CloseHandle(hToken));
  1039. return hr;
  1040. }
  1041. //+----------------------------------------------------------------------------
  1042. //
  1043. // Function: HrIsCMProfilePrivate
  1044. //
  1045. // Synopsis: This function compares the inputed file path with the application
  1046. // data path of the system. If the file path contains the app data
  1047. // path then it is considered to be a private profile.
  1048. //
  1049. // Arguments: LPTSTR szFilePath - directory or file path to compare against
  1050. //
  1051. // Returns: HRESULT - S_OK if a private profile, S_FALSE if it is an all users
  1052. // profile. Standard error codes otherwise.
  1053. //
  1054. // History: quintinb original code
  1055. //
  1056. //+----------------------------------------------------------------------------
  1057. HRESULT HrIsCMProfilePrivate(LPCTSTR szFilePath)
  1058. {
  1059. UINT uiLen;
  1060. TCHAR szAppDataDir[MAX_PATH+1];
  1061. TCHAR szTemp[MAX_PATH+1] = {TEXT("")};
  1062. CPlatform plat;
  1063. if ((NULL == szFilePath) || (TEXT('\0') == szFilePath[0]))
  1064. {
  1065. return E_POINTER;
  1066. }
  1067. //
  1068. // Can't be a private user profile unless we are on NT5
  1069. //
  1070. if (!(plat.IsAtLeastNT5()))
  1071. {
  1072. return S_FALSE;
  1073. }
  1074. //
  1075. // Figure out what the user directory of the current user is. We can compare this
  1076. // against the directory of the phonebook and see if we have a private user
  1077. // profile or an all user profile.
  1078. if (FAILED(GetNT5FolderPath(CSIDL_APPDATA, szAppDataDir)))
  1079. {
  1080. return E_UNEXPECTED;
  1081. }
  1082. uiLen = lstrlen(szAppDataDir) + 1;
  1083. lstrcpyn(szTemp, szFilePath, uiLen);
  1084. if ((NULL != szTemp) && (0 == lstrcmpi(szAppDataDir, szTemp)))
  1085. {
  1086. return S_OK;
  1087. }
  1088. else
  1089. {
  1090. return S_FALSE;
  1091. }
  1092. }
  1093. //+----------------------------------------------------------------------------
  1094. //
  1095. // Function: RefreshDesktop
  1096. //
  1097. // Synopsis: This function refreshes the desktop and basically takes the place
  1098. // of showicon.exe (in fact the code is a cut and paste from the
  1099. // main of showicon).
  1100. //
  1101. // Arguments: None
  1102. //
  1103. // Returns: Nothing
  1104. //
  1105. // History: quintinb Created Header 5/5/98
  1106. //
  1107. //+----------------------------------------------------------------------------
  1108. void RefreshDesktop()
  1109. {
  1110. LPMALLOC pMalloc = NULL;
  1111. LPITEMIDLIST pItemIDList = NULL;
  1112. //
  1113. // Get the IMalloc for the Shell.
  1114. //
  1115. HRESULT hr = SHGetMalloc(&pMalloc);
  1116. if (SUCCEEDED(hr))
  1117. {
  1118. // Get the desktop ID list..
  1119. hr = SHGetSpecialFolderLocation(NULL,
  1120. CSIDL_DESKTOP,
  1121. &pItemIDList);
  1122. if (SUCCEEDED(hr))
  1123. {
  1124. // Notify of change.
  1125. SHChangeNotify(SHCNE_UPDATEDIR,
  1126. SHCNF_IDLIST,
  1127. (LPCVOID)pItemIDList,
  1128. NULL);
  1129. pMalloc->Free(pItemIDList);
  1130. }
  1131. MYVERIFY(SUCCEEDED(pMalloc->Release()));
  1132. }
  1133. }
  1134. //+----------------------------------------------------------------------------
  1135. //
  1136. // Function: GetPrivateCmUserDir
  1137. //
  1138. // Synopsis: This function fills in the string passed in with the path to the
  1139. // path where CM should be installed. For instance, it should return
  1140. // c:\users\quintinb\Application Data\Microsoft\Network\Connection Manager
  1141. // for me. Please note that this function is NT5 only.
  1142. //
  1143. // Arguments: LPTSTR pszDir - String to the Users Connection Manager Directory
  1144. //
  1145. // Returns: LPTSTR - String to the Users Connection Manager Directory
  1146. //
  1147. // History: quintinb Created Header 2/19/98
  1148. //
  1149. //+----------------------------------------------------------------------------
  1150. LPTSTR GetPrivateCmUserDir(LPTSTR pszDir, HINSTANCE hInstance)
  1151. {
  1152. LPITEMIDLIST pidl;
  1153. LPMALLOC pMalloc;
  1154. CPlatform plat;
  1155. TCHAR szTemp[MAX_PATH+1];
  1156. MYDBGASSERT(pszDir);
  1157. pszDir[0] = TEXT('\0');
  1158. if (!plat.IsAtLeastNT5())
  1159. {
  1160. CMASSERTMSG(FALSE, TEXT("GetPrivateCmUserDir -- This NT5 only function was called from a different platform."));
  1161. goto exit;
  1162. }
  1163. if (FAILED(GetNT5FolderPath(CSIDL_APPDATA, pszDir)))
  1164. {
  1165. goto exit;
  1166. }
  1167. MYVERIFY(0 != LoadString(hInstance, IDS_CMSUBFOLDER, szTemp, MAX_PATH));
  1168. MYVERIFY(NULL != lstrcat(pszDir, szTemp));
  1169. exit:
  1170. return pszDir;
  1171. }
  1172. //+----------------------------------------------------------------------------
  1173. //
  1174. // Function: LaunchProfile
  1175. //
  1176. // Synopsis: This function handles launching the CM profile (NTRAID 201307) after
  1177. // installation. On NT5 it opens the connfolder and launches the
  1178. // correct connection by doing a shell execute on the pidl we get from
  1179. // enumerating the connections folder. On down level we use Cmmgr32.exe
  1180. // and the full path to the cmp file. Please note that on downlevel we
  1181. // only care about the input param pszFullPathToCmpFile, while on NT5
  1182. // we only care about pszwServiceName and bInstallForAllUsers.
  1183. //
  1184. // Arguments: LPCTSTR pszFullPathToCmpFile - the full path to the cmp file (used on legacy only)
  1185. // LPCSTR pszServiceName - the Long Service Name
  1186. // BOOL bInstallForAllUsers -
  1187. //
  1188. // Returns: HRESULT -- standard COM error codes
  1189. //
  1190. // History: quintinb Created 11/16/98
  1191. //
  1192. //+----------------------------------------------------------------------------
  1193. HRESULT LaunchProfile(LPCTSTR pszFullPathToCmpFile, LPCTSTR pszServiceName,
  1194. LPCTSTR pszPhoneBook, BOOL bInstallForAllUsers)
  1195. {
  1196. CPlatform plat;
  1197. HRESULT hr = E_FAIL;
  1198. if ((NULL == pszFullPathToCmpFile) || (NULL == pszServiceName) ||
  1199. (NULL != pszPhoneBook && TEXT('\0') == pszPhoneBook[0]))
  1200. {
  1201. CMASSERTMSG(FALSE, TEXT("Invalid argument passed to LaunchProfile"));
  1202. return E_INVALIDARG;
  1203. }
  1204. if (plat.IsAtLeastNT5())
  1205. {
  1206. CMASSERTMSG((TEXT('\0') != pszServiceName), TEXT("Empty ServiceName passed to LaunchProfile on win2k."));
  1207. pfnRasGetEntryPropertiesSpec pfnGetEntryProperties = NULL;
  1208. if (!GetRasApis(NULL, NULL, NULL, NULL, &pfnGetEntryProperties, NULL))
  1209. {
  1210. return E_UNEXPECTED;
  1211. }
  1212. DWORD dwRes;
  1213. DWORD dwSize;
  1214. LPRASENTRY_V500 pRasEntry = NULL;
  1215. pRasEntry = (LPRASENTRY_V500)CmMalloc(sizeof(RASENTRY_V500));
  1216. if (NULL != pRasEntry)
  1217. {
  1218. ZeroMemory(pRasEntry, sizeof(RASENTRY_V500));
  1219. pRasEntry->dwSize = sizeof(RASENTRY_V500);
  1220. dwSize = sizeof(RASENTRY_V500);
  1221. dwRes = (pfnGetEntryProperties)(pszPhoneBook, pszServiceName, (LPRASENTRY)pRasEntry, &dwSize, NULL, NULL);
  1222. if (0 == dwRes)
  1223. {
  1224. //
  1225. // Then we were able to get the RasEntry, load the NetShell API
  1226. // and call HrCreateShortcut
  1227. //
  1228. if (plat.IsAtLeastNT51())
  1229. {
  1230. pfnLaunchConnectionExSpec pfnLaunchConnectionEx = NULL;
  1231. if (GetNetShellApis(NULL, NULL, &pfnLaunchConnectionEx))
  1232. {
  1233. //
  1234. // Launch Connections Folder and Connection together
  1235. //
  1236. DWORD dwFlags = 0x1; // 0x1 => Opens the folder before launching the connection
  1237. hr = (pfnLaunchConnectionEx)(dwFlags, pRasEntry->guidId);
  1238. MYVERIFY(SUCCEEDED(hr));
  1239. }
  1240. }
  1241. else
  1242. {
  1243. pfnLaunchConnectionSpec pfnLaunchConnection = NULL;
  1244. if (GetNetShellApis(&pfnLaunchConnection, NULL, NULL))
  1245. {
  1246. //
  1247. // Now Launch the Connections Folder
  1248. //
  1249. CLoadConnFolder Connections;
  1250. Connections.HrLaunchConnFolder();
  1251. //
  1252. // Finally Launch the Connection
  1253. //
  1254. hr = (pfnLaunchConnection)(pRasEntry->guidId);
  1255. MYVERIFY(SUCCEEDED(hr));
  1256. }
  1257. }
  1258. }
  1259. else
  1260. {
  1261. CMTRACE1(TEXT("LaunchProfile -- RasGetEntryProperties returned %u"), dwRes);
  1262. CMASSERTMSG(FALSE, TEXT("Unable to find the connection that we are supposed to launch in the RAS pbk."));
  1263. return HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
  1264. }
  1265. CmFree(pRasEntry);
  1266. }
  1267. }
  1268. else
  1269. {
  1270. SHELLEXECUTEINFO sei;
  1271. if ((NULL != pszFullPathToCmpFile) && (TEXT('\0') != pszFullPathToCmpFile))
  1272. {
  1273. TCHAR szCmmgrPath[MAX_PATH+1];
  1274. TCHAR szSystemDir[MAX_PATH+1];
  1275. TCHAR szCmp[MAX_PATH+1];
  1276. ZeroMemory(&szCmp, sizeof(szCmp));
  1277. ZeroMemory(&szCmmgrPath, sizeof(szCmmgrPath));
  1278. ZeroMemory(&szSystemDir, sizeof(szSystemDir));
  1279. lstrcpy(szCmp, TEXT("\""));
  1280. lstrcat(szCmp, pszFullPathToCmpFile);
  1281. lstrcat(szCmp, TEXT("\""));
  1282. UINT uRet = GetSystemDirectory(szSystemDir, MAX_PATH);
  1283. if ((0 == uRet) || (MAX_PATH < uRet))
  1284. {
  1285. //
  1286. // Give up, not the end of the world not to launch the profile
  1287. //
  1288. return E_UNEXPECTED;
  1289. }
  1290. else
  1291. {
  1292. wsprintf(szCmmgrPath, TEXT("%s\\cmmgr32.exe"), szSystemDir);
  1293. }
  1294. ZeroMemory(&sei, sizeof(sei));
  1295. sei.cbSize = sizeof(sei);
  1296. sei.fMask = SEE_MASK_FLAG_NO_UI;
  1297. sei.nShow = SW_SHOWNORMAL;
  1298. sei.lpFile = szCmmgrPath;
  1299. sei.lpParameters = szCmp;
  1300. sei.lpDirectory = szSystemDir;
  1301. if (!ShellExecuteEx(&sei))
  1302. {
  1303. CMASSERTMSG(FALSE, TEXT("Unable to launch installed connection!"));
  1304. }
  1305. else
  1306. {
  1307. hr = S_OK;
  1308. }
  1309. }
  1310. }
  1311. return hr;
  1312. }
  1313. //+----------------------------------------------------------------------------
  1314. //
  1315. // Function: AllUserProfilesInstalled
  1316. //
  1317. // Synopsis: Checks if any profiles are listed in the HKLM Mappings key.
  1318. //
  1319. // Arguments: None
  1320. //
  1321. // Returns: BOOL - TRUE if mappings values exist in the HKLM mappings key
  1322. //
  1323. // History: quintinb Created Header 11/1/98
  1324. //
  1325. //+----------------------------------------------------------------------------
  1326. BOOL AllUserProfilesInstalled()
  1327. {
  1328. BOOL bReturn = FALSE;
  1329. HKEY hKey;
  1330. DWORD dwNumValues;
  1331. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0,
  1332. KEY_READ, &hKey))
  1333. {
  1334. if ((ERROR_SUCCESS == RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL,
  1335. &dwNumValues, NULL, NULL, NULL, NULL)) && (dwNumValues > 0))
  1336. {
  1337. //
  1338. // Then we have mappings values
  1339. //
  1340. bReturn = TRUE;
  1341. }
  1342. RegCloseKey(hKey);
  1343. }
  1344. return bReturn;
  1345. }
  1346. //+----------------------------------------------------------------------------
  1347. //
  1348. // Function: GetProcAddressFromRasApi32orRnaph
  1349. //
  1350. // Synopsis: A helper function to first look in RasApi32.dll (using the global
  1351. // dll class pointer) and then check in Rnaph.dll if the required
  1352. // function was not found.
  1353. //
  1354. // Arguments: LPTSTR pszFunc - String of the function to look for
  1355. // CPlatform* pPlat - a CPlatform class pointer to prevent creating
  1356. // and destructing a new one everytime this is called.
  1357. //
  1358. // Returns: LPVOID - NULL if the function wasn't found, a pFunc otherwise.
  1359. //
  1360. // History: quintinb Created 11/23/98
  1361. //
  1362. //+----------------------------------------------------------------------------
  1363. LPVOID GetProcAddressFromRasApi32orRnaph(LPCSTR pszFunc, CPlatform* pPlat)
  1364. {
  1365. LPVOID pFunc;
  1366. MYDBGASSERT(g_pRasApi32);
  1367. pFunc = g_pRasApi32->GetProcAddress(pszFunc);
  1368. if (NULL == pFunc)
  1369. {
  1370. //
  1371. // On win95 gold check rnaph
  1372. //
  1373. if (pPlat->IsWin95Gold())
  1374. {
  1375. if (NULL == g_pRnaph)
  1376. {
  1377. g_pRnaph = (CDynamicLibrary*)CmMalloc(sizeof(CDynamicLibrary));
  1378. if (NULL == g_pRnaph)
  1379. {
  1380. return FALSE;
  1381. }
  1382. }
  1383. if (!(g_pRnaph->IsLoaded()))
  1384. {
  1385. g_pRnaph->Load(TEXT("rnaph.dll"));
  1386. }
  1387. pFunc = g_pRnaph->GetProcAddress(pszFunc);
  1388. }
  1389. }
  1390. return pFunc;
  1391. }
  1392. //+----------------------------------------------------------------------------
  1393. //
  1394. // Function: GetNetShellApis
  1395. //
  1396. // Synopsis: This is a wrapper function to access the private Netshell api's that allow
  1397. // cmstp.exe to interact with the Connections folder on Windows 2000.
  1398. // This function caches the Netshell function pointers as they are
  1399. // accessed for later use. NULL can be passed if a function isn't required.
  1400. //
  1401. // Arguments: pfnLaunchConnectionSpec* pLaunchConnection - var to hold function pointer
  1402. // pfnCreateShortcutSpec* pCreateShortcut - var to hold function pointer
  1403. // pfnLaunchConnectionEx pLaunchConnectionEx - var to hold function pointer
  1404. //
  1405. // Returns: BOOL - TRUE if all required APIs were retrieved
  1406. //
  1407. // History: quintinb Created 2/17/99
  1408. //
  1409. //+----------------------------------------------------------------------------
  1410. BOOL GetNetShellApis(pfnLaunchConnectionSpec* pLaunchConnection, pfnCreateShortcutSpec* pCreateShortcut,
  1411. pfnLaunchConnectionExSpec* pLaunchConnectionEx)
  1412. {
  1413. CPlatform plat;
  1414. static pfnLaunchConnectionSpec pfnLaunchConnection = NULL;
  1415. static pfnCreateShortcutSpec pfnCreateShortcut = NULL;
  1416. static pfnLaunchConnectionExSpec pfnLaunchConnectionEx = NULL;
  1417. if (!(plat.IsAtLeastNT5()))
  1418. {
  1419. //
  1420. // These functions are only used on NT5. Return FALSE otherwise.
  1421. //
  1422. CMASSERTMSG(FALSE, TEXT("Trying to use NetShell Private Api's on platforms other than Windows 2000."));
  1423. return FALSE;
  1424. }
  1425. if (NULL == g_pNetShell)
  1426. {
  1427. g_pNetShell = (CDynamicLibrary*)CmMalloc(sizeof(CDynamicLibrary));
  1428. if (NULL == g_pNetShell)
  1429. {
  1430. return FALSE;
  1431. }
  1432. }
  1433. if (!(g_pNetShell->IsLoaded()))
  1434. {
  1435. g_pNetShell->Load(TEXT("netshell.dll"));
  1436. }
  1437. if (NULL != pLaunchConnection)
  1438. {
  1439. if (pfnLaunchConnection)
  1440. {
  1441. *pLaunchConnection = pfnLaunchConnection;
  1442. }
  1443. else
  1444. {
  1445. *pLaunchConnection = (pfnLaunchConnectionSpec)g_pNetShell->GetProcAddress("HrLaunchConnection");
  1446. if (NULL == *pLaunchConnection)
  1447. {
  1448. return FALSE;
  1449. }
  1450. else
  1451. {
  1452. pfnLaunchConnection = *pLaunchConnection;
  1453. }
  1454. }
  1455. }
  1456. if (NULL != pCreateShortcut)
  1457. {
  1458. if (pfnCreateShortcut)
  1459. {
  1460. *pCreateShortcut = pfnCreateShortcut;
  1461. }
  1462. else
  1463. {
  1464. *pCreateShortcut = (pfnCreateShortcutSpec)g_pNetShell->GetProcAddress("HrCreateDesktopIcon");
  1465. if (NULL == *pCreateShortcut)
  1466. {
  1467. return FALSE;
  1468. }
  1469. else
  1470. {
  1471. pfnCreateShortcut = *pCreateShortcut;
  1472. }
  1473. }
  1474. }
  1475. if (NULL != pLaunchConnectionEx)
  1476. {
  1477. if (pfnLaunchConnectionEx)
  1478. {
  1479. *pLaunchConnectionEx = pfnLaunchConnectionEx;
  1480. }
  1481. else
  1482. {
  1483. if (!(plat.IsAtLeastNT51()))
  1484. {
  1485. return FALSE;
  1486. }
  1487. else
  1488. {
  1489. *pLaunchConnectionEx = (pfnLaunchConnectionExSpec)g_pNetShell->GetProcAddress("HrLaunchConnectionEx");
  1490. if (NULL == *pLaunchConnectionEx)
  1491. {
  1492. return FALSE;
  1493. }
  1494. else
  1495. {
  1496. pfnLaunchConnectionEx = *pLaunchConnectionEx;
  1497. }
  1498. }
  1499. }
  1500. }
  1501. return TRUE;
  1502. }
  1503. //+----------------------------------------------------------------------------
  1504. //
  1505. // Function: GetRasApis
  1506. //
  1507. // Synopsis: This is a wrapper function to access the RasApis that cmstp.exe uses.
  1508. // This function caches the RAS api function pointers as they are
  1509. // accessed for later use. NULL can be passed if a function isn't required.
  1510. //
  1511. // Arguments: pfnRasDeleteEntrySpec* pRasDeleteEntry - var to hold func pointer
  1512. // pfnRasEnumEntriesSpec* pRasEnumEntries - var to hold func pointer
  1513. // pfnRasSetEntryPropertiesSpec* pRasSetEntryProperties - var to hold func pointer
  1514. // pfnRasEnumDevicesSpec* pRasEnumDevices - var to hold func pointer
  1515. // pfnRasSetCredentialsSpec* pRasSetCredentials - var to hold func pointer
  1516. //
  1517. // Returns: BOOL - TRUE if all required APIs were retrieved
  1518. //
  1519. // History: quintinb Created 11/23/98
  1520. //
  1521. //+----------------------------------------------------------------------------
  1522. BOOL GetRasApis(pfnRasDeleteEntrySpec* pRasDeleteEntry, pfnRasEnumEntriesSpec* pRasEnumEntries,
  1523. pfnRasSetEntryPropertiesSpec* pRasSetEntryProperties,
  1524. pfnRasEnumDevicesSpec* pRasEnumDevices, pfnRasGetEntryPropertiesSpec* pRasGetEntryProperties,
  1525. pfnRasSetCredentialsSpec* pRasSetCredentials)
  1526. {
  1527. CPlatform plat;
  1528. static pfnRasDeleteEntrySpec pfnRasDeleteEntry = NULL;
  1529. static pfnRasEnumEntriesSpec pfnRasEnumEntries = NULL;
  1530. static pfnRasSetEntryPropertiesSpec pfnRasSetEntryProperties = NULL;
  1531. static pfnRasEnumDevicesSpec pfnRasEnumDevices = NULL;
  1532. static pfnRasGetEntryPropertiesSpec pfnRasGetEntryProperties = NULL;
  1533. static pfnRasSetCredentialsSpec pfnRasSetCredentials = NULL;
  1534. if (NULL == g_pRasApi32)
  1535. {
  1536. g_pRasApi32 = (CDynamicLibrary*)CmMalloc(sizeof(CDynamicLibrary));
  1537. if (NULL == g_pRasApi32)
  1538. {
  1539. return FALSE;
  1540. }
  1541. }
  1542. if (!(g_pRasApi32->IsLoaded()))
  1543. {
  1544. g_pRasApi32->Load(TEXT("rasapi32.dll"));
  1545. }
  1546. if (NULL != pRasDeleteEntry)
  1547. {
  1548. if (pfnRasDeleteEntry)
  1549. {
  1550. *pRasDeleteEntry = pfnRasDeleteEntry;
  1551. }
  1552. else
  1553. {
  1554. *pRasDeleteEntry = (pfnRasDeleteEntrySpec)GetProcAddressFromRasApi32orRnaph("RasDeleteEntryA",
  1555. &plat);
  1556. if (NULL == *pRasDeleteEntry)
  1557. {
  1558. return FALSE;
  1559. }
  1560. else
  1561. {
  1562. pfnRasDeleteEntry = *pRasDeleteEntry;
  1563. }
  1564. }
  1565. }
  1566. if (NULL != pRasEnumEntries)
  1567. {
  1568. if (pfnRasEnumEntries)
  1569. {
  1570. *pRasEnumEntries = pfnRasEnumEntries;
  1571. }
  1572. else
  1573. {
  1574. *pRasEnumEntries = (pfnRasEnumEntriesSpec)g_pRasApi32->GetProcAddress("RasEnumEntriesA");
  1575. if (NULL == *pRasEnumEntries)
  1576. {
  1577. //
  1578. // A required Function couldn't be loaded
  1579. //
  1580. return FALSE;
  1581. }
  1582. else
  1583. {
  1584. pfnRasEnumEntries = *pRasEnumEntries;
  1585. }
  1586. }
  1587. }
  1588. if (NULL != pRasSetEntryProperties)
  1589. {
  1590. if (pfnRasSetEntryProperties)
  1591. {
  1592. *pRasSetEntryProperties = pfnRasSetEntryProperties;
  1593. }
  1594. else
  1595. {
  1596. *pRasSetEntryProperties = (pfnRasSetEntryPropertiesSpec)GetProcAddressFromRasApi32orRnaph("RasSetEntryPropertiesA",
  1597. &plat);
  1598. if (NULL == *pRasSetEntryProperties)
  1599. {
  1600. return FALSE;
  1601. }
  1602. else
  1603. {
  1604. pfnRasSetEntryProperties = *pRasSetEntryProperties;
  1605. }
  1606. }
  1607. }
  1608. if (NULL != pRasEnumDevices)
  1609. {
  1610. if (pfnRasEnumDevices)
  1611. {
  1612. *pRasEnumDevices = pfnRasEnumDevices;
  1613. }
  1614. else
  1615. {
  1616. *pRasEnumDevices = (pfnRasEnumDevicesSpec)GetProcAddressFromRasApi32orRnaph("RasEnumDevicesA",
  1617. &plat);
  1618. if (NULL == *pRasEnumDevices)
  1619. {
  1620. return FALSE;
  1621. }
  1622. else
  1623. {
  1624. pfnRasEnumDevices = *pRasEnumDevices;
  1625. }
  1626. }
  1627. }
  1628. if (NULL != pRasGetEntryProperties)
  1629. {
  1630. if (pfnRasGetEntryProperties)
  1631. {
  1632. *pRasGetEntryProperties = pfnRasGetEntryProperties;
  1633. }
  1634. else
  1635. {
  1636. *pRasGetEntryProperties = (pfnRasGetEntryPropertiesSpec)GetProcAddressFromRasApi32orRnaph("RasGetEntryPropertiesA", &plat);
  1637. if (NULL == *pRasGetEntryProperties)
  1638. {
  1639. return FALSE;
  1640. }
  1641. else
  1642. {
  1643. pfnRasGetEntryProperties = *pRasGetEntryProperties;
  1644. }
  1645. }
  1646. }
  1647. if (NULL != pRasSetCredentials)
  1648. {
  1649. if (pfnRasSetCredentials)
  1650. {
  1651. *pRasSetCredentials = pfnRasSetCredentials;
  1652. }
  1653. else
  1654. {
  1655. *pRasSetCredentials = (pfnRasSetCredentialsSpec)GetProcAddressFromRasApi32orRnaph("RasSetCredentialsA", &plat);
  1656. if (NULL == *pRasSetCredentials)
  1657. {
  1658. return FALSE;
  1659. }
  1660. else
  1661. {
  1662. pfnRasSetCredentials = *pRasSetCredentials;
  1663. }
  1664. }
  1665. }
  1666. return TRUE;
  1667. }
  1668. //+----------------------------------------------------------------------------
  1669. //
  1670. // Function: GetShell32Apis
  1671. //
  1672. // Synopsis: This function is used to load the shell32.dll and call getprocaddress
  1673. // on the needed functions. This function is used to speed up the process
  1674. // by keeping one copy of shell32.dll in memory and caching the function
  1675. // pointers requested. If a function pointer hasn't been requested yet,
  1676. // then it will have to be looked up.
  1677. //
  1678. // Arguments: pfnSHGetFolderPathSpec* pGetFolderPath - pointer for SHGetFolderPath
  1679. // pfnSHGetSpecialFolderPathWSpec* pGetSpecialFolderPathW - pointer for GetSpecialFolderPathW
  1680. //
  1681. // Returns: BOOL - TRUE if all requested function pointers were retreived.
  1682. //
  1683. // History: quintinb Created 11/23/98
  1684. //
  1685. //+----------------------------------------------------------------------------
  1686. BOOL GetShell32Apis(pfnSHGetFolderPathSpec* pGetFolderPath,
  1687. pfnSHGetSpecialFolderPathWSpec* pGetSpecialFolderPathW)
  1688. {
  1689. static pfnSHGetFolderPathSpec pfnSHGetFolderPath = NULL; // this takes a User token
  1690. static pfnSHGetSpecialFolderPathWSpec pfnSHGetSpecialFolderPathW = NULL;
  1691. #ifdef UNICODE
  1692. const CHAR c_pszSHGetFolderPath[] = "SHGetFolderPathW";
  1693. #else
  1694. const CHAR c_pszSHGetFolderPath[] = "SHGetFolderPathA";
  1695. #endif
  1696. const CHAR c_pszSHGetSpecialFolderPathW[] = "SHGetSpecialFolderPathW";
  1697. if (NULL == g_pShell32)
  1698. {
  1699. g_pShell32 = (CDynamicLibrary*)CmMalloc(sizeof(CDynamicLibrary));
  1700. if (NULL == g_pShell32)
  1701. {
  1702. return FALSE;
  1703. }
  1704. }
  1705. if (!(g_pShell32->IsLoaded()))
  1706. {
  1707. if(!g_pShell32->Load(TEXT("shell32.dll")))
  1708. {
  1709. return FALSE;
  1710. }
  1711. }
  1712. if (NULL != pGetFolderPath)
  1713. {
  1714. if (pfnSHGetFolderPath)
  1715. {
  1716. *pGetFolderPath = pfnSHGetFolderPath;
  1717. }
  1718. else
  1719. {
  1720. *pGetFolderPath = (pfnSHGetFolderPathSpec)g_pShell32->GetProcAddress(c_pszSHGetFolderPath);
  1721. if (NULL == *pGetFolderPath)
  1722. {
  1723. return FALSE;
  1724. }
  1725. else
  1726. {
  1727. pfnSHGetFolderPath = *pGetFolderPath;
  1728. }
  1729. }
  1730. }
  1731. if (NULL != pGetSpecialFolderPathW)
  1732. {
  1733. if (pfnSHGetSpecialFolderPathW)
  1734. {
  1735. *pGetSpecialFolderPathW = pfnSHGetSpecialFolderPathW;
  1736. }
  1737. else
  1738. {
  1739. *pGetSpecialFolderPathW = (pfnSHGetSpecialFolderPathWSpec)g_pShell32->GetProcAddress(c_pszSHGetSpecialFolderPathW);
  1740. if (NULL == *pGetSpecialFolderPathW)
  1741. {
  1742. return FALSE;
  1743. }
  1744. else
  1745. {
  1746. pfnSHGetSpecialFolderPathW = *pGetSpecialFolderPathW;
  1747. }
  1748. }
  1749. }
  1750. return TRUE;
  1751. }