//+---------------------------------------------------------------------------- // // 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 // // Include the shared custom action parsing code between CM and CMAK // #include "parseca.cpp" //+---------------------------------------------------------------------------- // // 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] && pItem->pszParameters) { // // Note that since we may or may not need a plus sign, a comma, or a // space, I use a little trick with wsprintf to make the logic simpler. // If an empty string ("") is passed into wsprintf for a %s then the // %s is replaced by nothing. Thus I can use szSpace to print a space // into the string or print nothing into the string by just giving it // a space in the first char or leaving it the null character ('\0'). // TCHAR szPlus[2] = {0}; TCHAR szComma[2] = {0}; TCHAR szSpace[2] = {0}; TCHAR szRelativePath[10] = {0}; TCHAR szName[MAX_PATH+1]; TCHAR szBuffer[2*MAX_PATH+1]; TCHAR szNum[MAX_PATH+1]; TCHAR* pszFileName; BOOL bLongName; // // Get just the filename of the program // GetFileName(pItem->szProgram, szName); // // If we are including the program in the CM package, then we have to // add the relative path from the directory where the cmp resides. // We also need to use just the filename itself, instead of the full path. // MYDBGASSERT(9 > lstrlen(pszShortServiceName)); if (pItem->bIncludeBinary && (9 > lstrlen(pszShortServiceName))) { wsprintf(szRelativePath, TEXT("%s\\"), pszShortServiceName); pszFileName = szName; bLongName = !IsFile8dot3(szName); } else { pszFileName = pItem->szProgram; // // Here we are more concerned if the item has spaces in it than if it is a long // file name. // LPTSTR pszSpace = CmStrchr(pszFileName, TEXT(' ')); bLongName = (NULL != pszSpace); } // // If this is a long filename, then surrond the filename with plus signs (+) // if (bLongName) { szPlus[0] = TEXT('+'); } // // If we have a dll, then deal with the dll function name // if (pItem->szFunctionName[0]) { szComma[0] = TEXT(','); } if (pItem->pszParameters[0]) { szSpace[0] = TEXT(' '); } // // Now build the string using wsprintf, notice that empty parameters ("") will write // nothing into the string // wsprintf(szBuffer, TEXT("%s%s%s%s%s%s%s%s"), szPlus, szRelativePath, pszFileName, szPlus, szComma, pItem->szFunctionName, szSpace, pItem->pszParameters); // // Now write the buffer string out to the cms file // MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, TEXT("%d"), iFileNum)); if (0 != WritePrivateProfileString(m_ActionSectionStrings[i], szNum, szBuffer, pszCmsFile)) { // // if dwFlags == 0 or bUseTunneling is FALSE 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. // LPTSTR pszFlagsValue = NULL; if (0 != pItem->dwFlags && bUseTunneling) { MYVERIFY(CELEMS(szBuffer) > (UINT)wsprintf(szBuffer, TEXT("%u"), pItem->dwFlags)); pszFlagsValue = szBuffer; } 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); } } else { CMASSERTMSG(FALSE, TEXT("WriteCustomActionsToCms -- custom action with empty program field!")); } pItem = pItem->Next; iFileNum++; } } } return hr; } */ 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 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. // LPTSTR pszFlagsValue = NULL; if (0 != pItem->dwFlags && bUseTunneling) { MYVERIFY(CELEMS(szName) > (UINT)wsprintf(szName, TEXT("%u"), pItem->dwFlags)); 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; 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; } 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 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. // // 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) { if ((NULL == piIndex) || (c_dwLargestExecutionState < dwFlags)) { 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. // DWORD dwHighestBitSet; if (ALL_CONNECTIONS == dwFlags) { dwHighestBitSet = 0; } else if (dwFlags & DIRECT_ONLY) { dwHighestBitSet = 1; } else if (dwFlags & DIALUP_ONLY) { dwHighestBitSet = 3; } else if (dwFlags & ALL_TUNNEL) { dwHighestBitSet = 4; } else if (dwFlags & ALL_DIALUP) { dwHighestBitSet = 2; } else { return E_INVALIDARG; } *piIndex = c_iExecutionFlagsToIndexMap[dwHighestBitSet]; 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 == lstrcmpi(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; }