windows-nt/Source/XPSP1/NT/net/rras/cm/cmstp/common.cpp
2020-09-26 16:20:57 +08:00

2016 lines
63 KiB
C++

//+----------------------------------------------------------------------------
//
// File: common.cpp
//
// Module: CMSTP.EXE
//
// Synopsis: This source file contains functions common to several
// different aspects of the CM profile installer (install,
// uninstall, migration).
//
// Copyright (c) 1997-1999 Microsoft Corporation
//
// Author: quintinb Created 11/18/97
//
//+----------------------------------------------------------------------------
#include "cmmaster.h"
//
// for GetPhoneBookPath
//
#include "getpbk.cpp"
//
// For GetAllUsersCmDir
//
#include "allcmdir.cpp"
//
// Need the definition for CM_PBK_FILTER_PREFIX
//
#include "cmdefs.h"
//
// Include the Connections folder specific headers
//
//#include "shlobjp.h"
//#include <objbase.h> // needed for initing guids
//#include <initguid.h> // DON'T CHANGE the ORDER of these header files unless you know what you are doing
//#include <oleguid.h> // IID_IDataObject
//#include <shlguid.h> // IID_IShellFolder
//+----------------------------------------------------------------------------
//
// Function: GetHiddenPhoneBookPath
//
// Synopsis: This function returns the path for the hidden RAS pbk to contain
// the PPP connectoid of a double dial connection. Before returing
// it checks to see if the phonebook exists or not. If the phonebook
// doesn't exist then it returns FALSE. If the function returns
// TRUE the path allocated and stored in *ppszPhonebook must be
// freed using CmFree.
//
// Arguments: LPCTSTR pszProfileDir - full path to the profile directory (dir where cmp resides)
// LPTSTR* ppszPhonebook - pointer to hold the allocated path
//
// Returns: BOOL - TRUE if the phonebook path can be constructed and the
// phonebook file exists.
//
// History: quintinb Created Header 04/14/00
//
//+----------------------------------------------------------------------------
BOOL GetHiddenPhoneBookPath(LPCTSTR pszProfileDir, LPTSTR* ppszPhonebook)
{
// REVIEW: quintinb 12-18-00
// This function returns the wrong path for the Hidden phonebook. This file is
// now named _CMPhone (no .pbk extension) and is now located in the same directory
// as the rasphone.pbk file on NT4, NT5, and whistler. Note that this function isn't
// used on Win9x. Since the problem was low priority (leaving a few K file on the users
// harddrive at uninstall time, we punted the problem as it was close to Beta2 lockdown.
//
BOOL bReturn = FALSE;
if (pszProfileDir && ppszPhonebook && (TEXT('\0') != pszProfileDir[0]))
{
LPTSTR pszPathToReturn = NULL;
*ppszPhonebook = (LPTSTR) CmMalloc((lstrlen(pszProfileDir) + lstrlen(CM_PBK_FILTER_PREFIX) + 13) * sizeof(TCHAR));
MYDBGASSERT(*ppszPhonebook);
if (*ppszPhonebook)
{
//
// Note -- The connection enumerator (netman) will filter out notifications
// for connections created in files with the CM_PBK_FILTER_PREFIX in their
// name.
//
wsprintf(*ppszPhonebook, TEXT("%s\\%sphone.pbk"), pszProfileDir, CM_PBK_FILTER_PREFIX);
bReturn = FileExists(*ppszPhonebook);
}
}
if (FALSE == bReturn)
{
CmFree(*ppszPhonebook);
*ppszPhonebook = NULL;
}
return bReturn;
}
//+----------------------------------------------------------------------------
//
// Function: RemoveShowIconFromRunPostSetupCommands
//
// Synopsis: This function removes showicon.exe from the RunPostSetupCommands
// section of old 1.0 Infs.
//
// Arguments: LPCTSTR szInfFile - the inf file to remove showicon.exe from
//
// Returns: Nothing
//
// History: quintinb Created Header 10/22/98
//
//+----------------------------------------------------------------------------
void RemoveShowIconFromRunPostSetupCommands(LPCTSTR szInfFile)
{
DWORD dwSize = 1024;
DWORD dwSizeNeeded = 1024;
TCHAR* pszBuffer = NULL;
TCHAR* pszNewBuffer = NULL;
const TCHAR* const c_pszRunPostSetupCommandsSection = TEXT("RunPostSetupCommandsSection");
const TCHAR* const c_pszShowIcon = TEXT("showicon.exe");
pszBuffer = (TCHAR*)CmMalloc(sizeof(TCHAR)*dwSize);
if (NULL == pszBuffer)
{
CMASSERTMSG(FALSE, TEXT("RemoveShowIconFromRunPostSetupCommands -- CmMalloc returned a NULL pointer."));
goto exit;
}
dwSizeNeeded = GetPrivateProfileSection(c_pszRunPostSetupCommandsSection, pszBuffer,
dwSize, szInfFile);
while((dwSizeNeeded + 2) == dwSize)
{
//
// the buffer isn't big enough, try again.
//
dwSize += 1024;
MYDBGASSERT(dwSize <= 32*1024); // 32767 is the max size on Win95
CmFree(pszBuffer);
pszBuffer = (TCHAR*)CmMalloc(sizeof(TCHAR)*dwSize);
if (NULL == pszBuffer)
{
CMASSERTMSG(FALSE, TEXT("RemoveShowIconFromRunPostSetupCommands -- CmMalloc returned a NULL pointer."));
goto exit;
}
dwSizeNeeded = GetPrivateProfileSection(c_pszRunPostSetupCommandsSection,
pszBuffer, dwSize, szInfFile);
}
//
// Search the Buffer to find and remove and occurences of showicon.exe
//
if (0 != dwSizeNeeded)
{
//
// Allocate a new buffer of the same size.
//
pszNewBuffer = (TCHAR*)CmMalloc(sizeof(TCHAR)*dwSize);
if (NULL == pszNewBuffer)
{
CMASSERTMSG(FALSE, TEXT("RemoveShowIconFromRunPostSetupCommands -- CmMalloc returned a NULL pointer."));
goto exit;
}
//
// Use Temp pointers to walk the buffers
//
TCHAR *pszNewBufferTemp = pszNewBuffer;
TCHAR *pszBufferTemp = pszBuffer;
while (TEXT('\0') != pszBufferTemp[0])
{
//
// If the string isn't showicon.exe then go ahead and copy it to the new
// buffer. Otherwise, don't.
//
if (0 != lstrcmpi(c_pszShowIcon, pszBufferTemp))
{
lstrcpy(pszNewBufferTemp, pszBufferTemp);
pszNewBufferTemp = pszNewBufferTemp + (lstrlen(pszNewBufferTemp) + 1)*sizeof(TCHAR);
}
pszBufferTemp = pszBufferTemp + (lstrlen(pszBufferTemp) + 1)*sizeof(TCHAR);
}
//
// Erase the current Section and then rewrite it with the new section
//
MYVERIFY(0 != WritePrivateProfileSection(c_pszRunPostSetupCommandsSection,
NULL, szInfFile));
MYVERIFY(0 != WritePrivateProfileSection(c_pszRunPostSetupCommandsSection,
pszNewBuffer, szInfFile));
}
exit:
CmFree(pszBuffer);
CmFree(pszNewBuffer);
}
//+----------------------------------------------------------------------------
//
// Function: HrRegDeleteKeyTree
//
// Synopsis: Deletes an entire registry hive.
//
// Arguments: hkeyParent [in] Handle to open key where the desired key resides.
// szRemoveKey [in] Name of key to delete.
//
// Returns: HRESULT HrRegDeleteKeyTree -
//
// History: danielwe 25 Feb 1997
// borrowed and modified -- quintinb -- 4-2-98
//
//+----------------------------------------------------------------------------
HRESULT HrRegDeleteKeyTree (HKEY hkeyParent, LPCTSTR szRemoveKey)
{
LONG lResult;
HRESULT hr;
MYDBGASSERT(hkeyParent);
MYDBGASSERT(szRemoveKey);
// Open the key we want to remove
HKEY hkeyRemove;
lResult = RegOpenKeyEx(hkeyParent, szRemoveKey, 0, KEY_ALL_ACCESS,
&hkeyRemove);
hr = HRESULT_FROM_WIN32 (lResult);
if (SUCCEEDED(hr))
{
TCHAR szValueName [MAX_PATH+1];
DWORD cchBuffSize = MAX_PATH;
FILETIME ft;
// Enum the keys children, and remove those sub-trees
while (ERROR_NO_MORE_ITEMS != (lResult = RegEnumKeyEx(hkeyRemove,
0,
szValueName,
&cchBuffSize,
NULL,
NULL,
NULL,
&ft)))
{
MYVERIFY(SUCCEEDED(HrRegDeleteKeyTree (hkeyRemove, szValueName)));
cchBuffSize = MAX_PATH;
}
MYVERIFY(ERROR_SUCCESS == RegCloseKey (hkeyRemove));
if ((ERROR_SUCCESS == lResult) || (ERROR_NO_MORE_ITEMS == lResult))
{
lResult = RegDeleteKey(hkeyParent, szRemoveKey);
}
hr = HRESULT_FROM_WIN32 (lResult);
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: RemovePhonebookEntry
//
// Synopsis: This function loads RAS dynamically and then deletes the specified
// connectoids. It will either delete only the connectoid exactly
// specified by the phonebook and entry name (bMatchSimilarEntries == FALSE)
// or it will enumerate all entries in the phonebook and delete any
// entry that matches the first lstrlen(pszEntryName) chars of the given
// connectoid name (thus deleting backup and tunnel connectoids). Note
// that on NT5 we must set the <> parameter of the connectoid to "" so
// that the RasCustomDeleteEntryNotify will not get called and thus have
// cmstp.exe /u launched on the connection.
//
// Arguments: LPTSTR pszEntryName - the long service name of the profile to delete
// LPTSTR pszPhonebook - the full path to the pbk file to delete entries from
// BOOL bMatchSimilarEntries - whether the function should delete similarly
// named connectoids or only the exact connectoid
// specified.
//
// Returns: BOOL - returns TRUE if the function was successful, FALSE otherwise
//
// History: quintinb 7/14/98 Created
// quintinb 7/27/99 rewrote to include deleting a single connectoid or
// enumerating to delete all similarly named connectoids
//
//+----------------------------------------------------------------------------
BOOL RemovePhonebookEntry(LPCTSTR pszEntryName, LPTSTR pszPhonebook, BOOL bMatchSimilarEntries)
{
pfnRasDeleteEntrySpec pfnDeleteEntry;
pfnRasEnumEntriesSpec pfnEnumEntries;
pfnRasSetEntryPropertiesSpec pfnSetEntryProperties;
pfnRasSetCredentialsSpec pfnSetCredentials;
DWORD dwStructSize;
DWORD dwSize;
DWORD dwNum;
DWORD dwRet;
DWORD dwIdx;
DWORD dwLen;
CPlatform plat;
BOOL bReturn = FALSE;
BOOL bExit;
TCHAR szTemp[MAX_PATH+1];
RASENTRYNAME* pRasEntries = NULL;
RASENTRYNAME* pCurrentRasEntry = NULL;
//
// Check Inputs
//
MYDBGASSERT(NULL != pszEntryName);
MYDBGASSERT((NULL == pszPhonebook) || (TEXT('\0') != pszPhonebook[0]));
if ((NULL == pszEntryName) || ((NULL != pszPhonebook) && (TEXT('\0') == pszPhonebook[0])))
{
CMTRACE(TEXT("RemovePhonebookEntry -- Invalid Parameter passed in."));
goto exit;
}
//
// Get Function Pointers for the Ras Apis that we need
//
if(!GetRasApis(&pfnDeleteEntry, &pfnEnumEntries, &pfnSetEntryProperties, NULL, NULL,
(plat.IsAtLeastNT5() ? &pfnSetCredentials : NULL)))
{
CMTRACE(TEXT("RemovePhonebookEntry -- Unable to get RAS apis."));
bReturn = FALSE;
goto exit;
}
//
// Setup the Structure Sizes correctly
//
if (plat.IsAtLeastNT5())
{
dwStructSize = sizeof(RASENTRYNAME_V500);
}
else
{
dwStructSize = sizeof(RASENTRYNAME);
}
//
// Init the Size to one struct and dwNum to zero entries
//
bExit = FALSE;
dwSize = dwStructSize*1;
dwNum = 0;
do
{
pRasEntries = (RASENTRYNAME*)CmMalloc(dwSize);
if (NULL == pRasEntries)
{
CMASSERTMSG(FALSE, TEXT("RemovePhonebookEntry -- CmMalloc returned a NULL pointer."));
goto exit;
}
//
// Set the struct size
//
pRasEntries->dwSize = dwStructSize;
dwRet = (pfnEnumEntries)(NULL, pszPhonebook, (RASENTRYNAME*)pRasEntries, &dwSize, &dwNum);
//
// Check the return code from RasEnumEntries
//
if (ERROR_BUFFER_TOO_SMALL == dwRet)
{
CMTRACE1(TEXT("RemovePhonebookEntry -- RasEnumEntries said our buffer was too small, New Size=%u"), dwNum*dwStructSize);
CmFree(pRasEntries);
dwSize = dwStructSize * dwNum;
dwNum = 0;
}
else if (ERROR_SUCCESS == dwRet)
{
CMTRACE1(TEXT("RemovePhonebookEntry -- RasEnumEntries successful, %u entries enumerated."), dwNum);
bExit = TRUE;
}
else
{
CMTRACE1(TEXT("RemovePhonebookEntry -- RasEnumEntries Failed, dwRet == %u"), dwRet);
goto exit;
}
} while (!bExit);
//
// At this point we should have entries to process, if not then we will exit here. Otherwise
// we will look for matches and then delete any we find.
//
dwLen = lstrlen(pszEntryName) + 1; // get the length of the Entry Name
bReturn = TRUE; // assume everything is okay at this point.
//
// okay now we are ready to perform the deletions
//
pCurrentRasEntry = pRasEntries;
for (dwIdx=0; dwIdx < dwNum; dwIdx++)
{
CMTRACE2(TEXT("\tRemovePhonebookEntry -- RasEnumEntries returned %s in %s"), pCurrentRasEntry->szEntryName, MYDBGSTR(pszPhonebook));
if (bMatchSimilarEntries)
{
//
// Match entries that have the first lstrlen(pszEntryName) chars
// the same.
//
lstrcpyn(szTemp, pCurrentRasEntry->szEntryName, dwLen);
}
else
{
//
// Only match exact entries.
//
lstrcpy(szTemp, pCurrentRasEntry->szEntryName);
}
if (0 == lstrcmp(szTemp, pszEntryName))
{
//
// We have an entry that starts with the Long Service Name, so delete it. Note
// that if this is NT5 then we need to clear the szCustomDialDll param of the
// connectoid so we don't get called again on the RasCustomDeleteNotify entry
// point
//
if (plat.IsAtLeastNT5())
{
//
// On NT5, we also want to make sure we clean up any credentials associated with this
// connectoid. We do that by calling RasSetCredentials
//
RASCREDENTIALSA RasCreds = {0};
RasCreds.dwSize = sizeof(RASCREDENTIALSA);
RasCreds.dwMask = RASCM_UserName | RASCM_Password | RASCM_Domain;
dwRet = (pfnSetCredentials)(pszPhonebook, pCurrentRasEntry->szEntryName, &RasCreds, TRUE); // TRUE == fClearCredentials
MYDBGASSERT(ERROR_SUCCESS == dwRet);
RASENTRY_V500 RasEntryV5 = {0};
RasEntryV5.dwSize = sizeof(RASENTRY_V500);
RasEntryV5.dwType = RASET_Internet;
// RasEntryV5.szCustomDialDll[0] = TEXT('\0'); -- already zero-ed
dwRet = ((pfnSetEntryProperties)(pszPhonebook, pCurrentRasEntry->szEntryName,
(RASENTRY*)&RasEntryV5, RasEntryV5.dwSize, NULL, 0));
if (ERROR_SUCCESS != dwRet)
{
CMTRACE3(TEXT("\t\tRemovePhonebookEntry -- RasSetEntryProperties failed on entry %s in %s, dwRet = %u"), pCurrentRasEntry->szEntryName, MYDBGSTR(pszPhonebook), dwRet);
bReturn = FALSE;
continue; // don't try to delete the entry it might cause a re-launch problem
}
else
{
CMTRACE2(TEXT("\t\tRemovePhonebookEntry -- Clearing CustomDialDll setting with RasSetEntryProperties on entry %s in %s"), pCurrentRasEntry->szEntryName, MYDBGSTR(pszPhonebook));
}
}
dwRet = (pfnDeleteEntry)(pszPhonebook, pCurrentRasEntry->szEntryName);
if (ERROR_SUCCESS != dwRet)
{
CMTRACE3(TEXT("\t\tRemovePhonebookEntry -- RasDeleteEntry failed on entry %s in %s, dwRet = %u"), pCurrentRasEntry->szEntryName, MYDBGSTR(pszPhonebook), dwRet);
bReturn = FALSE; // set return to FALSE but continue trying to delete entries
}
else
{
CMTRACE2(TEXT("\t\tRemovePhonebookEntry -- Deleted entry %s in %s"), pCurrentRasEntry->szEntryName, MYDBGSTR(pszPhonebook));
}
}
//
// Increment to next RasEntryName struct, note we have to do this manually since
// the sizeof(RASENTRYNAME) is wrong for NT5 structs.
//
pCurrentRasEntry = (RASENTRYNAME*)((BYTE*)pCurrentRasEntry + dwStructSize);
}
exit:
CmFree(pRasEntries);
return bReturn;
}
//+----------------------------------------------------------------------------
//
// Function: DeleteNT5ShortcutFromPathAndName
//
// Synopsis: This function deletes the link specified by the CSIDL (see SHGetSpecialFolderLocation),
// and the profilename. Used before installing a profile to make
// sure we don't get duplicate links.
//
// Arguments: LPCTSTR szProfileName - string that holds the profilename
// int nFolder - the CSIDL identifier of the folder that holds the
// link to delete
//
// Returns: Nothing
//
// History: quintinb Created 5/26/98
//
//+----------------------------------------------------------------------------
void DeleteNT5ShortcutFromPathAndName(HINSTANCE hInstance, LPCTSTR szProfileName, int nFolder)
{
TCHAR szFolderDir[MAX_PATH+1];
if (SUCCEEDED(GetNT5FolderPath(nFolder, szFolderDir)))
{
//
// Now add \Shortcut to %LongServiceName% to the end of path
//
TCHAR szCleanString[MAX_PATH+1];
TCHAR szShortCutPreface[MAX_PATH+1];
ZeroMemory(szCleanString, sizeof(szCleanString));
MYVERIFY(0 != LoadString(hInstance, IDS_SHORTCUT_TO, szShortCutPreface, MAX_PATH));
MYVERIFY(CELEMS(szCleanString) > (UINT)wsprintf(szCleanString, TEXT("%s\\%s %s.lnk"), szFolderDir, szShortCutPreface, szProfileName));
if (SetFileAttributes(szCleanString, FILE_ATTRIBUTE_NORMAL))
{
SHFILEOPSTRUCT fOpStruct;
ZeroMemory(&fOpStruct, sizeof(fOpStruct));
fOpStruct.wFunc = FO_DELETE;
fOpStruct.pFrom = szCleanString;
fOpStruct.fFlags = FOF_SILENT | FOF_NOCONFIRMATION;
//
// The shell32.dll on Win95 doesn't contain the SHFileOperationW function. Thus if we compile
// this Unicode we must revisit this code and dynamically link to it.
//
MYVERIFY(0 == SHFileOperation(&fOpStruct));
}
}
}
//+----------------------------------------------------------------------------
//
// Function: CreateNT5ProfileShortcut
//
// Synopsis: This function uses private APIs in NetShell.dll to create a desktop
// shortcut to the specified connections.
//
// Arguments: LPTSTR pszProfileName - Name of the Connection to look for
// LPTSTR pszPhoneBook - Full path to the pbk that the connection resides in
// BOOL bAllUsers - TRUE if looking for an All Users connection
//
// Returns: HRESULT - returns normal hr codes
//
// History: quintinb Created 5/5/98
// quintinb Updated to use Netshell APIs 2/17/99
//
//+----------------------------------------------------------------------------
HRESULT CreateNT5ProfileShortcut(LPCTSTR pszProfileName, LPCTSTR pszPhoneBook, BOOL bAllUsers)
{
HRESULT hr = E_FAIL;
pfnCreateShortcutSpec pfnCreateShortcut = NULL;
pfnRasGetEntryPropertiesSpec pfnGetEntryProperties = NULL;
//
// Check Inputs
//
if ((NULL == pszProfileName) || (TEXT('\0') == pszProfileName[0]) ||
(NULL != pszPhoneBook && TEXT('\0') == pszPhoneBook[0]))
{
//
// Then they passed in an invalid string argument, thus return invalid arg. Note
// that pszPhoneBook can be NULL but that if it isn't NULL it cannot be empty.
//
return E_INVALIDARG;
}
//
// First Find the GUID of the connection
//
if (!GetRasApis(NULL, NULL, NULL, NULL, &pfnGetEntryProperties, NULL))
{
return E_UNEXPECTED;
}
DWORD dwRes;
DWORD dwSize;
LPRASENTRY_V500 pRasEntry = NULL;
pRasEntry = (LPRASENTRY_V500)CmMalloc(sizeof(RASENTRY_V500));
if (NULL != pRasEntry)
{
ZeroMemory(pRasEntry, sizeof(RASENTRY_V500));
pRasEntry->dwSize = sizeof(RASENTRY_V500);
dwSize = sizeof(RASENTRY_V500);
dwRes = (pfnGetEntryProperties)(pszPhoneBook, pszProfileName, (LPRASENTRY)pRasEntry, &dwSize, NULL, NULL);
if (0 == dwRes)
{
//
// Then we were able to get the RasEntry, load the NetShell API
// and call HrCreateShortcut
//
pfnSHGetSpecialFolderPathWSpec pfnSHGetSpecialFolderPathW;
if(GetShell32Apis(NULL, &pfnSHGetSpecialFolderPathW))
{
WCHAR szwPath[MAX_PATH+1];
hr = (pfnSHGetSpecialFolderPathW)(NULL, szwPath,
bAllUsers ? CSIDL_COMMON_DESKTOPDIRECTORY : CSIDL_DESKTOPDIRECTORY, FALSE);
if (SUCCEEDED(hr) && GetNetShellApis(NULL, &pfnCreateShortcut, NULL))
{
hr = (pfnCreateShortcut)(pRasEntry->guidId, szwPath);
}
}
}
else
{
CMTRACE1(TEXT("CreateNT5ProfileShortcut -- RasGetEntryProperties returned %u"), dwRes);
CMASSERTMSG(FALSE, TEXT("Unable to find the connection for which the shortcut was requested in the RAS pbk."));
return HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
}
CmFree(pRasEntry);
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: WriteCmPhonebookEntry
//
// Synopsis: This function creates an NT5 phonebook entry for a CM connection.
// .
// The function sets:
// - the szAutoDialDll to cmdial32.dll
// - the modem name and device type
// - the type to RASET_Inernet.
//
// Arguments: LPCTSTR szLongServiceName - Name of the Connectoid to be created
// LPCTSTR szFullPathtoPBK - full path to the pbk to put the connectoid in, if NULL
// the system phonebook is used.
// LPCTSTR pszCmsFile - The full path of the referencing .CMS for the profile
//
// Returns: BOOL - TRUE on success
//
// History: 05/05/98 - quintinb - Created Header
// ??/??/?? - henryt - Modified to work on multiple platforms. added modem stuff.
// 01/12/99 - nickball - Replaced fDoDirectConnect with szCmsFile. Handled no modem case.
//
//+----------------------------------------------------------------------------
BOOL WriteCmPhonebookEntry(LPCTSTR szLongServiceName,
LPCTSTR szFullPathtoPBK,
LPCTSTR pszCmsFile)
{
pfnRasSetEntryPropertiesSpec pfnSetEntryProperties;
DWORD dwRet = 1;
CPlatform plat;
RASENTRY *pRasEntry = NULL;
BOOL bReturn = FALSE;
DWORD dwReturn;
BOOL fSupportDialup;
BOOL fSupportDirect;
BOOL fDoDirectConnect;
BOOL fSeekVpn;
const TCHAR* const c_pszOne = TEXT("1");
MYDBGASSERT(szLongServiceName);
MYDBGASSERT(pszCmsFile);
if (NULL == szLongServiceName || NULL == pszCmsFile)
{
return FALSE;
}
CMTRACE2(TEXT("WriteCmPhonebookEntry() - szLongServiceName is %s, szFullPathtoPBK is %s"), szLongServiceName, szFullPathtoPBK ? szFullPathtoPBK : TEXT("<NULL>"));
if (!GetRasApis(NULL, NULL, &pfnSetEntryProperties, NULL, NULL, NULL))
{
return FALSE;
}
//
// alloc RASENTRY properly
//
if (plat.IsAtLeastNT5())
{
RASENTRY_V500 *pRasEntryV500 = (RASENTRY_V500 *)CmMalloc(sizeof(RASENTRY_V500));
if (!pRasEntryV500)
{
CMTRACE(TEXT("WriteCmPhonebookEntry failed to alloc mem"));
goto exit;
}
ZeroMemory(pRasEntryV500, sizeof(RASENTRY_V500));
pRasEntryV500->dwSize = sizeof(RASENTRY_V500);
pRasEntryV500->dwType = RASET_Internet;
pRasEntry = (RASENTRY *)pRasEntryV500;
}
else
{
pRasEntry = (RASENTRY *)CmMalloc(sizeof(RASENTRY));
if (!pRasEntry)
{
CMTRACE(TEXT("WriteCmPhonebookEntry failed to alloc mem"));
goto exit;
}
pRasEntry->dwSize = sizeof(RASENTRY);
}
//
// Update the RAS entry with our DLL name for AutoDial and CustomDial
// Note: NT5 gets CustomDial only, no AutoDial and AutoDialFunc.
//
if (plat.IsAtLeastNT5())
{
//
// Use the machine independent %windir%\system32\cmdial32.dll on NT5
//
lstrcpy(((RASENTRY_V500 *)pRasEntry)->szCustomDialDll, c_pszCmDialPath);
}
else
{
TCHAR szSystemDirectory[MAX_PATH+1];
//
// Specify _InetDialHandler@16 as the entry point used for AutoDial.
//
lstrcpy(pRasEntry->szAutodialFunc, c_pszInetDialHandler);
//
// Get the system directory path
//
if (0 == GetSystemDirectory(szSystemDirectory, CELEMS(szSystemDirectory)))
{
goto exit;
}
UINT uCount = (UINT)wsprintf(pRasEntry->szAutodialDll, TEXT("%s\\cmdial32.dll"), szSystemDirectory);
MYDBGASSERT(uCount < CELEMS(pRasEntry->szAutodialDll));
}
if (plat.IsWin9x())
{
//
// Win9x requires these to be set
//
pRasEntry->dwFramingProtocol = RASFP_Ppp;
pRasEntry->dwCountryID = 1;
pRasEntry->dwCountryCode = 1;
//lstrcpy(pRasEntry->szAreaCode, TEXT("425"));
lstrcpy(pRasEntry->szLocalPhoneNumber, TEXT("default"));
}
//
// Is the profile configured to first use Direct Connect
//
fSupportDialup = GetPrivateProfileInt(c_pszCmSection, c_pszCmEntryDialup, 1, pszCmsFile);
fSupportDirect = GetPrivateProfileInt (c_pszCmSection, c_pszCmEntryDirect, 1, pszCmsFile);
fDoDirectConnect = ((fSupportDialup && fSupportDirect &&
GetPrivateProfileInt(c_pszCmSection, c_pszCmEntryConnectionType, 0, pszCmsFile)) ||
(!fSupportDialup));
fSeekVpn = fDoDirectConnect;
//
// First try dial-up if appropriate
//
if (!fDoDirectConnect && !PickModem(pRasEntry->szDeviceType, pRasEntry->szDeviceName, FALSE))
{
CMTRACE(TEXT("*******Failed to pick a dial-up device!!!!"));
//
// If direct capable, try to find a VPN device
//
fSeekVpn = fSupportDirect;
}
//
// If seeking a VPN device
//
if (fSeekVpn)
{
if (!PickModem(pRasEntry->szDeviceType, pRasEntry->szDeviceName, TRUE))
{
CMTRACE(TEXT("*******Failed to pick a VPN device!!!!"));
}
else
{
//
// Found VPN device, set default type as appropriate
//
if (!fDoDirectConnect)
{
CFileNameParts CmsParts(pszCmsFile);
TCHAR szCmpFile[MAX_PATH+1];
MYVERIFY(CELEMS(szCmpFile) > (UINT)wsprintf(szCmpFile, TEXT("%s%s"), CmsParts.m_Drive,
CmsParts.m_Dir));
szCmpFile[lstrlen(szCmpFile) - 1] = TEXT('\0');
lstrcat(szCmpFile, c_pszCmpExt);
WritePrivateProfileString(c_pszCmSection, c_pszCmEntryConnectionType, c_pszOne, szCmpFile);
}
}
}
//
// No device??? Use last resort for dial-up on NT5
//
if (plat.IsAtLeastNT5() && !pRasEntry->szDeviceType[0])
{
lstrcpy(pRasEntry->szDeviceType, RASDT_Modem);
lstrcpy(pRasEntry->szDeviceName, TEXT("Unavailable device ()"));
CMTRACE2(TEXT("*******Writing szDeviceType - %s and szDeviceName %s"),
pRasEntry->szDeviceType, pRasEntry->szDeviceName);
}
//
// Zero is the success return value from RasSetEntryProperties
//
dwReturn = ((pfnSetEntryProperties)(szFullPathtoPBK, szLongServiceName,
pRasEntry, pRasEntry->dwSize, NULL, 0));
if (ERROR_SUCCESS == dwReturn)
{
bReturn = TRUE;
}
CMTRACE1(TEXT("WriteCmPhonebookEntry() - RasSetEntryProperties failed with error %d"), dwReturn);
exit:
CmFree(pRasEntry);
return bReturn;
}
//+----------------------------------------------------------------------------
//
// Function: GetRasModems
//
// Synopsis: get a list of modem devices from RAS
//
// Arguments: pprdiRasDevInfo Ras device info list
// pdwCnt modem count
//
// Returns: TRUE, if a list is obtained
//
//+----------------------------------------------------------------------------
BOOL GetRasModems(
LPRASDEVINFO *pprdiRasDevInfo,
LPDWORD pdwCnt
)
{
DWORD dwLen;
DWORD dwRes;
DWORD dwCnt;
pfnRasEnumDevicesSpec pfnEnumDevices;
if (pprdiRasDevInfo)
{
*pprdiRasDevInfo = NULL;
}
if (pdwCnt)
{
*pdwCnt = 0;
}
if (!GetRasApis(NULL, NULL, NULL, &pfnEnumDevices, NULL, NULL))
{
return FALSE;
}
dwLen = 0;
dwRes = pfnEnumDevices(NULL, &dwLen, &dwCnt);
CMTRACE3(TEXT("GetRasModems() RasEnumDevices(NULL,pdwLen,&dwCnt) returns %u, dwLen=%u, dwCnt=%u."), dwRes, dwLen, dwCnt);
if ((dwRes != ERROR_SUCCESS) && (dwRes != ERROR_BUFFER_TOO_SMALL) ||
(dwLen < sizeof(**pprdiRasDevInfo)))
{
return FALSE;
}
*pprdiRasDevInfo = (LPRASDEVINFO) CmMalloc(__max(dwLen, sizeof(**pprdiRasDevInfo)));
if (*pprdiRasDevInfo)
{
(*pprdiRasDevInfo)->dwSize = sizeof(**pprdiRasDevInfo);
dwRes = pfnEnumDevices(*pprdiRasDevInfo, &dwLen, &dwCnt);
CMTRACE3(TEXT("GetRasModems() RasEnumDevices(NULL,pdwLen,&dwCnt) returns %u, dwLen=%u, dwCnt=%u."), dwRes, dwLen, dwCnt);
if (dwRes != ERROR_SUCCESS)
{
CmFree(*pprdiRasDevInfo);
*pprdiRasDevInfo = NULL;
return FALSE;
}
if (pdwCnt)
{
*pdwCnt = dwCnt;
}
}
else
{
CMASSERTMSG(FALSE, TEXT("GetRasModems -- CmMalloc returned a NULL pointer for *pprdiRasDevInfo."));
return FALSE;
}
return TRUE;
}
//+----------------------------------------------------------------------------
//
// Function: PickModem
//
// Synopsis: Pick a default modem
//
// Arguments: OUT pszDeviceType, the device type if not NULL
// OUT pszDeviceName, the device name if not NULL
// OUT fUseVpnDevice Use VPN device or not
//
// Returns: TRUE, is modem is found
//
//+----------------------------------------------------------------------------
BOOL PickModem(
LPTSTR pszDeviceType,
LPTSTR pszDeviceName,
BOOL fUseVpnDevice
)
{
LPRASDEVINFO prdiModems;
DWORD dwCnt;
DWORD dwIdx;
//
// First, get a list of modems from RAS
//
if (!GetRasModems(&prdiModems, &dwCnt) || dwCnt == 0)
{
return FALSE;
}
//
// find the first device and use it by default.
// Use VPN device if it's a VPN connection.
//
for (dwIdx=0; dwIdx<dwCnt; dwIdx++)
{
if (fUseVpnDevice && !lstrcmpi(prdiModems[dwIdx].szDeviceType, RASDT_Vpn) ||
!fUseVpnDevice && (!lstrcmpi(prdiModems[dwIdx].szDeviceType, RASDT_Isdn) ||
!lstrcmpi(prdiModems[dwIdx].szDeviceType, RASDT_Modem)))
{
break;
}
}
//
// If we have a match, fill device name and device type
//
if (dwIdx < dwCnt)
{
if (pszDeviceType)
{
lstrcpy(pszDeviceType, prdiModems[dwIdx].szDeviceType);
}
if (pszDeviceName)
{
lstrcpy(pszDeviceName, prdiModems[dwIdx].szDeviceName);
}
}
CmFree(prdiModems);
return (dwIdx < dwCnt);
}
//+----------------------------------------------------------------------------
//
// Function: IsMemberOfGroup
//
// Synopsis: This function return TRUE if the current user is a member of
// the passed and FALSE passed in Group RID.
//
// Arguments: DWORD dwGroupRID -- the RID of the group to check membership of
// BOOL bUseBuiltinDomainRid -- whether the SECURITY_BUILTIN_DOMAIN_RID
// RID should be used to build the Group
// SID
//
// Returns: BOOL - TRUE if the user is a member of the specified group
//
// History: quintinb Shamelessly stolen from MSDN 02/19/98
// quintinb Reworked and renamed 06/18/99
// to apply to more than just Admins
// quintinb Rewrote to use CheckTokenMemberShip 08/18/99
// since the MSDN method was no longer
// correct on NT5 -- 389229
//
//+----------------------------------------------------------------------------
BOOL IsMemberOfGroup(DWORD dwGroupRID, BOOL bUseBuiltinDomainRid)
{
CPlatform cmplat;
PSID psidGroup = NULL;
SID_IDENTIFIER_AUTHORITY siaNtAuthority = SECURITY_NT_AUTHORITY;
BOOL bSuccess = FALSE;
if (FALSE == cmplat.IsAtLeastNT5())
{
CMASSERTMSG(FALSE, TEXT("IsMemberOfGroup -- Trying to use an NT5 only function on a downlevel platform."));
return FALSE;
}
//
// Make a SID for the Group we are checking for, Note that we if we need the Built
// in Domain RID (for Groups like Administrators, PowerUsers, Users, etc)
// then we will have two entries to pass to AllocateAndInitializeSid. Otherwise,
// (for groups like Authenticated Users) we will only have one.
//
BYTE byNum;
DWORD dwFirstRID;
DWORD dwSecondRID;
if (bUseBuiltinDomainRid)
{
byNum = 2;
dwFirstRID = SECURITY_BUILTIN_DOMAIN_RID;
dwSecondRID = dwGroupRID;
}
else
{
byNum = 1;
dwFirstRID = dwGroupRID;
dwSecondRID = 0;
}
if (AllocateAndInitializeSid(&siaNtAuthority, byNum, dwFirstRID, dwSecondRID,
0, 0, 0, 0, 0, 0, &psidGroup))
{
//
// Now we need to dynamically load the CheckTokenMemberShip API from
// advapi32.dll since it is a Win2k only API.
//
HMODULE hAdvapi = GetModuleHandle(TEXT("advapi32.dll"));
if (hAdvapi)
{
typedef BOOL (WINAPI *pfnCheckTokenMembershipSpec)(HANDLE, PSID, PBOOL);
pfnCheckTokenMembershipSpec pfnCheckTokenMembership;
pfnCheckTokenMembership = (pfnCheckTokenMembershipSpec)GetProcAddress(hAdvapi, "CheckTokenMembership");
if (pfnCheckTokenMembership)
{
//
// Check to see if the user is actually a member of the group in question
//
if (!(pfnCheckTokenMembership)(NULL, psidGroup, &bSuccess))
{
bSuccess = FALSE;
CMASSERTMSG(FALSE, TEXT("CheckTokenMemberShip Failed."));
}
}
else
{
CMASSERTMSG(FALSE, TEXT("IsMemberOfGroup -- GetProcAddress failed for CheckTokenMemberShip"));
}
}
else
{
CMASSERTMSG(FALSE, TEXT("IsMemberOfGroup -- Unable to get the module handle for advapi32.dll"));
}
FreeSid (psidGroup);
}
return bSuccess;
}
//+----------------------------------------------------------------------------
//
// Function: IsAdmin
//
// Synopsis: Check to see if the user is a member of the Administrators group
// or not.
//
// Arguments: None
//
// Returns: BOOL - TRUE if the current user is an Administrator
//
// History: quintinb Created Header 8/18/99
//
//+----------------------------------------------------------------------------
BOOL IsAdmin(void)
{
return IsMemberOfGroup(DOMAIN_ALIAS_RID_ADMINS, TRUE); // TRUE == bUseBuiltinDomainRid
}
//+----------------------------------------------------------------------------
//
// Function: IsAuthenticatedUser
//
// Synopsis: Check to see if the current user is a member of the
// Authenticated Users group.
//
// Arguments: None
//
// Returns: BOOL - TRUE if the current user is a member of the
// Authenticated Users group.
//
// History: quintinb Created Header 8/18/99
//
//+----------------------------------------------------------------------------
BOOL IsAuthenticatedUser(void)
{
return IsMemberOfGroup(SECURITY_AUTHENTICATED_USER_RID, FALSE); // FALSE == bUseBuiltinDomainRid
}
//+----------------------------------------------------------------------------
//
// Function: GetNT5FolderPath
//
// Synopsis: Get the folder path on NT5
// Since cmstp.exe is launched in netman by CreateProcessAsUser
// SHGetSpecialFolderPath does not work. We have to call
// SHGetFolderPath with an access token.
//
// Arguments: int nFolder - Value specifying the folder for which to retrieve
// the location.
// OUT LPTSTR lpszPath - Address of a character buffer that receives
// the drive and path of the specified folder. This
// buffer must be at least MAX_PATH characters in size.
//
// Returns: HRESULT -
//
// History: fengsun Created Header 6/18/98
// quintinb modified to use GetShell32Apis 11-22-98
//
//+----------------------------------------------------------------------------
HRESULT GetNT5FolderPath(int nFolder, OUT LPTSTR lpszPath)
{
MYDBGASSERT(lpszPath);
pfnSHGetFolderPathSpec pfnSHGetFolderPath;
//
// Call shell32.dll-->SHGetFolderPath, which takes a token.
//
if(!GetShell32Apis(&pfnSHGetFolderPath, NULL))
{
CMASSERTMSG(FALSE, TEXT("Failed to load shell32.dll or ShGetFolderPath"));
return E_UNEXPECTED;
}
//
// Get the current process token
//
HANDLE hToken; // The token of the process, to be passed to SHGetFolderPath
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
{
CMASSERTMSG(FALSE, TEXT("OpenThreadToken failed"));
return E_UNEXPECTED;
}
HRESULT hr = pfnSHGetFolderPath(NULL, nFolder, hToken, 0, lpszPath);
MYVERIFY(0 != CloseHandle(hToken));
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: HrIsCMProfilePrivate
//
// Synopsis: This function compares the inputed file path with the application
// data path of the system. If the file path contains the app data
// path then it is considered to be a private profile.
//
// Arguments: LPTSTR szFilePath - directory or file path to compare against
//
// Returns: HRESULT - S_OK if a private profile, S_FALSE if it is an all users
// profile. Standard error codes otherwise.
//
// History: quintinb original code
//
//+----------------------------------------------------------------------------
HRESULT HrIsCMProfilePrivate(LPCTSTR szFilePath)
{
UINT uiLen;
TCHAR szAppDataDir[MAX_PATH+1];
TCHAR szTemp[MAX_PATH+1] = {TEXT("")};
CPlatform plat;
if ((NULL == szFilePath) || (TEXT('\0') == szFilePath[0]))
{
return E_POINTER;
}
//
// Can't be a private user profile unless we are on NT5
//
if (!(plat.IsAtLeastNT5()))
{
return S_FALSE;
}
//
// Figure out what the user directory of the current user is. We can compare this
// against the directory of the phonebook and see if we have a private user
// profile or an all user profile.
if (FAILED(GetNT5FolderPath(CSIDL_APPDATA, szAppDataDir)))
{
return E_UNEXPECTED;
}
uiLen = lstrlen(szAppDataDir) + 1;
lstrcpyn(szTemp, szFilePath, uiLen);
if ((NULL != szTemp) && (0 == lstrcmpi(szAppDataDir, szTemp)))
{
return S_OK;
}
else
{
return S_FALSE;
}
}
//+----------------------------------------------------------------------------
//
// Function: RefreshDesktop
//
// Synopsis: This function refreshes the desktop and basically takes the place
// of showicon.exe (in fact the code is a cut and paste from the
// main of showicon).
//
// Arguments: None
//
// Returns: Nothing
//
// History: quintinb Created Header 5/5/98
//
//+----------------------------------------------------------------------------
void RefreshDesktop()
{
LPMALLOC pMalloc = NULL;
LPITEMIDLIST pItemIDList = NULL;
//
// Get the IMalloc for the Shell.
//
HRESULT hr = SHGetMalloc(&pMalloc);
if (SUCCEEDED(hr))
{
// Get the desktop ID list..
hr = SHGetSpecialFolderLocation(NULL,
CSIDL_DESKTOP,
&pItemIDList);
if (SUCCEEDED(hr))
{
// Notify of change.
SHChangeNotify(SHCNE_UPDATEDIR,
SHCNF_IDLIST,
(LPCVOID)pItemIDList,
NULL);
pMalloc->Free(pItemIDList);
}
MYVERIFY(SUCCEEDED(pMalloc->Release()));
}
}
//+----------------------------------------------------------------------------
//
// Function: GetPrivateCmUserDir
//
// Synopsis: This function fills in the string passed in with the path to the
// path where CM should be installed. For instance, it should return
// c:\users\quintinb\Application Data\Microsoft\Network\Connection Manager
// for me. Please note that this function is NT5 only.
//
// Arguments: LPTSTR pszDir - String to the Users Connection Manager Directory
//
// Returns: LPTSTR - String to the Users Connection Manager Directory
//
// History: quintinb Created Header 2/19/98
//
//+----------------------------------------------------------------------------
LPTSTR GetPrivateCmUserDir(LPTSTR pszDir, HINSTANCE hInstance)
{
LPITEMIDLIST pidl;
LPMALLOC pMalloc;
CPlatform plat;
TCHAR szTemp[MAX_PATH+1];
MYDBGASSERT(pszDir);
pszDir[0] = TEXT('\0');
if (!plat.IsAtLeastNT5())
{
CMASSERTMSG(FALSE, TEXT("GetPrivateCmUserDir -- This NT5 only function was called from a different platform."));
goto exit;
}
if (FAILED(GetNT5FolderPath(CSIDL_APPDATA, pszDir)))
{
goto exit;
}
MYVERIFY(0 != LoadString(hInstance, IDS_CMSUBFOLDER, szTemp, MAX_PATH));
MYVERIFY(NULL != lstrcat(pszDir, szTemp));
exit:
return pszDir;
}
//+----------------------------------------------------------------------------
//
// Function: LaunchProfile
//
// Synopsis: This function handles launching the CM profile (NTRAID 201307) after
// installation. On NT5 it opens the connfolder and launches the
// correct connection by doing a shell execute on the pidl we get from
// enumerating the connections folder. On down level we use Cmmgr32.exe
// and the full path to the cmp file. Please note that on downlevel we
// only care about the input param pszFullPathToCmpFile, while on NT5
// we only care about pszwServiceName and bInstallForAllUsers.
//
// Arguments: LPCTSTR pszFullPathToCmpFile - the full path to the cmp file (used on legacy only)
// LPCSTR pszServiceName - the Long Service Name
// BOOL bInstallForAllUsers -
//
// Returns: HRESULT -- standard COM error codes
//
// History: quintinb Created 11/16/98
//
//+----------------------------------------------------------------------------
HRESULT LaunchProfile(LPCTSTR pszFullPathToCmpFile, LPCTSTR pszServiceName,
LPCTSTR pszPhoneBook, BOOL bInstallForAllUsers)
{
CPlatform plat;
HRESULT hr = E_FAIL;
if ((NULL == pszFullPathToCmpFile) || (NULL == pszServiceName) ||
(NULL != pszPhoneBook && TEXT('\0') == pszPhoneBook[0]))
{
CMASSERTMSG(FALSE, TEXT("Invalid argument passed to LaunchProfile"));
return E_INVALIDARG;
}
if (plat.IsAtLeastNT5())
{
CMASSERTMSG((TEXT('\0') != pszServiceName), TEXT("Empty ServiceName passed to LaunchProfile on win2k."));
pfnRasGetEntryPropertiesSpec pfnGetEntryProperties = NULL;
if (!GetRasApis(NULL, NULL, NULL, NULL, &pfnGetEntryProperties, NULL))
{
return E_UNEXPECTED;
}
DWORD dwRes;
DWORD dwSize;
LPRASENTRY_V500 pRasEntry = NULL;
pRasEntry = (LPRASENTRY_V500)CmMalloc(sizeof(RASENTRY_V500));
if (NULL != pRasEntry)
{
ZeroMemory(pRasEntry, sizeof(RASENTRY_V500));
pRasEntry->dwSize = sizeof(RASENTRY_V500);
dwSize = sizeof(RASENTRY_V500);
dwRes = (pfnGetEntryProperties)(pszPhoneBook, pszServiceName, (LPRASENTRY)pRasEntry, &dwSize, NULL, NULL);
if (0 == dwRes)
{
//
// Then we were able to get the RasEntry, load the NetShell API
// and call HrCreateShortcut
//
if (plat.IsAtLeastNT51())
{
pfnLaunchConnectionExSpec pfnLaunchConnectionEx = NULL;
if (GetNetShellApis(NULL, NULL, &pfnLaunchConnectionEx))
{
//
// Launch Connections Folder and Connection together
//
DWORD dwFlags = 0x1; // 0x1 => Opens the folder before launching the connection
hr = (pfnLaunchConnectionEx)(dwFlags, pRasEntry->guidId);
MYVERIFY(SUCCEEDED(hr));
}
}
else
{
pfnLaunchConnectionSpec pfnLaunchConnection = NULL;
if (GetNetShellApis(&pfnLaunchConnection, NULL, NULL))
{
//
// Now Launch the Connections Folder
//
CLoadConnFolder Connections;
Connections.HrLaunchConnFolder();
//
// Finally Launch the Connection
//
hr = (pfnLaunchConnection)(pRasEntry->guidId);
MYVERIFY(SUCCEEDED(hr));
}
}
}
else
{
CMTRACE1(TEXT("LaunchProfile -- RasGetEntryProperties returned %u"), dwRes);
CMASSERTMSG(FALSE, TEXT("Unable to find the connection that we are supposed to launch in the RAS pbk."));
return HRESULT_FROM_WIN32(ERROR_CONNECTION_INVALID);
}
CmFree(pRasEntry);
}
}
else
{
SHELLEXECUTEINFO sei;
if ((NULL != pszFullPathToCmpFile) && (TEXT('\0') != pszFullPathToCmpFile))
{
TCHAR szCmmgrPath[MAX_PATH+1];
TCHAR szSystemDir[MAX_PATH+1];
TCHAR szCmp[MAX_PATH+1];
ZeroMemory(&szCmp, sizeof(szCmp));
ZeroMemory(&szCmmgrPath, sizeof(szCmmgrPath));
ZeroMemory(&szSystemDir, sizeof(szSystemDir));
lstrcpy(szCmp, TEXT("\""));
lstrcat(szCmp, pszFullPathToCmpFile);
lstrcat(szCmp, TEXT("\""));
UINT uRet = GetSystemDirectory(szSystemDir, MAX_PATH);
if ((0 == uRet) || (MAX_PATH < uRet))
{
//
// Give up, not the end of the world not to launch the profile
//
return E_UNEXPECTED;
}
else
{
wsprintf(szCmmgrPath, TEXT("%s\\cmmgr32.exe"), szSystemDir);
}
ZeroMemory(&sei, sizeof(sei));
sei.cbSize = sizeof(sei);
sei.fMask = SEE_MASK_FLAG_NO_UI;
sei.nShow = SW_SHOWNORMAL;
sei.lpFile = szCmmgrPath;
sei.lpParameters = szCmp;
sei.lpDirectory = szSystemDir;
if (!ShellExecuteEx(&sei))
{
CMASSERTMSG(FALSE, TEXT("Unable to launch installed connection!"));
}
else
{
hr = S_OK;
}
}
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: AllUserProfilesInstalled
//
// Synopsis: Checks if any profiles are listed in the HKLM Mappings key.
//
// Arguments: None
//
// Returns: BOOL - TRUE if mappings values exist in the HKLM mappings key
//
// History: quintinb Created Header 11/1/98
//
//+----------------------------------------------------------------------------
BOOL AllUserProfilesInstalled()
{
BOOL bReturn = FALSE;
HKEY hKey;
DWORD dwNumValues;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_pszRegCmMappings, 0,
KEY_READ, &hKey))
{
if ((ERROR_SUCCESS == RegQueryInfoKey(hKey, NULL, NULL, NULL, NULL, NULL, NULL,
&dwNumValues, NULL, NULL, NULL, NULL)) && (dwNumValues > 0))
{
//
// Then we have mappings values
//
bReturn = TRUE;
}
RegCloseKey(hKey);
}
return bReturn;
}
//+----------------------------------------------------------------------------
//
// Function: GetProcAddressFromRasApi32orRnaph
//
// Synopsis: A helper function to first look in RasApi32.dll (using the global
// dll class pointer) and then check in Rnaph.dll if the required
// function was not found.
//
// Arguments: LPTSTR pszFunc - String of the function to look for
// CPlatform* pPlat - a CPlatform class pointer to prevent creating
// and destructing a new one everytime this is called.
//
// Returns: LPVOID - NULL if the function wasn't found, a pFunc otherwise.
//
// History: quintinb Created 11/23/98
//
//+----------------------------------------------------------------------------
LPVOID GetProcAddressFromRasApi32orRnaph(LPCSTR pszFunc, CPlatform* pPlat)
{
LPVOID pFunc;
MYDBGASSERT(g_pRasApi32);
pFunc = g_pRasApi32->GetProcAddress(pszFunc);
if (NULL == pFunc)
{
//
// On win95 gold check rnaph
//
if (pPlat->IsWin95Gold())
{
if (NULL == g_pRnaph)
{
g_pRnaph = (CDynamicLibrary*)CmMalloc(sizeof(CDynamicLibrary));
if (NULL == g_pRnaph)
{
return FALSE;
}
}
if (!(g_pRnaph->IsLoaded()))
{
g_pRnaph->Load(TEXT("rnaph.dll"));
}
pFunc = g_pRnaph->GetProcAddress(pszFunc);
}
}
return pFunc;
}
//+----------------------------------------------------------------------------
//
// Function: GetNetShellApis
//
// Synopsis: This is a wrapper function to access the private Netshell api's that allow
// cmstp.exe to interact with the Connections folder on Windows 2000.
// This function caches the Netshell function pointers as they are
// accessed for later use. NULL can be passed if a function isn't required.
//
// Arguments: pfnLaunchConnectionSpec* pLaunchConnection - var to hold function pointer
// pfnCreateShortcutSpec* pCreateShortcut - var to hold function pointer
// pfnLaunchConnectionEx pLaunchConnectionEx - var to hold function pointer
//
// Returns: BOOL - TRUE if all required APIs were retrieved
//
// History: quintinb Created 2/17/99
//
//+----------------------------------------------------------------------------
BOOL GetNetShellApis(pfnLaunchConnectionSpec* pLaunchConnection, pfnCreateShortcutSpec* pCreateShortcut,
pfnLaunchConnectionExSpec* pLaunchConnectionEx)
{
CPlatform plat;
static pfnLaunchConnectionSpec pfnLaunchConnection = NULL;
static pfnCreateShortcutSpec pfnCreateShortcut = NULL;
static pfnLaunchConnectionExSpec pfnLaunchConnectionEx = NULL;
if (!(plat.IsAtLeastNT5()))
{
//
// These functions are only used on NT5. Return FALSE otherwise.
//
CMASSERTMSG(FALSE, TEXT("Trying to use NetShell Private Api's on platforms other than Windows 2000."));
return FALSE;
}
if (NULL == g_pNetShell)
{
g_pNetShell = (CDynamicLibrary*)CmMalloc(sizeof(CDynamicLibrary));
if (NULL == g_pNetShell)
{
return FALSE;
}
}
if (!(g_pNetShell->IsLoaded()))
{
g_pNetShell->Load(TEXT("netshell.dll"));
}
if (NULL != pLaunchConnection)
{
if (pfnLaunchConnection)
{
*pLaunchConnection = pfnLaunchConnection;
}
else
{
*pLaunchConnection = (pfnLaunchConnectionSpec)g_pNetShell->GetProcAddress("HrLaunchConnection");
if (NULL == *pLaunchConnection)
{
return FALSE;
}
else
{
pfnLaunchConnection = *pLaunchConnection;
}
}
}
if (NULL != pCreateShortcut)
{
if (pfnCreateShortcut)
{
*pCreateShortcut = pfnCreateShortcut;
}
else
{
*pCreateShortcut = (pfnCreateShortcutSpec)g_pNetShell->GetProcAddress("HrCreateDesktopIcon");
if (NULL == *pCreateShortcut)
{
return FALSE;
}
else
{
pfnCreateShortcut = *pCreateShortcut;
}
}
}
if (NULL != pLaunchConnectionEx)
{
if (pfnLaunchConnectionEx)
{
*pLaunchConnectionEx = pfnLaunchConnectionEx;
}
else
{
if (!(plat.IsAtLeastNT51()))
{
return FALSE;
}
else
{
*pLaunchConnectionEx = (pfnLaunchConnectionExSpec)g_pNetShell->GetProcAddress("HrLaunchConnectionEx");
if (NULL == *pLaunchConnectionEx)
{
return FALSE;
}
else
{
pfnLaunchConnectionEx = *pLaunchConnectionEx;
}
}
}
}
return TRUE;
}
//+----------------------------------------------------------------------------
//
// Function: GetRasApis
//
// Synopsis: This is a wrapper function to access the RasApis that cmstp.exe uses.
// This function caches the RAS api function pointers as they are
// accessed for later use. NULL can be passed if a function isn't required.
//
// Arguments: pfnRasDeleteEntrySpec* pRasDeleteEntry - var to hold func pointer
// pfnRasEnumEntriesSpec* pRasEnumEntries - var to hold func pointer
// pfnRasSetEntryPropertiesSpec* pRasSetEntryProperties - var to hold func pointer
// pfnRasEnumDevicesSpec* pRasEnumDevices - var to hold func pointer
// pfnRasSetCredentialsSpec* pRasSetCredentials - var to hold func pointer
//
// Returns: BOOL - TRUE if all required APIs were retrieved
//
// History: quintinb Created 11/23/98
//
//+----------------------------------------------------------------------------
BOOL GetRasApis(pfnRasDeleteEntrySpec* pRasDeleteEntry, pfnRasEnumEntriesSpec* pRasEnumEntries,
pfnRasSetEntryPropertiesSpec* pRasSetEntryProperties,
pfnRasEnumDevicesSpec* pRasEnumDevices, pfnRasGetEntryPropertiesSpec* pRasGetEntryProperties,
pfnRasSetCredentialsSpec* pRasSetCredentials)
{
CPlatform plat;
static pfnRasDeleteEntrySpec pfnRasDeleteEntry = NULL;
static pfnRasEnumEntriesSpec pfnRasEnumEntries = NULL;
static pfnRasSetEntryPropertiesSpec pfnRasSetEntryProperties = NULL;
static pfnRasEnumDevicesSpec pfnRasEnumDevices = NULL;
static pfnRasGetEntryPropertiesSpec pfnRasGetEntryProperties = NULL;
static pfnRasSetCredentialsSpec pfnRasSetCredentials = NULL;
if (NULL == g_pRasApi32)
{
g_pRasApi32 = (CDynamicLibrary*)CmMalloc(sizeof(CDynamicLibrary));
if (NULL == g_pRasApi32)
{
return FALSE;
}
}
if (!(g_pRasApi32->IsLoaded()))
{
g_pRasApi32->Load(TEXT("rasapi32.dll"));
}
if (NULL != pRasDeleteEntry)
{
if (pfnRasDeleteEntry)
{
*pRasDeleteEntry = pfnRasDeleteEntry;
}
else
{
*pRasDeleteEntry = (pfnRasDeleteEntrySpec)GetProcAddressFromRasApi32orRnaph("RasDeleteEntryA",
&plat);
if (NULL == *pRasDeleteEntry)
{
return FALSE;
}
else
{
pfnRasDeleteEntry = *pRasDeleteEntry;
}
}
}
if (NULL != pRasEnumEntries)
{
if (pfnRasEnumEntries)
{
*pRasEnumEntries = pfnRasEnumEntries;
}
else
{
*pRasEnumEntries = (pfnRasEnumEntriesSpec)g_pRasApi32->GetProcAddress("RasEnumEntriesA");
if (NULL == *pRasEnumEntries)
{
//
// A required Function couldn't be loaded
//
return FALSE;
}
else
{
pfnRasEnumEntries = *pRasEnumEntries;
}
}
}
if (NULL != pRasSetEntryProperties)
{
if (pfnRasSetEntryProperties)
{
*pRasSetEntryProperties = pfnRasSetEntryProperties;
}
else
{
*pRasSetEntryProperties = (pfnRasSetEntryPropertiesSpec)GetProcAddressFromRasApi32orRnaph("RasSetEntryPropertiesA",
&plat);
if (NULL == *pRasSetEntryProperties)
{
return FALSE;
}
else
{
pfnRasSetEntryProperties = *pRasSetEntryProperties;
}
}
}
if (NULL != pRasEnumDevices)
{
if (pfnRasEnumDevices)
{
*pRasEnumDevices = pfnRasEnumDevices;
}
else
{
*pRasEnumDevices = (pfnRasEnumDevicesSpec)GetProcAddressFromRasApi32orRnaph("RasEnumDevicesA",
&plat);
if (NULL == *pRasEnumDevices)
{
return FALSE;
}
else
{
pfnRasEnumDevices = *pRasEnumDevices;
}
}
}
if (NULL != pRasGetEntryProperties)
{
if (pfnRasGetEntryProperties)
{
*pRasGetEntryProperties = pfnRasGetEntryProperties;
}
else
{
*pRasGetEntryProperties = (pfnRasGetEntryPropertiesSpec)GetProcAddressFromRasApi32orRnaph("RasGetEntryPropertiesA", &plat);
if (NULL == *pRasGetEntryProperties)
{
return FALSE;
}
else
{
pfnRasGetEntryProperties = *pRasGetEntryProperties;
}
}
}
if (NULL != pRasSetCredentials)
{
if (pfnRasSetCredentials)
{
*pRasSetCredentials = pfnRasSetCredentials;
}
else
{
*pRasSetCredentials = (pfnRasSetCredentialsSpec)GetProcAddressFromRasApi32orRnaph("RasSetCredentialsA", &plat);
if (NULL == *pRasSetCredentials)
{
return FALSE;
}
else
{
pfnRasSetCredentials = *pRasSetCredentials;
}
}
}
return TRUE;
}
//+----------------------------------------------------------------------------
//
// Function: GetShell32Apis
//
// Synopsis: This function is used to load the shell32.dll and call getprocaddress
// on the needed functions. This function is used to speed up the process
// by keeping one copy of shell32.dll in memory and caching the function
// pointers requested. If a function pointer hasn't been requested yet,
// then it will have to be looked up.
//
// Arguments: pfnSHGetFolderPathSpec* pGetFolderPath - pointer for SHGetFolderPath
// pfnSHGetSpecialFolderPathWSpec* pGetSpecialFolderPathW - pointer for GetSpecialFolderPathW
//
// Returns: BOOL - TRUE if all requested function pointers were retreived.
//
// History: quintinb Created 11/23/98
//
//+----------------------------------------------------------------------------
BOOL GetShell32Apis(pfnSHGetFolderPathSpec* pGetFolderPath,
pfnSHGetSpecialFolderPathWSpec* pGetSpecialFolderPathW)
{
static pfnSHGetFolderPathSpec pfnSHGetFolderPath = NULL; // this takes a User token
static pfnSHGetSpecialFolderPathWSpec pfnSHGetSpecialFolderPathW = NULL;
#ifdef UNICODE
const CHAR c_pszSHGetFolderPath[] = "SHGetFolderPathW";
#else
const CHAR c_pszSHGetFolderPath[] = "SHGetFolderPathA";
#endif
const CHAR c_pszSHGetSpecialFolderPathW[] = "SHGetSpecialFolderPathW";
if (NULL == g_pShell32)
{
g_pShell32 = (CDynamicLibrary*)CmMalloc(sizeof(CDynamicLibrary));
if (NULL == g_pShell32)
{
return FALSE;
}
}
if (!(g_pShell32->IsLoaded()))
{
if(!g_pShell32->Load(TEXT("shell32.dll")))
{
return FALSE;
}
}
if (NULL != pGetFolderPath)
{
if (pfnSHGetFolderPath)
{
*pGetFolderPath = pfnSHGetFolderPath;
}
else
{
*pGetFolderPath = (pfnSHGetFolderPathSpec)g_pShell32->GetProcAddress(c_pszSHGetFolderPath);
if (NULL == *pGetFolderPath)
{
return FALSE;
}
else
{
pfnSHGetFolderPath = *pGetFolderPath;
}
}
}
if (NULL != pGetSpecialFolderPathW)
{
if (pfnSHGetSpecialFolderPathW)
{
*pGetSpecialFolderPathW = pfnSHGetSpecialFolderPathW;
}
else
{
*pGetSpecialFolderPathW = (pfnSHGetSpecialFolderPathWSpec)g_pShell32->GetProcAddress(c_pszSHGetSpecialFolderPathW);
if (NULL == *pGetSpecialFolderPathW)
{
return FALSE;
}
else
{
pfnSHGetSpecialFolderPathW = *pGetSpecialFolderPathW;
}
}
}
return TRUE;
}