Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

2318 lines
82 KiB

//+----------------------------------------------------------------------------
//
// File: customaction.cpp
//
// Module: CMAK.EXE
//
// Synopsis: Implemenation of the CustomActionList and CustomActionListEnumerator
// classes used by CMAK to handle its custom actions.
//
// Copyright (c) 2000 Microsoft Corporation
//
// Author: quintinb Created 02/26/00
//
//+----------------------------------------------------------------------------
#include <cmmaster.h>
//
// Include the shared custom action parsing code between CM and CMAK
//
#include "parseca.cpp"
//
// Include the locale-safe replacement for lstrcmpi
//
#include "CompareString.h"
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::CustomActionList
//
// Synopsis: Constructor for the CustomActionList class. Initializes the
// m_ActionSectionStrings array with all of the section strings and
// zeros all of the other parameters of the class.
//
// Arguments: None
//
// Returns: Nothing
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
CustomActionList::CustomActionList()
{
//
// First set the m_ActionSectionStrings so that we can read the actions
// from the appropriate sections in the cms file.
//
m_ActionSectionStrings[PREINIT] = (TCHAR*)c_pszCmSectionPreInit;
m_ActionSectionStrings[PRECONNECT] = (TCHAR*)c_pszCmSectionPreConnect;
m_ActionSectionStrings[PREDIAL] = (TCHAR*)c_pszCmSectionPreDial;
m_ActionSectionStrings[PRETUNNEL] = (TCHAR*)c_pszCmSectionPreTunnel;
m_ActionSectionStrings[ONCONNECT] = (TCHAR*)c_pszCmSectionOnConnect;
m_ActionSectionStrings[ONINTCONNECT] = (TCHAR*)c_pszCmSectionOnIntConnect;
m_ActionSectionStrings[ONDISCONNECT] = (TCHAR*)c_pszCmSectionOnDisconnect;
m_ActionSectionStrings[ONCANCEL] = (TCHAR*)c_pszCmSectionOnCancel;
m_ActionSectionStrings[ONERROR] = (TCHAR*)c_pszCmSectionOnError;
//
// Zero m_CustomActionHash
//
ZeroMemory(&m_CustomActionHash, c_iNumCustomActionTypes*sizeof(CustomActionListItem*));
//
// Zero the Display strings array
//
ZeroMemory(&m_ActionTypeStrings, (c_iNumCustomActionTypes)*sizeof(TCHAR*));
m_pszAllTypeString = NULL;
//
// Zero the Execution Strings
//
ZeroMemory(&m_ExecutionStrings, (c_iNumCustomActionExecutionStates)*sizeof(TCHAR*));
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::~CustomActionList
//
// Synopsis: Destructor for the CustomActionList class. Frees all memory
// allocated by the class including the CustomActionListItem
// structures stored in the array of linked lists (the true data
// of the class).
//
// Arguments: None
//
// Returns: Nothing
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
CustomActionList::~CustomActionList()
{
//
// Free the memory we allocated
//
for (int i = 0; i < c_iNumCustomActionTypes; i++)
{
//
// Free each CustomAction List
//
CustomActionListItem* pCurrent = m_CustomActionHash[i];
while (NULL != pCurrent)
{
CustomActionListItem* pNext = pCurrent->Next;
CmFree(pCurrent->pszParameters);
CmFree(pCurrent);
pCurrent = pNext;
}
//
// Free the Action Type display strings
//
CmFree(m_ActionTypeStrings[i]);
}
//
// Free the All Action display string
//
CmFree(m_pszAllTypeString);
//
// Free the execution strings
//
for (int i = 0; i < c_iNumCustomActionExecutionStates; i++)
{
CmFree(m_ExecutionStrings[i]);
}
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::ReadCustomActionsFromCms
//
// Synopsis: Reads all custom actions in from the given cms file and stores
// them in the classes custom action hash table by the type of
// custom action. This function relies on ParseCustomActionString
// to do the actual parsing of the custom action string. Given the
// current architecture of CM this function should really only be
// called once per class object as there is no way to reset the class object
// (other than explicitly calling the destructor). However, there is
// no code to prevent the caller from pulling connect actions from more than
// one source. Thus let the caller beware.
//
// Arguments: HINSTANCE hInstance - instance handle to load string resources
// TCHAR* pszCmsFile - full path to the cms file to get
// the custom actions from
// TCHAR* pszShortServiceName - short service name of the profile
//
// Returns: HRESULT - standard COM error codes
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::ReadCustomActionsFromCms(HINSTANCE hInstance, TCHAR* pszCmsFile, TCHAR* pszShortServiceName)
{
MYDBGASSERT(hInstance);
MYDBGASSERT(pszCmsFile);
MYDBGASSERT(pszShortServiceName);
if ((NULL == hInstance) || (NULL == pszCmsFile) || (NULL == pszShortServiceName))
{
return E_INVALIDARG;
}
HRESULT hr = S_OK;
int iFileNum = 0;
for (int i = 0; i < c_iNumCustomActionTypes; i++)
{
TCHAR szNum[MAX_PATH+1];
LPTSTR pszTemp = NULL;
CustomActionListItem CustomAction;
iFileNum = 0;
do
{
CmFree(pszTemp);
MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, TEXT("%d"), iFileNum));
pszTemp = GetPrivateProfileStringWithAlloc(m_ActionSectionStrings[i], szNum, TEXT(""), pszCmsFile);
if (pszTemp)
{
MYDBGASSERT(pszTemp[0]);
hr = ParseCustomActionString(pszTemp, &CustomAction, pszShortServiceName);
if (SUCCEEDED(hr))
{
//
// We have parsed the string, now we need to get the Flags and the description
//
CustomAction.Type = (CustomActionTypes)i;
MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, c_pszCmEntryConactDesc, iFileNum));
GetPrivateProfileString(m_ActionSectionStrings[i], szNum, TEXT(""), CustomAction.szDescription, CELEMS(CustomAction.szDescription), pszCmsFile); //lint !e534
MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, c_pszCmEntryConactFlags, iFileNum));
CustomAction.dwFlags = (DWORD)GetPrivateProfileInt(m_ActionSectionStrings[i], szNum, 0, pszCmsFile);
hr = Add(hInstance, &CustomAction, pszShortServiceName);
if (FAILED(hr))
{
CMASSERTMSG(FALSE, TEXT("CustomActionList::ReadCustomActionsFromCms -- Unable to add a custom action to the list, Add failed."));
}
CmFree(CustomAction.pszParameters);
CustomAction.pszParameters = NULL;
}
else
{
CMTRACE2(TEXT("ReadCustomActionsFromCms -- Unable to parse %s, hr=%d"), pszTemp, hr);
}
}
iFileNum++;
} while(pszTemp);
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::ParseCustomActionString
//
// Synopsis: This function takes a custom action string retrieved from a
// cms file and parses it into the various parts of a custom
// action (program, parameters, function name, etc.)
//
// Arguments: LPTSTR pszStringToParse - custom action buffer to be parsed into
// the various parts of a custom action
// CustomActionListItem* pCustomAction - pointer to a custom action
// structure to be filled in
// TCHAR* pszShortServiceName - short service name of the profile
//
// Returns: HRESULT - standard COM error codes
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::ParseCustomActionString(LPTSTR pszStringToParse, CustomActionListItem* pCustomAction, TCHAR* pszShortServiceName)
{
MYDBGASSERT(pszStringToParse);
MYDBGASSERT(TEXT('\0') != pszStringToParse[0]);
MYDBGASSERT(pCustomAction);
MYDBGASSERT(pszShortServiceName);
if ((NULL == pszStringToParse) || (TEXT('\0') == pszStringToParse[0]) ||
(NULL == pCustomAction) || (NULL == pszShortServiceName))
{
return E_INVALIDARG;
}
//
// Zero the CustomAction struct
//
ZeroMemory(pCustomAction, sizeof(CustomActionListItem));
CmStrTrim(pszStringToParse);
LPTSTR pszProgram = NULL;
LPTSTR pszFunctionName = NULL;
HRESULT hr = HrParseCustomActionString(pszStringToParse, &pszProgram,
&(pCustomAction->pszParameters), &pszFunctionName);
if (SUCCEEDED(hr))
{
lstrcpyn(pCustomAction->szProgram, pszProgram, CELEMS(pCustomAction->szProgram));
lstrcpyn(pCustomAction->szFunctionName, pszFunctionName, CELEMS(pCustomAction->szFunctionName));
//
// Now we have the filename string, but we need to check to see if
// it includes the relative path. If so, then we need to set
// bIncludeBinary to TRUE;
//
TCHAR szTemp[MAX_PATH+1];
if (MAX_PATH >= (lstrlen(g_szOsdir) + lstrlen(pCustomAction->szProgram)))
{
MYVERIFY(CELEMS(szTemp) > (UINT)wsprintf(szTemp, TEXT("%s%s"), g_szOsdir, pCustomAction->szProgram));
pCustomAction->bIncludeBinary = FileExists(szTemp);
}
else
{
pCustomAction->bIncludeBinary = FALSE;
}
}
CmFree(pszProgram);
CmFree(pszFunctionName);
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::WriteCustomActionsToCms
//
// Synopsis: This function takes a custom action string retrieved from a
// cms file and parses it into the various parts of a custom
// action (program, parameters, function name, etc.)
//
// Arguments: TCHAR* pszCmsFile - Cms file to write the custom action to
// TCHAR* pszShortServiceName - short service name of the profile
// BOOL bUseTunneling - whether this a tunneling profile or not,
// controls whether Pre-Tunnel actions should be
// written and whether the Flags parameter for
// each action should be written (since they are
// only needed if tunneling is an option).
//
// Returns: HRESULT - standard COM error codes
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::WriteCustomActionsToCms(TCHAR* pszCmsFile, TCHAR* pszShortServiceName, BOOL bUseTunneling)
{
HRESULT hr = S_OK;
for (int i = 0; i < c_iNumCustomActionTypes; i++)
{
//
// Clear out the section
//
MYVERIFY(0 != WritePrivateProfileSection(m_ActionSectionStrings[i], TEXT("\0\0"), pszCmsFile));
//
// Make sure that we have a linked list of actions to process and that if we
// are writing PRETUNNEL actions that we are actually tunneling.
//
if (m_CustomActionHash[i] && (i != PRETUNNEL || (i == PRETUNNEL && bUseTunneling)))
{
int iFileNum = 0;
CustomActionListItem* pItem = m_CustomActionHash[i];
while (pItem)
{
if (pItem->szProgram[0])
{
//
// Get just the filename of the program
//
TCHAR szName[MAX_PATH+1];
if (pItem->bIncludeBinary)
{
wsprintf(szName, TEXT("%s\\%s"), pszShortServiceName, GetName(pItem->szProgram));
}
else
{
lstrcpyn(szName, pItem->szProgram, CELEMS(szName));
}
UINT uSizeNeeded = lstrlen(szName);
LPTSTR pszSpace = CmStrchr(szName, TEXT(' '));
BOOL bLongName = (NULL != pszSpace);
if (bLongName)
{
uSizeNeeded = uSizeNeeded + 2; // for the two plus signs
}
if (pItem->szFunctionName[0])
{
uSizeNeeded = uSizeNeeded + lstrlen(pItem->szFunctionName) + 1;// add one for the comma
}
if (pItem->pszParameters && pItem->pszParameters[0])
{
uSizeNeeded = uSizeNeeded + lstrlen(pItem->pszParameters) + 1;// add one for the space
}
uSizeNeeded = (uSizeNeeded + 1) * sizeof(TCHAR);
LPTSTR pszBuffer = (LPTSTR)CmMalloc(uSizeNeeded);
if (pszBuffer)
{
if (bLongName)
{
pszBuffer[0] = TEXT('+');
}
lstrcat(pszBuffer, szName);
if (bLongName)
{
lstrcat(pszBuffer, TEXT("+"));
}
if (pItem->szFunctionName[0])
{
lstrcat(pszBuffer, TEXT(","));
lstrcat(pszBuffer, pItem->szFunctionName);
}
if (pItem->pszParameters && pItem->pszParameters[0])
{
lstrcat(pszBuffer, TEXT(" "));
lstrcat(pszBuffer, pItem->pszParameters);
}
//
// Now write the buffer string out to the cms file
//
TCHAR szNum[MAX_PATH+1];
MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, TEXT("%d"), iFileNum));
if (0 != WritePrivateProfileString(m_ActionSectionStrings[i], szNum, pszBuffer, pszCmsFile))
{
//
// if dwFlags == 0 or bUseTunneling is FALSE (and the NonInteractive flag isn't set)
// then delete the flags line instead of setting it. We only need the flags to
// tell us when to run a connect action if we have the option of tunneling unless the
// Admin has specified that this connect action can run non-interactively (without
// showing UI).
//
LPTSTR pszFlagsValue = NULL;
if (0 != pItem->dwFlags)
{
if (bUseTunneling)
{
MYVERIFY(CELEMS(szName) > (UINT)wsprintf(szName, TEXT("%u"), pItem->dwFlags));
pszFlagsValue = szName;
}
else if (pItem->dwFlags & NONINTERACTIVE)
{
MYVERIFY(CELEMS(szName) > (UINT)wsprintf(szName, TEXT("%u"), NONINTERACTIVE));
pszFlagsValue = szName;
}
}
MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, c_pszCmEntryConactFlags, iFileNum));
if (0 == WritePrivateProfileString(m_ActionSectionStrings[i], szNum, pszFlagsValue, pszCmsFile))
{
hr = HRESULT_FROM_WIN32(GetLastError());
CMTRACE1(TEXT("CustomActionList::WriteCustomActionsToCms -- unable to write flags, hr is 0x%x"), hr);
}
//
// If description parameter is null or is only a temporary description, then delete the
// description instead of writing it.
//
LPTSTR pszDescValue = NULL;
if (pItem->szDescription[0] && !pItem->bTempDescription)
{
pszDescValue = pItem->szDescription;
}
MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, c_pszCmEntryConactDesc, iFileNum));
if (0 == WritePrivateProfileString(m_ActionSectionStrings[i], szNum, pszDescValue, pszCmsFile))
{
hr = HRESULT_FROM_WIN32(GetLastError());
CMTRACE1(TEXT("CustomActionList::WriteCustomActionsToCms -- unable to write description, hr is 0x%x"), hr);
}
}
else
{
hr = HRESULT_FROM_WIN32(GetLastError());
CMTRACE1(TEXT("CustomActionList::WriteCustomActionsToCms -- unable to write connect action, hr is 0x%x"), hr);
}
CmFree(pszBuffer);
}
else
{
CMASSERTMSG(FALSE, TEXT("CustomActionList::WriteCustomActionsToCms -- Unable to allocate pszBuffer!"));
}
}
else
{
CMASSERTMSG(FALSE, TEXT("WriteCustomActionsToCms -- custom action with empty program field!"));
}
pItem = pItem->Next;
iFileNum++;
}
}
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::AddOrRemoveCmdl
//
// Synopsis: This function is designed ensure that the builtin custom action
// cmdl is either in the custom action list or is removed from the
// custom action list depending on the bAddCmdl flag. Thus if the
// Flag is TRUE the connect action is added if it doesn't exist
// already. If the bAddCmdl flag is FALSE then the custom action is
// removed from the list. Also note that there is now two cmdl
// variations that could exist in a profile. One for downloading
// VPN updates and one for PBK updates. Thus we also have the
// bForVpn flag that controls which version of the custom action
// we are adding or removing.
//
// Arguments: HINSTANCE hInstance - instance handle to load string resources
// BOOL bAddCmdl - whether cmdl should be added or deleted
//
// Returns: HRESULT - standard COM error codes
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::AddOrRemoveCmdl(HINSTANCE hInstance, BOOL bAddCmdl, BOOL bForVpn)
{
HRESULT hr;
CustomActionListItem* pItem = NULL;
CustomActionListItem* pCurrent;
CustomActionListItem* pFollower;
if ((NULL == hInstance))
{
hr = E_INVALIDARG;
goto exit;
}
//
// cmdl32.exe
//
pItem = (CustomActionListItem*)CmMalloc(sizeof(CustomActionListItem));
MYDBGASSERT(pItem);
if (pItem)
{
UINT uDescId;
if (bForVpn)
{
uDescId = IDS_CMDL_VPN_DESC;
pItem->pszParameters = CmStrCpyAlloc(TEXT("/VPN %PROFILE%"));
}
else
{
uDescId = IDS_CMDL_DESC;
pItem->pszParameters = CmStrCpyAlloc(TEXT("%PROFILE%"));
}
MYVERIFY(LoadString(hInstance, uDescId, pItem->szDescription, CELEMS(pItem->szDescription)));
lstrcpy(pItem->szProgram, TEXT("cmdl32.exe"));
pItem->Type = ONCONNECT;
pItem->bBuiltInAction = TRUE;
pItem->bTempDescription = TRUE;
pItem->dwFlags = NONINTERACTIVE;
MYDBGASSERT(pItem->pszParameters);
if (pItem->pszParameters)
{
hr = Find(hInstance, pItem->szDescription, pItem->Type, &pCurrent, &pFollower);
if (FAILED(hr))
{
//
// No cmdl32.exe. If bAddCmdl is TRUE then we need to add it, otherwise our job here is done.
// If we are going to add it, lets make it the first in the list. The user can move it later
// if they wish.
//
if (bAddCmdl)
{
pItem->Next = m_CustomActionHash[pItem->Type];
m_CustomActionHash[pItem->Type] = pItem;
pItem = NULL; // don't free pItem
}
hr = S_OK;
}
else
{
//
// cmdl32.exe already exists and bAddCmdl is TRUE, nothing to do. If bAddCmdl is FALSE
// and it already exists then we need to delete it.
//
if (bAddCmdl)
{
hr = S_FALSE;
//
// Make sure to set the dwFlags to NONINTERACTIVE since this might be an upgrade.
//
if (pCurrent)
{
pCurrent->dwFlags = pCurrent->dwFlags | NONINTERACTIVE;
}
}
else
{
hr = Delete(hInstance, pItem->szDescription, pItem->Type);
}
}
}
else
{
hr = E_OUTOFMEMORY;
goto exit;
}
}
else
{
hr = E_OUTOFMEMORY;
goto exit;
}
exit:
if (pItem)
{
CmFree(pItem->pszParameters);
CmFree(pItem);
}
return hr;
}
HRESULT DuplicateCustomActionListItem(CustomActionListItem* pCustomAction, CustomActionListItem** ppNewItem)
{
HRESULT hr = S_OK;
if (pCustomAction && ppNewItem)
{
*ppNewItem = (CustomActionListItem*)CmMalloc(sizeof(CustomActionListItem));
if (*ppNewItem)
{
//
// Duplicate the existing item
//
CopyMemory(*ppNewItem, pCustomAction, sizeof(CustomActionListItem));
//
// NULL out the Next pointer
//
(*ppNewItem)->Next = NULL;
//
// If we have a param string, that must also be duplicated since
// it is an allocated string.
//
if (pCustomAction->pszParameters)
{
(*ppNewItem)->pszParameters = CmStrCpyAlloc(pCustomAction->pszParameters);
if (NULL == (*ppNewItem)->pszParameters)
{
hr = E_OUTOFMEMORY;
CmFree(*ppNewItem);
*ppNewItem = NULL;
}
}
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
hr = E_INVALIDARG;
*ppNewItem = NULL;
CMASSERTMSG(FALSE, TEXT("DuplicateCustomActionListItem"));
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::Add
//
// Synopsis: This function adds the given custom action to the custom action
// hash table. Note that add is for new items and returns an error
// if an existing custom action of the same description and type
// already exists. Also note that the CustomActionListItem passed in
// is not just added to the hash table. Add creates its own memory
// for the custom action objects and the caller should not expect
// add to free the past in memory.
//
// Arguments: HINSTANCE hInstance - instance handle to load string resources
// CustomActionListItem* pCustomAction - custom action structure to
// add to the list of existing
// custom actions
//
// Returns: HRESULT - standard COM error codes
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::Add(HINSTANCE hInstance, CustomActionListItem* pCustomAction, LPCTSTR pszShortServiceName)
{
HRESULT hr = S_OK;
MYDBGASSERT(hInstance);
MYDBGASSERT(pCustomAction);
MYDBGASSERT(pCustomAction->szProgram[0]);
if ((NULL == hInstance) || (NULL == pCustomAction) || (TEXT('\0') == pCustomAction->szProgram[0]))
{
hr = E_INVALIDARG;
goto exit;
}
//
// First make sure that we have a description parameter because the description
// and the Type uniquely identify a custom action
//
TCHAR szCmProxy[MAX_PATH+1];
TCHAR szCmRoute[MAX_PATH+1];
wsprintf(szCmRoute, TEXT("%s\\cmroute.dll"), pszShortServiceName);
wsprintf(szCmProxy, TEXT("%s\\cmproxy.dll"), pszShortServiceName);
if (TEXT('\0') == pCustomAction->szDescription[0])
{
if (IsCmDl(pCustomAction))
{
//
// Cmdl32.exe as a post built-in custom action normally gets added through
// AddOrRemoveCmdl. However, to allow the user to move the custom actions
// around in the list, we want to add it here. Note, that we must distinguish
// between the VPN download and the PBK download so that we get the description correct on each.
//
LPTSTR pszVpnSwitch = CmStrStr(pCustomAction->pszParameters, TEXT("/v"));
UINT uDescStringId;
if (NULL == pszVpnSwitch)
{
pszVpnSwitch = CmStrStr(pCustomAction->pszParameters, TEXT("/V"));
}
if (pszVpnSwitch)
{
uDescStringId = IDS_CMDL_VPN_DESC;
}
else
{
uDescStringId = IDS_CMDL_DESC;
}
pCustomAction->bBuiltInAction = TRUE;
pCustomAction->bTempDescription = TRUE;
MYVERIFY(LoadString(hInstance, uDescStringId, pCustomAction->szDescription, CELEMS(pCustomAction->szDescription)));
}
else
{
hr = FillInTempDescription(pCustomAction);
MYDBGASSERT(SUCCEEDED(hr));
}
}
else if (0 == lstrcmpi(pCustomAction->szProgram, szCmProxy))
{
if (ONCONNECT == pCustomAction->Type)
{
pCustomAction->bBuiltInAction = TRUE;
MYVERIFY(LoadString(hInstance, IDS_CMPROXY_CON_DESC, pCustomAction->szDescription, CELEMS(pCustomAction->szDescription)));
}
else if (ONDISCONNECT == pCustomAction->Type)
{
pCustomAction->bBuiltInAction = TRUE;
MYVERIFY(LoadString(hInstance, IDS_CMPROXY_DIS_DESC, pCustomAction->szDescription, CELEMS(pCustomAction->szDescription)));
}
}
else if (0 == lstrcmpi(pCustomAction->szProgram, szCmRoute))
{
if (ONCONNECT == pCustomAction->Type)
{
pCustomAction->bBuiltInAction = TRUE;
MYVERIFY(LoadString(hInstance, IDS_CMROUTE_DESC, pCustomAction->szDescription, CELEMS(pCustomAction->szDescription)));
}
}
//
// First figure out if we already have a list of connect actions for
// the type specified. If not, then create one.
//
if (NULL == m_CustomActionHash[pCustomAction->Type])
{
hr = DuplicateCustomActionListItem(pCustomAction, &(m_CustomActionHash[pCustomAction->Type]));
goto exit;
}
else
{
CustomActionListItem* pCurrent = NULL;
CustomActionListItem* pFollower = NULL;
//
// Search for an existing record with the same description. If one exists return
// an error that it already exists.
//
hr = Find(hInstance, pCustomAction->szDescription, pCustomAction->Type, &pCurrent, &pFollower);
if (SUCCEEDED(hr))
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
goto exit;
}
//
// If we got here, then we have a list but we don't have a matching entry. Thus
// we must add a new entry to the end of the list
//
if (pFollower && (NULL == pFollower->Next))
{
hr = DuplicateCustomActionListItem(pCustomAction, &(pFollower->Next));
goto exit;
}
else
{
CMASSERTMSG(FALSE, TEXT("CustomActionList::Add -- couldn't find place to add the new element!"));
hr = E_UNEXPECTED;
goto exit;
}
}
exit:
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::Edit
//
// Synopsis: This function is used to edit an existing action. The function
// tries to replace the old action with the new one, keeping the
// same place in the respective custom action list. However, since
// the new item could be of a different type than the old item, this
// isn't always possible. When the item changes type, it is deleted
// from the old list and appended to the new custom action type list.
// Also note, that when the caller is attempting to rename or re-type
// an item, the function checks for collisions with existing items
// of that name/type. If the caller tries to rename an item
// to the same name/type as another existing item then the function returns
// an error.
//
// Arguments: HINSTANCE hInstance - instance handle to load string resources
// CustomActionListItem* pOldCustomAction - a custom action struct
// containing at least the
// description and type of
// the item that is to be
// editted.
// CustomActionListItem* pNewCustomAction - the new data for the
// custom action.
//
// Returns: HRESULT - standard COM error codes
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::Edit(HINSTANCE hInstance, CustomActionListItem* pOldCustomAction, CustomActionListItem* pNewCustomAction, LPCTSTR pszShortServiceName)
{
MYDBGASSERT(hInstance);
MYDBGASSERT(pOldCustomAction);
MYDBGASSERT(pNewCustomAction);
MYDBGASSERT(pNewCustomAction->szDescription[0]);
MYDBGASSERT(pOldCustomAction->szDescription[0]);
if ((NULL == hInstance) || (NULL == pOldCustomAction) || (NULL == pNewCustomAction) ||
(TEXT('\0') == pOldCustomAction->szDescription[0]) || (TEXT('\0') == pNewCustomAction->szDescription[0]))
{
return E_INVALIDARG;
}
//
// First try to find the old custom action
//
CustomActionListItem* pTemp = NULL;
CustomActionListItem* pTempFollower = NULL;
CustomActionListItem* pExistingItem = NULL;
CustomActionListItem* pFollower = NULL;
CustomActionListItem** ppPointerToFillIn = NULL;
HRESULT hr = Find (hInstance, pOldCustomAction->szDescription, pOldCustomAction->Type, &pExistingItem, &pFollower);
if (SUCCEEDED(hr))
{
//
// Okay, we found the old custom action. If the type and desc are the same between the two actions,
// then all we need to do is copy over the data and be done with it. However, if the user changed
// the type or description then we need to double check that an action with the description and type
// of the new action doesn't already exist (editting action XYZ of type Post-Connect
// into action XYZ of type Pre-Connect when there already exists XYZ of type Pre-Connect).
//
if ((pOldCustomAction->Type == pNewCustomAction->Type) &&
(0 == lstrcmpi(pExistingItem->szDescription, pNewCustomAction->szDescription)))
{
if (NULL == pFollower)
{
ppPointerToFillIn = &(m_CustomActionHash[pNewCustomAction->Type]);
}
else
{
ppPointerToFillIn = &(pFollower->Next);
}
hr = DuplicateCustomActionListItem(pNewCustomAction, ppPointerToFillIn);
if (SUCCEEDED(hr))
{
(*ppPointerToFillIn)->Next = pExistingItem->Next;
CmFree(pExistingItem->pszParameters);
CmFree(pExistingItem);
pExistingItem = NULL;
}
}
else
{
hr = Find (hInstance, pNewCustomAction->szDescription, pNewCustomAction->Type, &pTemp, &pTempFollower);
if (SUCCEEDED(hr))
{
//
// If the caller really wants to do this, then have them delete the old custom action
// and then call edit with the new custom action as both old and new.
//
hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
}
else
{
//
// If the types are different then it needs to go on a different sub list. If
// only the name is different then we just need to copy it over.
//
if(pOldCustomAction->Type != pNewCustomAction->Type)
{
//
// Delete the old action of type X
//
hr = Delete(hInstance, pOldCustomAction->szDescription, pOldCustomAction->Type);
MYDBGASSERT(SUCCEEDED(hr));
//
// Add the new action of type Y
//
if (SUCCEEDED(hr))
{
hr = Add(hInstance, pNewCustomAction, pszShortServiceName);
MYDBGASSERT(SUCCEEDED(hr));
}
}
else
{
if (NULL == pFollower)
{
ppPointerToFillIn = &(m_CustomActionHash[pNewCustomAction->Type]);
}
else
{
ppPointerToFillIn = &(pFollower->Next);
}
hr = DuplicateCustomActionListItem(pNewCustomAction, ppPointerToFillIn);
if (SUCCEEDED(hr))
{
(*ppPointerToFillIn)->Next = pExistingItem->Next;
CmFree(pExistingItem->pszParameters);
CmFree(pExistingItem);
pExistingItem = NULL;
}
}
}
}
}
else
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::Find
//
// Synopsis: This function searches the array of linked lists for an item with
// the given type and description. If it finds the the item it returns
// successfully and fills in the ppItem and ppFollower pointers with
// pointers to the item itself and the item before the requested item,
// respectively. If the item is the first item in the list, then
// *ppFollower will be NULL. Note that this function is internal to the
// class because it returns pointers to the classes internal data.
// Also note, that if we have a list, but don't find the desired item
// then *ppFollower returns the last item in the list. This is desired
// behavior since it allows Add to use *ppFollower to directly add a new
// item to the list.
//
// Arguments: HINSTANCE hInstance - instance handle for resources
// LPCTSTR pszDescription - description of the item to look for
// CustomActionTypes Type - type of the item to look for
// CustomActionListItem** ppItem - an OUT param that is filled in with
// a pointer to the item on a successful find
// CustomActionListItem** ppFollower - an OUT param that is filled in with
// a pointer to the item before the
// item in the list on a successful find
// (note that this is useful since it
// is a singly linked list). This
// param will be NULL if the item is the
// first item in the list on a successful
// find.
//
//
// Returns: HRESULT - standard COM error codes
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::Find(HINSTANCE hInstance, LPCTSTR pszDescription, CustomActionTypes Type, CustomActionListItem** ppItem, CustomActionListItem** ppFollower)
{
if ((NULL == hInstance) || (NULL == pszDescription) || (TEXT('\0') == pszDescription[0]) || (NULL == ppItem) || (NULL == ppFollower))
{
return E_INVALIDARG;
}
HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
CustomActionListItem* pCurrent = m_CustomActionHash[Type];
TCHAR szDescWithBuiltInSuffix[MAX_PATH+1];
*ppFollower = NULL;
*ppItem = NULL;
LPTSTR pszBuiltInSuffix = CmLoadString(hInstance, IDS_BUILT_IN); // if we got a NULL pointer then just don't do the extra compare
MYDBGASSERT(pszBuiltInSuffix);
//
// Search the list to find the item
//
while (pCurrent)
{
if (0 == lstrcmpi(pCurrent->szDescription, pszDescription))
{
//
// We found the item
//
*ppItem = pCurrent;
hr = S_OK;
break;
}
else if (pszBuiltInSuffix && pCurrent->bBuiltInAction)
{
//
// This is a built in action, lets try adding the builtin string to the description
// and try the comparision again
//
wsprintf(szDescWithBuiltInSuffix, TEXT("%s%s"), pCurrent->szDescription, pszBuiltInSuffix);
if (0 == lstrcmpi(szDescWithBuiltInSuffix, pszDescription))
{
*ppItem = pCurrent;
hr = S_OK;
break;
}
else
{
*ppFollower = pCurrent;
pCurrent = pCurrent->Next;
}
}
else
{
*ppFollower = pCurrent;
pCurrent = pCurrent->Next;
}
}
CmFree(pszBuiltInSuffix);
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::Delete
//
// Synopsis: This function searches through the array of custom action lists
// to find an item with the given description and type. If it finds
// the item it deletes it from the list. If the item cannot be found
// an error is returned.
//
// Arguments: TCHAR* pszDescription - description of the item to look for
// CustomActionTypes Type - type of the item to look for
//
// Returns: HRESULT - standard COM error codes
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::Delete(HINSTANCE hInstance, TCHAR* pszDescription, CustomActionTypes Type)
{
HRESULT hr = S_OK;
if ((NULL == pszDescription) || (TEXT('\0') == pszDescription[0]))
{
return E_INVALIDARG;
}
CustomActionListItem* pCurrent = NULL;
CustomActionListItem* pFollower = NULL;
hr = Find(hInstance, pszDescription, Type, &pCurrent, &pFollower);
if (SUCCEEDED(hr))
{
//
// We found the item to delete
//
if (pFollower)
{
pFollower->Next = pCurrent->Next;
}
else
{
//
// It is the first item in the list
//
m_CustomActionHash[Type] = pCurrent->Next;
}
CmFree(pCurrent->pszParameters);
CmFree(pCurrent);
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::MoveUp
//
// Synopsis: Moves the custom action specified by the given description and type
// up one place in the linked list for the given type. Note that if
// the custom action is already at the top of its list, we return
// S_FALSE;
//
// Arguments: TCHAR* pszDescription - description of the custom action to move
// CustomActionTypes Type - type of the custom action to move
//
// Returns: HRESULT - standard COM error codes. Note that S_FALSE denotes that
// MoveUp succeeded but that the item was already at the
// head of its list and thus couldn't be moved.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::MoveUp(HINSTANCE hInstance, TCHAR* pszDescription, CustomActionTypes Type)
{
if ((NULL == pszDescription) || (TEXT('\0') == pszDescription[0]))
{
return E_INVALIDARG;
}
HRESULT hr = E_UNEXPECTED;
CustomActionListItem* pCurrent = NULL;
CustomActionListItem* pFollower = NULL;
CustomActionListItem* pBeforeFollower = NULL;
hr = Find(hInstance, pszDescription, Type, &pCurrent, &pFollower);
if (SUCCEEDED(hr))
{
//
// We found the item to move up
//
if (pFollower)
{
//
// Now Find the item in front of pFollower
//
hr = Find(hInstance, pFollower->szDescription, pFollower->Type, &pFollower, &pBeforeFollower);
if (SUCCEEDED(hr))
{
if (pBeforeFollower)
{
pBeforeFollower->Next = pCurrent;
}
else
{
//
// pFollower is first in the list
//
m_CustomActionHash[Type] = pCurrent;
}
pFollower->Next = pCurrent->Next;
pCurrent->Next = pFollower;
hr = S_OK;
}
}
else
{
//
// It is the first item in the list, we cannot move it up
//
hr = S_FALSE;
}
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::MoveDown
//
// Synopsis: Moves the custom action specified by the given description and type
// down one place in the linked list for the given type. Note that if
// the custom action is already at the bottom of its list, we return
// S_FALSE;
//
// Arguments: TCHAR* pszDescription - description of the custom action to move
// CustomActionTypes Type - type of the custom action to move
//
// Returns: HRESULT - standard COM error codes. Note that S_FALSE denotes that
// MoveDown succeeded but that the item was already at the
// tail of its list and thus couldn't be moved.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::MoveDown(HINSTANCE hInstance, TCHAR* pszDescription, CustomActionTypes Type)
{
if ((NULL == pszDescription) || (TEXT('\0') == pszDescription[0]))
{
return E_INVALIDARG;
}
HRESULT hr = E_UNEXPECTED;
CustomActionListItem* pCurrent = NULL;
CustomActionListItem* pFollower = NULL;
hr = Find(hInstance, pszDescription, Type, &pCurrent, &pFollower);
if (SUCCEEDED(hr))
{
//
// We found the item to move down
//
if (NULL == pCurrent->Next)
{
//
// The item is already last in its list
//
hr = S_FALSE;
}
else if (pFollower)
{
pFollower->Next = pCurrent->Next;
pCurrent->Next = pFollower->Next->Next;
pFollower->Next->Next = pCurrent;
}
else
{
//
// Then the item is first in the list
//
m_CustomActionHash[Type] = pCurrent->Next;
pCurrent->Next = m_CustomActionHash[Type]->Next;
m_CustomActionHash[Type]->Next = pCurrent;
}
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::AddCustomActionTypesToComboBox
//
// Synopsis: This function adds the custom action type strings (Pre-Connect,
// Post-Connect, etc.) to the given combo box. Note that whether
// tunneling is enabled or not and whether the All string is asked for
// or not affects the strings added to the combo.
//
// Arguments: HWND hDlg - Window handle of the dialog that contains the combobox
// UINT uCtrlId - combo box control ID to add the strings too
// HINSTANCE hInstance - instance handle used to load resource strings
// BOOL bUseTunneling - is this a tunneling profile?
// BOOL bAddAll - should we include the <All> selection in the list?
//
// Returns: HRESULT - standard COM error codes
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::AddCustomActionTypesToComboBox(HWND hDlg, UINT uCtrlId, HINSTANCE hInstance, BOOL bUseTunneling, BOOL bAddAll)
{
if ((0 == hDlg) || (0 == uCtrlId))
{
return E_INVALIDARG;
}
HRESULT hr = S_OK;
//
// Clear the combo list
//
SendDlgItemMessage(hDlg, uCtrlId, CB_RESETCONTENT, 0, (LPARAM)0); //lint !e534 CB_RESETCONTENT doesn't return anything useful
//
// Ensure the type strings are loaded
//
hr = EnsureActionTypeStringsLoaded(hInstance);
if (SUCCEEDED(hr))
{
//
// Setup the all display string, if needed
//
if (bAddAll)
{
SendDlgItemMessage(hDlg, uCtrlId, CB_ADDSTRING, 0, (LPARAM)m_pszAllTypeString);
}
//
// Setup the rest of the display strings
//
for (int i = 0; i < c_iNumCustomActionTypes; i++)
{
//
// Don't Add the PreTunnel String unless we are tunneling
//
if (i != PRETUNNEL || (i == PRETUNNEL && bUseTunneling))
{
SendDlgItemMessage(hDlg, uCtrlId, CB_ADDSTRING, 0, (LPARAM)m_ActionTypeStrings[i]);
}
}
}
else
{
CMASSERTMSG(FALSE, TEXT("CustomActionList::AddCustomActionTypesToComboBox -- Failed to load type strings"));
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::AddCustomActionsToListView
//
// Synopsis: This function adds actions of the given type to the given
// list view control. After adding the actions it sets the
// selection mark and highlight to the given value (defaulting
// to the first item in the list).
//
// Arguments: HWND hListView - window handle of the list view control
// HINSTANCE hInstance - instance handle of the exe, used for resources
// CustomActionTypes Type - type of custom action to add to the list
// view control, see the CustomActionTypes
// definition for more info
// BOOL bUseTunneling - whether the tunneling is enabled or not for
// the current profile. Determines whether
// PreTunnel actions should be shown in the
// ALL action view (and raises an error if
// PreTunnel is specified but FALSE is passed).
// int iItemToSelect - after the items are added to the list, the
// selection mark is set. This defaults to 0, but
// if the caller wants a specific index selected
// they can pass it in here. If the index is
// invalid then 0 is selected.
// BOOL bTypeInSecondCol - when TRUE the second column is filled with
// the type string instead of the program.
//
// Returns: HRESULT - standard COM error codes. Note that S_FALSE denotes that
// the function could not set the requested item index (iItemToSelect)
// as the selected item. Thus it set 0 as the selected item.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::AddCustomActionsToListView(HWND hListView, HINSTANCE hInstance, CustomActionTypes Type, BOOL bUseTunneling, int iItemToSelect, BOOL bTypeInSecondCol)
{
if ((NULL == hListView) || (-1 > Type) || (c_iNumCustomActionTypes < Type) || (!bUseTunneling && PRETUNNEL == Type))
{
return E_INVALIDARG;
}
HRESULT hr = S_OK;
LVITEM lvItem = {0};
TCHAR szTemp[MAX_PATH+1];
CustomActionListItem* pCurrent;
//
// Clear all of the items in the list view
//
MYVERIFY(FALSE != ListView_DeleteAllItems(hListView));
hr = EnsureActionTypeStringsLoaded(hInstance);
if (FAILED(hr))
{
CMASSERTMSG(FALSE, TEXT("CustomActionList::AddCustomActionsToListView -- Failed to load type strings."));
return E_UNEXPECTED;
}
//
// Figure out what type of items to add to the list view
//
int iStart;
int iEnd;
int iTotalCount = 0;
if (ALL == Type)
{
iStart = 0;
iEnd = c_iNumCustomActionTypes;
}
else
{
iStart = Type;
iEnd = iStart + 1;
}
//
// Load the built in string suffix just in case we have some built in actions to display
//
LPTSTR pszBuiltInSuffix = CmLoadString(hInstance, IDS_BUILT_IN); // if we have a NULL then just don't append anything
MYDBGASSERT(pszBuiltInSuffix);
//
// Now add the items
//
for (int i = iStart; i < iEnd; i++)
{
//
// Don't display PreTunnel actions unless we are tunneling
//
if (!bUseTunneling && (PRETUNNEL == i))
{
pCurrent = NULL;
}
else
{
pCurrent = m_CustomActionHash[i];
}
while(pCurrent)
{
//
// Add the initial item
//
LPTSTR pszDescription;
TCHAR szDescription[MAX_PATH+1];
if (pszBuiltInSuffix && pCurrent->bBuiltInAction)
{
lstrcpy(szDescription, pCurrent->szDescription);
lstrcat(szDescription, pszBuiltInSuffix);
pszDescription = szDescription;
}
else
{
pszDescription = pCurrent->szDescription;
}
lvItem.mask = LVIF_TEXT;
lvItem.pszText = pszDescription;
lvItem.iItem = iTotalCount;
lvItem.iSubItem = 0;
if (-1 == ListView_InsertItem(hListView, &lvItem))
{
hr = HRESULT_FROM_WIN32(GetLastError());
CMTRACE2(TEXT("CustomActionList::AddCustomActionsToListView -- unable to add %s, hr 0x%x"), pCurrent->szDescription, hr);
}
//
// Now add the type of the item
//
lvItem.iSubItem = 1;
if (bTypeInSecondCol)
{
lvItem.pszText = m_ActionTypeStrings[pCurrent->Type];
}
else
{
if (pCurrent->bIncludeBinary)
{
lvItem.pszText = CmStrrchr(pCurrent->szProgram, TEXT('\\'));
if (lvItem.pszText)
{
//
// Advance past the slash
//
lvItem.pszText = CharNext(lvItem.pszText);
}
else
{
//
// We couldn't take out the shortservicename\
// Instead of erroring, lets show them the whole string, better than nothing.
//
lvItem.pszText = pCurrent->szProgram;
}
}
else
{
lvItem.pszText = pCurrent->szProgram;
}
}
if (0 == ListView_SetItem(hListView, &lvItem))
{
hr = HRESULT_FROM_WIN32(GetLastError());
CMTRACE2(TEXT("CustomActionList::AddCustomActionsToListView -- unable to add type for %s, hr 0x%x"), pCurrent->szDescription, hr);
}
pCurrent = pCurrent->Next;
iTotalCount++;
}
}
CmFree(pszBuiltInSuffix);
//
// Now that we have added everything to the list, set the cursor selection to the
// desired item in the list, if we have any.
//
int iCurrentCount = ListView_GetItemCount(hListView);
if (iCurrentCount)
{
//
// If we have enough items to satisfy iItemToSelect, then
// select the first item in the list.
//
if (iCurrentCount < iItemToSelect)
{
hr = S_FALSE;
iItemToSelect = 0;
}
//
// Select the item
//
SetListViewSelection(hListView, iItemToSelect);
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::GetExistingActionData
//
// Synopsis: This function looks up an action of the given type and
// description and then duplicates the item into the provided pointer.
// The function returns an error if it cannot find the requested item.
//
// Arguments: HINSTANCE hInstance - instance handle for resources
// LPCTSTR pszDescription - description of the item to look up
// CustomActionTypes Type - type of the item to lookup
// CustomActionListItem** ppCustomAction - pointer to hold the
// returned item, note
// it is the user responsibility
// to free this item
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::GetExistingActionData(HINSTANCE hInstance, LPCTSTR pszDescription, CustomActionTypes Type, CustomActionListItem** ppCustomAction)
{
if ((NULL == pszDescription) || (TEXT('\0') == pszDescription[0]) || (NULL == ppCustomAction))
{
return E_INVALIDARG;
}
//
// Find the existing entry
//
CustomActionListItem* pCurrent = NULL;
CustomActionListItem* pFollower = NULL;
HRESULT hr = Find(hInstance, pszDescription, Type, &pCurrent, &pFollower);
if (SUCCEEDED(hr))
{
hr = DuplicateCustomActionListItem(pCurrent, ppCustomAction);
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::GetTypeFromTypeString
//
// Synopsis: This function takes the inputted type string and compares it
// against the type strings it has loaded to tell the caller the
// numerical value of the type.
//
//
// Arguments: HINSTANCE hInstance - instance handle used to load strings
// TCHAR* pszTypeString - type string that the caller is looking for
// the numerical type of.
// CustomActionTypes* pType - pointer to recieve the type on success
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::GetTypeFromTypeString(HINSTANCE hInstance, TCHAR* pszTypeString, CustomActionTypes* pType)
{
if (NULL == pszTypeString || NULL == pType)
{
return E_INVALIDARG;
}
HRESULT hr = EnsureActionTypeStringsLoaded(hInstance);
if (SUCCEEDED(hr))
{
hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
for (int i = 0; i < c_iNumCustomActionTypes; i++)
{
if (0 == lstrcmpi(m_ActionTypeStrings[i], pszTypeString))
{
hr = S_OK;
*pType = (CustomActionTypes)i;
}
}
//
// Check for all
//
if (FAILED(hr))
{
if (0 == lstrcmpi(m_pszAllTypeString, pszTypeString))
{
hr = S_OK;
*pType = (CustomActionTypes)i;
}
}
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::GetTypeStringFromType
//
// Synopsis: This function returns the type string of the given numerical
// type. Note that the returned string is an allocated string that
// is the caller's responsibility to free. The function will not
// return a NULL string if the function succeeds.
//
//
// Arguments: HINSTANCE hInstance - instance handle used to load strings
// TCHAR* pszTypeString - type string that the caller is looking for
// the numerical type of.
// CustomActionTypes* pType - pointer to recieve the type on success
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::GetTypeStringFromType(HINSTANCE hInstance, CustomActionTypes Type, TCHAR** ppszTypeString)
{
if (NULL == ppszTypeString || (-1 > Type) || (c_iNumCustomActionTypes <= Type))
{
return E_INVALIDARG;
}
HRESULT hr = EnsureActionTypeStringsLoaded(hInstance);
if (SUCCEEDED(hr))
{
if (ALL == Type)
{
*ppszTypeString = CmStrCpyAlloc(m_pszAllTypeString);
}
else
{
*ppszTypeString = CmStrCpyAlloc(m_ActionTypeStrings[Type]);
}
if (NULL == ppszTypeString)
{
hr = E_OUTOFMEMORY;
}
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::EnsureActionTypeStringsLoaded
//
// Synopsis: This function ensures that all of the action type strings have
// been loaded from string resources. If any of the action type
// strings are NULL the function will try to load them. If the
// any of the loads fail, the function fails. Thus the caller is
// gauranteed to have all of the type strings available for use
// if this function succeeds. The loaded strings are freed by the
// class destructor. If the CmLoadString call fails, the function
// will try to use a copy of the Action Section strings instead.
//
//
// Arguments: HINSTANCE hInstance - instance handle used to load strings
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::EnsureActionTypeStringsLoaded(HINSTANCE hInstance)
{
HRESULT hr = E_OUTOFMEMORY;
//
// First load the All type string
//
if (NULL == m_pszAllTypeString)
{
//
// LoadString the string we will display to the user in
// the action type combo box for the current type.
//
m_pszAllTypeString = CmLoadString(hInstance, IDS_ALLCONACT);
if (NULL == m_pszAllTypeString)
{
CMASSERTMSG(FALSE, TEXT("EnsureActionTypeStringsLoaded -- Failed to load a all action display string."));
//
// Special case the all string because we don't have a section string for it
//
m_pszAllTypeString = CmStrCpyAlloc(TEXT("All"));
if (NULL == m_pszAllTypeString)
{
goto exit;
}
}
}
//
// Load the rest of the type display strings
//
for (int i = 0; i < c_iNumCustomActionTypes; i++)
{
if (NULL == m_ActionTypeStrings[i])
{
//
// LoadString the string we will display to the user in
// the action type combo box for the current type.
//
m_ActionTypeStrings[i] = CmLoadString(hInstance, BASE_ACTION_STRING_ID + i);
if (NULL == m_ActionTypeStrings[i])
{
CMASSERTMSG(FALSE, TEXT("EnsureActionTypeStringsLoaded -- Failed to load a custom action type display string."));
//
// Try to use the section name instead of the localized version, if that fails then bail
//
m_ActionTypeStrings[i] = CmStrCpyAlloc(m_ActionSectionStrings[i]);
if (NULL == m_ActionTypeStrings[i])
{
goto exit;
}
}
}
}
//
// If we got this far everything should be peachy
//
hr = S_OK;
exit:
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::AddExecutionTypesToComboBox
//
// Synopsis: This function adds the execution type strings (Direct connections only,
// Dialup connections only, etc.) to the given combobox. Note that if
// tunneling is disabled then the combo box is disabled after being
// filled in. This is because this choice is only relevant to tunneling
// profiles.
//
// Arguments: HWND hDlg - window handle of the dialog containing the combo box
// UINT uCtrlId - combo box control ID
// HINSTANCE hInstance - instance handle for loading string resources
// BOOL bUseTunneling - is this a tunneling profile?
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::AddExecutionTypesToComboBox(HWND hDlg, UINT uCtrlId, HINSTANCE hInstance, BOOL bUseTunneling)
{
HRESULT hr = E_OUTOFMEMORY;
INT_PTR nResult;
//
// Clear the combo list
//
SendDlgItemMessage(hDlg, uCtrlId, CB_RESETCONTENT, 0, (LPARAM)0); //lint !e534 CB_RESETCONTENT doesn't return anything useful
//
// Load the of the execution display strings
//
for (int i = 0; i < c_iNumCustomActionExecutionStates; i++)
{
if (NULL == m_ExecutionStrings[i])
{
//
// LoadString the string we will display to the user in
// the execution combo box on the custom action popup dialog
//
m_ExecutionStrings[i] = CmLoadString(hInstance, BASE_EXECUTION_STRING_ID + i);
if (NULL == m_ExecutionStrings[i])
{
CMASSERTMSG(FALSE, TEXT("AddExecutionTypesToComboBox -- Failed to load a custom action execution display string."));
goto exit;
}
}
//
// Add the string to the combo box
//
SendDlgItemMessage(hDlg, uCtrlId, CB_ADDSTRING, 0, (LPARAM)m_ExecutionStrings[i]);
}
//
// Pick the first item in the list by default
//
nResult = SendDlgItemMessage(hDlg, uCtrlId, CB_GETCOUNT, (WPARAM)0, (LPARAM)0);
if ((CB_ERR != nResult) && (nResult > 0))
{
MYVERIFY(CB_ERR != SendDlgItemMessage(hDlg, uCtrlId, CB_SETCURSEL, (WPARAM)0, (LPARAM)0));
}
//
// If we aren't tunneling, then the control should be disabled since we only
// have one type of connection available to the user ... dialup connections.
// However, we will set the flags to 0 at this point, indicating connect for
// all connections (to fit in with legacy behavior).
//
if (!bUseTunneling)
{
EnableWindow(GetDlgItem(hDlg, uCtrlId), FALSE);
}
//
// If we got this far everything should be peachy
//
hr = S_OK;
exit:
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::FillInTempDescription
//
// Synopsis: This function creates the temporary description used for a custom
// action if the user didn't specify one. The temporary description
// is the Program concatenated with the displayed parameters string
// (namely the function name and the parameters together).
//
// Arguments: HWND hDlg - window handle of the dialog containing the combo box
// UINT uCtrlId - combo box control ID
// HINSTANCE hInstance - instance handle for loading string resources
// BOOL bUseTunneling - is this a tunneling profile?
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::FillInTempDescription(CustomActionListItem* pCustomAction)
{
MYDBGASSERT(pCustomAction);
MYDBGASSERT(TEXT('\0') == pCustomAction->szDescription[0]);
if ((NULL == pCustomAction) || (TEXT('\0') != pCustomAction->szDescription[0]))
{
return E_INVALIDARG;
}
TCHAR* pszFileName;
pCustomAction->bTempDescription = TRUE;
if (pCustomAction->bIncludeBinary)
{
//
// We want just the filename (not the entire path) associated with the
// item if the user is including the binary.
//
pszFileName = CmStrrchr(pCustomAction->szProgram, TEXT('\\'));
if (pszFileName)
{
pszFileName = CharNext(pszFileName);
}
else
{
pszFileName = pCustomAction->szProgram;
}
}
else
{
pszFileName = pCustomAction->szProgram;
}
lstrcpyn(pCustomAction->szDescription, pszFileName, CELEMS(pCustomAction->szDescription));
UINT uNumCharsLeftInDesc = CELEMS(pCustomAction->szDescription) - lstrlen(pCustomAction->szDescription);
LPTSTR pszCurrent = pCustomAction->szDescription + lstrlen(pCustomAction->szDescription);
if (pCustomAction->szFunctionName[0] && uNumCharsLeftInDesc)
{
//
// If we have space left in the description add a space and the function name next
//
*pszCurrent = TEXT(' ');
uNumCharsLeftInDesc--;
pszCurrent++;
lstrcpyn(pszCurrent, pCustomAction->szFunctionName, uNumCharsLeftInDesc);
pszCurrent = pCustomAction->szDescription + lstrlen(pCustomAction->szDescription);
uNumCharsLeftInDesc = (UINT)(CELEMS(pCustomAction->szDescription) - (pszCurrent - pCustomAction->szDescription) - 1);// one for the NULL char
}
if (pCustomAction->pszParameters && pCustomAction->pszParameters[0] && uNumCharsLeftInDesc)
{
*pszCurrent = TEXT(' ');
uNumCharsLeftInDesc--;
pszCurrent++;
lstrcpyn(pszCurrent, pCustomAction->pszParameters, uNumCharsLeftInDesc);
}
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::MapIndexToFlags
//
// Synopsis: This function gives the caller the Flags value for the given
// combobox index. Note that the flags value does not include
// the NonInteractive flag which is OR-ed on later.
//
// Arguments: int iIndex - combo index to retrieve the flags for
// DWORD* pdwFlags - DWORD pointer to receive the flags value
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::MapIndexToFlags(int iIndex, DWORD* pdwFlags)
{
if ((NULL == pdwFlags) || (c_iNumCustomActionExecutionStates <= iIndex) || (0 > iIndex))
{
return E_INVALIDARG;
}
*pdwFlags = (CustomActionExecutionStates)c_iExecutionIndexToFlagsMap[iIndex];
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::MapFlagsToIndex
//
// Synopsis: This function gives the caller the index value of the given flags
// value. Thus if you have a flags value, this function will tell you
// which combobox index to pick to get the string for that flags value.
//
// Arguments: DWORD dwFlags - flags value to lookup the index for
// int* piIndex - pointer to recieve the index value
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::MapFlagsToIndex(DWORD dwFlags, int* piIndex)
{
//
// Make sure the pointer to the index isn't NULL and that the flags are within bounds.
// Note that we know have the NonInteractive flag which we need to filter out of the mapping.
//
if ((NULL == piIndex) || (c_dwLargestExecutionState < (dwFlags & ~NONINTERACTIVE)))
{
return E_INVALIDARG;
}
//
// The flags are based on a bit mask. First look for all connections (since its
// zero) and then start looking for the most specific connection types first
// (direct/dialup only before all dialup/tunnel). Also note that we give precedent
// to tunnel connections and that we ingnore the NONINTERACTIVE flag except in the first
// if case.
//
DWORD dwArrayIndex;
if ((ALL_CONNECTIONS == dwFlags) || (NONINTERACTIVE == dwFlags))
{
dwArrayIndex = 0;
}
else if (dwFlags & DIRECT_ONLY)
{
dwArrayIndex = 1;
}
else if (dwFlags & DIALUP_ONLY)
{
dwArrayIndex = 3;
}
else if (dwFlags & ALL_TUNNEL)
{
dwArrayIndex = 4;
}
else if (dwFlags & ALL_DIALUP)
{
dwArrayIndex = 2;
}
else
{
return E_INVALIDARG;
}
*piIndex = c_iExecutionFlagsToIndexMap[dwArrayIndex];
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::GetListPositionAndBuiltInState
//
// Synopsis: This function searches for the item in question and returns to the
// caller whether the item has the following boolean properties:
// First in its custom action list
// Last in its custom action list
// A built in custom action
// Note that -1 (0xFFFFFFFF) is returned for a true value
// 0 for a false value
//
//
// Arguments: CustomActionListItem* pItem - item to look for (only desc and
// type are needed)
// int* piFirstInList - pointer to store whether this is the first
// item in the list or not
// int* piLastInList - pointer to store whether this is the last
// item in the list or not
// int* piIsBuiltIn - pointer to store whether this item is a built
// in custom action or not
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionList::GetListPositionAndBuiltInState(HINSTANCE hInstance, CustomActionListItem* pItem, int* piFirstInList,
int* piLastInList, int *piIsBuiltIn)
{
MYDBGASSERT(pItem);
MYDBGASSERT(piFirstInList);
MYDBGASSERT(piLastInList);
MYDBGASSERT(piIsBuiltIn);
if ((NULL == pItem) || (NULL == piFirstInList) || (NULL == piLastInList) || (NULL == piIsBuiltIn))
{
return E_INVALIDARG;
}
HRESULT hr;
CustomActionListItem* pCurrent = NULL;
CustomActionListItem* pFollower = NULL;
//
// Search for the item
//
hr = Find(hInstance, pItem->szDescription, pItem->Type, &pCurrent, &pFollower);
if (SUCCEEDED(hr))
{
*piFirstInList = (m_CustomActionHash[pItem->Type] == pCurrent) ? -1 : 0;
*piLastInList = (pCurrent && (NULL == pCurrent->Next)) ? -1 : 0;
*piIsBuiltIn = (pCurrent->bBuiltInAction) ? -1 : 0;
}
return hr;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionList::IsCmDl
//
// Synopsis: Checks to see if the passed in filename cmdl32.exe
//
// Arguments: LPTSTR szFileName - filename to check
//
// Returns: BOOL - returns TRUE if the dll is one of the cmdl dll's
//
// History: quintinb Created 11/24/97
//
//+----------------------------------------------------------------------------
BOOL CustomActionList::IsCmDl(CustomActionListItem* pItem)
{
MYDBGASSERT(pItem);
BOOL bRet = FALSE;
if (pItem && (ONCONNECT == pItem->Type))
{
LPTSTR pszFileName = CmStrrchr(pItem->szProgram, TEXT('\\'));
if (pszFileName)
{
pszFileName = CharNext(pszFileName);
}
else
{
pszFileName = pItem->szProgram;
}
if (0 == SafeCompareString(pszFileName, TEXT("cmdl32.exe")))
{
bRet = TRUE;
}
}
return bRet;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionListEnumerator::CustomActionListEnumerator
//
// Synopsis: Constructor for the CustomActionListEnumerator class. This function
// requires a CustomActionList to enumerate from.
//
// Arguments: CustomActionList* pActionListToWorkFrom - custom action list class
// to enumerate
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
CustomActionListEnumerator::CustomActionListEnumerator(CustomActionList* pActionListToWorkFrom)
{
MYDBGASSERT(pActionListToWorkFrom);
m_pActionList = pActionListToWorkFrom;
Reset();
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionListEnumerator::Reset
//
// Synopsis: Resets the CustomActionListEnumerator class. Thus the user can
// restart the enumeration by resetting the class.
//
// Arguments: None
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
void CustomActionListEnumerator::Reset()
{
m_iCurrentList = 0;
m_pCurrentListItem = NULL;
}
//+----------------------------------------------------------------------------
//
// Function: CustomActionListEnumerator::GetNextIncludedProgram
//
// Synopsis: This function is the work horse of the enumerator. It gets the
// next item in the enumeration with an included program. This
// enumerator is useful for getting all of the files that need to be
// included in the profile.
//
// Arguments: TCHAR* pszProgram - string buffer to hold the next program
// DWORD dwBufferSize - size of the passed in buffer
//
// Returns: HRESULT - standard COM error codes.
//
// History: quintinb Created Header 02/26/00
//
//+----------------------------------------------------------------------------
HRESULT CustomActionListEnumerator::GetNextIncludedProgram(TCHAR* pszProgram, DWORD dwBufferSize)
{
HRESULT hr = S_FALSE;
CustomActionListItem* pItem;
if (pszProgram && dwBufferSize)
{
if (m_pActionList)
{
while (m_iCurrentList < c_iNumCustomActionTypes)
{
if (m_pCurrentListItem)
{
//
// We are in the middle of an enumeration, use pCurrentProgramFileNameItem
// as the next item to examine.
//
pItem = m_pCurrentListItem;
}
else
{
//
// We are just starting or we have exhausted the current list
//
pItem = m_pActionList->m_CustomActionHash[m_iCurrentList];
}
while (pItem)
{
if (pItem->bIncludeBinary)
{
//
// We have the next item to pass back
//
lstrcpyn(pszProgram, pItem->szProgram, dwBufferSize);
//
// Next time we look for an item, start with the next in the list
//
m_pCurrentListItem = pItem->Next;
//
// If m_pCurrentListItem is NULL, we are at the end of the list now
// and we want to increment m_iCurrentList so that we start at the
// next list for the next item or terminate properly if we are
// on the last list
//
if (NULL == m_pCurrentListItem)
{
m_iCurrentList++;
}
hr = S_OK;
goto exit;
}
pItem = pItem->Next;
}
m_pCurrentListItem = NULL;
m_iCurrentList++;
}
}
else
{
hr = E_UNEXPECTED;
}
}
else
{
hr = E_INVALIDARG;
}
exit:
return hr;
}