Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2458 lines
85 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // File: customaction.cpp
  4. //
  5. // Module: CMAK.EXE
  6. //
  7. // Synopsis: Implemenation of the CustomActionList and CustomActionListEnumerator
  8. // classes used by CMAK to handle its custom actions.
  9. //
  10. // Copyright (c) 2000 Microsoft Corporation
  11. //
  12. // Author: quintinb Created 02/26/00
  13. //
  14. //+----------------------------------------------------------------------------
  15. #include <cmmaster.h>
  16. //
  17. // Include the shared custom action parsing code between CM and CMAK
  18. //
  19. #include "parseca.cpp"
  20. //+----------------------------------------------------------------------------
  21. //
  22. // Function: CustomActionList::CustomActionList
  23. //
  24. // Synopsis: Constructor for the CustomActionList class. Initializes the
  25. // m_ActionSectionStrings array with all of the section strings and
  26. // zeros all of the other parameters of the class.
  27. //
  28. // Arguments: None
  29. //
  30. // Returns: Nothing
  31. //
  32. // History: quintinb Created Header 02/26/00
  33. //
  34. //+----------------------------------------------------------------------------
  35. CustomActionList::CustomActionList()
  36. {
  37. //
  38. // First set the m_ActionSectionStrings so that we can read the actions
  39. // from the appropriate sections in the cms file.
  40. //
  41. m_ActionSectionStrings[PREINIT] = (TCHAR*)c_pszCmSectionPreInit;
  42. m_ActionSectionStrings[PRECONNECT] = (TCHAR*)c_pszCmSectionPreConnect;
  43. m_ActionSectionStrings[PREDIAL] = (TCHAR*)c_pszCmSectionPreDial;
  44. m_ActionSectionStrings[PRETUNNEL] = (TCHAR*)c_pszCmSectionPreTunnel;
  45. m_ActionSectionStrings[ONCONNECT] = (TCHAR*)c_pszCmSectionOnConnect;
  46. m_ActionSectionStrings[ONINTCONNECT] = (TCHAR*)c_pszCmSectionOnIntConnect;
  47. m_ActionSectionStrings[ONDISCONNECT] = (TCHAR*)c_pszCmSectionOnDisconnect;
  48. m_ActionSectionStrings[ONCANCEL] = (TCHAR*)c_pszCmSectionOnCancel;
  49. m_ActionSectionStrings[ONERROR] = (TCHAR*)c_pszCmSectionOnError;
  50. //
  51. // Zero m_CustomActionHash
  52. //
  53. ZeroMemory(&m_CustomActionHash, c_iNumCustomActionTypes*sizeof(CustomActionListItem*));
  54. //
  55. // Zero the Display strings array
  56. //
  57. ZeroMemory(&m_ActionTypeStrings, (c_iNumCustomActionTypes)*sizeof(TCHAR*));
  58. m_pszAllTypeString = NULL;
  59. //
  60. // Zero the Execution Strings
  61. //
  62. ZeroMemory(&m_ExecutionStrings, (c_iNumCustomActionExecutionStates)*sizeof(TCHAR*));
  63. }
  64. //+----------------------------------------------------------------------------
  65. //
  66. // Function: CustomActionList::~CustomActionList
  67. //
  68. // Synopsis: Destructor for the CustomActionList class. Frees all memory
  69. // allocated by the class including the CustomActionListItem
  70. // structures stored in the array of linked lists (the true data
  71. // of the class).
  72. //
  73. // Arguments: None
  74. //
  75. // Returns: Nothing
  76. //
  77. // History: quintinb Created Header 02/26/00
  78. //
  79. //+----------------------------------------------------------------------------
  80. CustomActionList::~CustomActionList()
  81. {
  82. //
  83. // Free the memory we allocated
  84. //
  85. for (int i = 0; i < c_iNumCustomActionTypes; i++)
  86. {
  87. //
  88. // Free each CustomAction List
  89. //
  90. CustomActionListItem* pCurrent = m_CustomActionHash[i];
  91. while (NULL != pCurrent)
  92. {
  93. CustomActionListItem* pNext = pCurrent->Next;
  94. CmFree(pCurrent->pszParameters);
  95. CmFree(pCurrent);
  96. pCurrent = pNext;
  97. }
  98. //
  99. // Free the Action Type display strings
  100. //
  101. CmFree(m_ActionTypeStrings[i]);
  102. }
  103. //
  104. // Free the All Action display string
  105. //
  106. CmFree(m_pszAllTypeString);
  107. //
  108. // Free the execution strings
  109. //
  110. for (int i = 0; i < c_iNumCustomActionExecutionStates; i++)
  111. {
  112. CmFree(m_ExecutionStrings[i]);
  113. }
  114. }
  115. //+----------------------------------------------------------------------------
  116. //
  117. // Function: CustomActionList::ReadCustomActionsFromCms
  118. //
  119. // Synopsis: Reads all custom actions in from the given cms file and stores
  120. // them in the classes custom action hash table by the type of
  121. // custom action. This function relies on ParseCustomActionString
  122. // to do the actual parsing of the custom action string. Given the
  123. // current architecture of CM this function should really only be
  124. // called once per class object as there is no way to reset the class object
  125. // (other than explicitly calling the destructor). However, there is
  126. // no code to prevent the caller from pulling connect actions from more than
  127. // one source. Thus let the caller beware.
  128. //
  129. // Arguments: HINSTANCE hInstance - instance handle to load string resources
  130. // TCHAR* pszCmsFile - full path to the cms file to get
  131. // the custom actions from
  132. // TCHAR* pszShortServiceName - short service name of the profile
  133. //
  134. // Returns: HRESULT - standard COM error codes
  135. //
  136. // History: quintinb Created Header 02/26/00
  137. //
  138. //+----------------------------------------------------------------------------
  139. HRESULT CustomActionList::ReadCustomActionsFromCms(HINSTANCE hInstance, TCHAR* pszCmsFile, TCHAR* pszShortServiceName)
  140. {
  141. MYDBGASSERT(hInstance);
  142. MYDBGASSERT(pszCmsFile);
  143. MYDBGASSERT(pszShortServiceName);
  144. if ((NULL == hInstance) || (NULL == pszCmsFile) || (NULL == pszShortServiceName))
  145. {
  146. return E_INVALIDARG;
  147. }
  148. HRESULT hr = S_OK;
  149. int iFileNum = 0;
  150. for (int i = 0; i < c_iNumCustomActionTypes; i++)
  151. {
  152. TCHAR szNum[MAX_PATH+1];
  153. LPTSTR pszTemp = NULL;
  154. CustomActionListItem CustomAction;
  155. iFileNum = 0;
  156. do
  157. {
  158. CmFree(pszTemp);
  159. MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, TEXT("%d"), iFileNum));
  160. pszTemp = GetPrivateProfileStringWithAlloc(m_ActionSectionStrings[i], szNum, TEXT(""), pszCmsFile);
  161. if (pszTemp)
  162. {
  163. MYDBGASSERT(pszTemp[0]);
  164. hr = ParseCustomActionString(pszTemp, &CustomAction, pszShortServiceName);
  165. if (SUCCEEDED(hr))
  166. {
  167. //
  168. // We have parsed the string, now we need to get the Flags and the description
  169. //
  170. CustomAction.Type = (CustomActionTypes)i;
  171. MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, c_pszCmEntryConactDesc, iFileNum));
  172. GetPrivateProfileString(m_ActionSectionStrings[i], szNum, TEXT(""), CustomAction.szDescription, CELEMS(CustomAction.szDescription), pszCmsFile); //lint !e534
  173. MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, c_pszCmEntryConactFlags, iFileNum));
  174. CustomAction.dwFlags = (DWORD)GetPrivateProfileInt(m_ActionSectionStrings[i], szNum, 0, pszCmsFile);
  175. hr = Add(hInstance, &CustomAction, pszShortServiceName);
  176. if (FAILED(hr))
  177. {
  178. CMASSERTMSG(FALSE, TEXT("CustomActionList::ReadCustomActionsFromCms -- Unable to add a custom action to the list, Add failed."));
  179. }
  180. CmFree(CustomAction.pszParameters);
  181. CustomAction.pszParameters = NULL;
  182. }
  183. else
  184. {
  185. CMTRACE2(TEXT("ReadCustomActionsFromCms -- Unable to parse %s, hr=%d"), pszTemp, hr);
  186. }
  187. }
  188. iFileNum++;
  189. } while(pszTemp);
  190. }
  191. return hr;
  192. }
  193. //+----------------------------------------------------------------------------
  194. //
  195. // Function: CustomActionList::ParseCustomActionString
  196. //
  197. // Synopsis: This function takes a custom action string retrieved from a
  198. // cms file and parses it into the various parts of a custom
  199. // action (program, parameters, function name, etc.)
  200. //
  201. // Arguments: LPTSTR pszStringToParse - custom action buffer to be parsed into
  202. // the various parts of a custom action
  203. // CustomActionListItem* pCustomAction - pointer to a custom action
  204. // structure to be filled in
  205. // TCHAR* pszShortServiceName - short service name of the profile
  206. //
  207. // Returns: HRESULT - standard COM error codes
  208. //
  209. // History: quintinb Created Header 02/26/00
  210. //
  211. //+----------------------------------------------------------------------------
  212. HRESULT CustomActionList::ParseCustomActionString(LPTSTR pszStringToParse, CustomActionListItem* pCustomAction, TCHAR* pszShortServiceName)
  213. {
  214. MYDBGASSERT(pszStringToParse);
  215. MYDBGASSERT(TEXT('\0') != pszStringToParse[0]);
  216. MYDBGASSERT(pCustomAction);
  217. MYDBGASSERT(pszShortServiceName);
  218. if ((NULL == pszStringToParse) || (TEXT('\0') == pszStringToParse[0]) ||
  219. (NULL == pCustomAction) || (NULL == pszShortServiceName))
  220. {
  221. return E_INVALIDARG;
  222. }
  223. //
  224. // Zero the CustomAction struct
  225. //
  226. ZeroMemory(pCustomAction, sizeof(CustomActionListItem));
  227. CmStrTrim(pszStringToParse);
  228. LPTSTR pszProgram = NULL;
  229. LPTSTR pszFunctionName = NULL;
  230. HRESULT hr = HrParseCustomActionString(pszStringToParse, &pszProgram,
  231. &(pCustomAction->pszParameters), &pszFunctionName);
  232. if (SUCCEEDED(hr))
  233. {
  234. lstrcpyn(pCustomAction->szProgram, pszProgram, CELEMS(pCustomAction->szProgram));
  235. lstrcpyn(pCustomAction->szFunctionName, pszFunctionName, CELEMS(pCustomAction->szFunctionName));
  236. //
  237. // Now we have the filename string, but we need to check to see if
  238. // it includes the relative path. If so, then we need to set
  239. // bIncludeBinary to TRUE;
  240. //
  241. TCHAR szTemp[MAX_PATH+1];
  242. if (MAX_PATH >= (lstrlen(g_szOsdir) + lstrlen(pCustomAction->szProgram)))
  243. {
  244. MYVERIFY(CELEMS(szTemp) > (UINT)wsprintf(szTemp, TEXT("%s%s"), g_szOsdir, pCustomAction->szProgram));
  245. pCustomAction->bIncludeBinary = FileExists(szTemp);
  246. }
  247. else
  248. {
  249. pCustomAction->bIncludeBinary = FALSE;
  250. }
  251. }
  252. CmFree(pszProgram);
  253. CmFree(pszFunctionName);
  254. return hr;
  255. }
  256. //+----------------------------------------------------------------------------
  257. //
  258. // Function: CustomActionList::WriteCustomActionsToCms
  259. //
  260. // Synopsis: This function takes a custom action string retrieved from a
  261. // cms file and parses it into the various parts of a custom
  262. // action (program, parameters, function name, etc.)
  263. //
  264. // Arguments: TCHAR* pszCmsFile - Cms file to write the custom action to
  265. // TCHAR* pszShortServiceName - short service name of the profile
  266. // BOOL bUseTunneling - whether this a tunneling profile or not,
  267. // controls whether Pre-Tunnel actions should be
  268. // written and whether the Flags parameter for
  269. // each action should be written (since they are
  270. // only needed if tunneling is an option).
  271. //
  272. // Returns: HRESULT - standard COM error codes
  273. //
  274. // History: quintinb Created Header 02/26/00
  275. //
  276. //+----------------------------------------------------------------------------
  277. /*
  278. HRESULT CustomActionList::WriteCustomActionsToCms(TCHAR* pszCmsFile, TCHAR* pszShortServiceName, BOOL bUseTunneling)
  279. {
  280. HRESULT hr = S_OK;
  281. for (int i = 0; i < c_iNumCustomActionTypes; i++)
  282. {
  283. //
  284. // Clear out the section
  285. //
  286. MYVERIFY(0 != WritePrivateProfileSection(m_ActionSectionStrings[i], TEXT("\0\0"), pszCmsFile));
  287. //
  288. // Make sure that we have a linked list of actions to process and that if we
  289. // are writing PRETUNNEL actions that we are actually tunneling.
  290. //
  291. if (m_CustomActionHash[i] && (i != PRETUNNEL || (i == PRETUNNEL && bUseTunneling)))
  292. {
  293. int iFileNum = 0;
  294. CustomActionListItem* pItem = m_CustomActionHash[i];
  295. while (pItem)
  296. {
  297. if (pItem->szProgram[0] && pItem->pszParameters)
  298. {
  299. //
  300. // Note that since we may or may not need a plus sign, a comma, or a
  301. // space, I use a little trick with wsprintf to make the logic simpler.
  302. // If an empty string ("") is passed into wsprintf for a %s then the
  303. // %s is replaced by nothing. Thus I can use szSpace to print a space
  304. // into the string or print nothing into the string by just giving it
  305. // a space in the first char or leaving it the null character ('\0').
  306. //
  307. TCHAR szPlus[2] = {0};
  308. TCHAR szComma[2] = {0};
  309. TCHAR szSpace[2] = {0};
  310. TCHAR szRelativePath[10] = {0};
  311. TCHAR szName[MAX_PATH+1];
  312. TCHAR szBuffer[2*MAX_PATH+1];
  313. TCHAR szNum[MAX_PATH+1];
  314. TCHAR* pszFileName;
  315. BOOL bLongName;
  316. //
  317. // Get just the filename of the program
  318. //
  319. GetFileName(pItem->szProgram, szName);
  320. //
  321. // If we are including the program in the CM package, then we have to
  322. // add the relative path from the directory where the cmp resides.
  323. // We also need to use just the filename itself, instead of the full path.
  324. //
  325. MYDBGASSERT(9 > lstrlen(pszShortServiceName));
  326. if (pItem->bIncludeBinary && (9 > lstrlen(pszShortServiceName)))
  327. {
  328. wsprintf(szRelativePath, TEXT("%s\\"), pszShortServiceName);
  329. pszFileName = szName;
  330. bLongName = !IsFile8dot3(szName);
  331. }
  332. else
  333. {
  334. pszFileName = pItem->szProgram;
  335. //
  336. // Here we are more concerned if the item has spaces in it than if it is a long
  337. // file name.
  338. //
  339. LPTSTR pszSpace = CmStrchr(pszFileName, TEXT(' '));
  340. bLongName = (NULL != pszSpace);
  341. }
  342. //
  343. // If this is a long filename, then surrond the filename with plus signs (+)
  344. //
  345. if (bLongName)
  346. {
  347. szPlus[0] = TEXT('+');
  348. }
  349. //
  350. // If we have a dll, then deal with the dll function name
  351. //
  352. if (pItem->szFunctionName[0])
  353. {
  354. szComma[0] = TEXT(',');
  355. }
  356. if (pItem->pszParameters[0])
  357. {
  358. szSpace[0] = TEXT(' ');
  359. }
  360. //
  361. // Now build the string using wsprintf, notice that empty parameters ("") will write
  362. // nothing into the string
  363. //
  364. wsprintf(szBuffer, TEXT("%s%s%s%s%s%s%s%s"), szPlus, szRelativePath, pszFileName, szPlus, szComma,
  365. pItem->szFunctionName, szSpace, pItem->pszParameters);
  366. //
  367. // Now write the buffer string out to the cms file
  368. //
  369. MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, TEXT("%d"), iFileNum));
  370. if (0 != WritePrivateProfileString(m_ActionSectionStrings[i], szNum, szBuffer, pszCmsFile))
  371. {
  372. //
  373. // if dwFlags == 0 or bUseTunneling is FALSE then delete the flags line instead
  374. // of setting it. We only need the flags to tell us when to run a connect action
  375. // if we have the option of tunneling.
  376. //
  377. LPTSTR pszFlagsValue = NULL;
  378. if (0 != pItem->dwFlags && bUseTunneling)
  379. {
  380. MYVERIFY(CELEMS(szBuffer) > (UINT)wsprintf(szBuffer, TEXT("%u"), pItem->dwFlags));
  381. pszFlagsValue = szBuffer;
  382. }
  383. MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, c_pszCmEntryConactFlags, iFileNum));
  384. if (0 == WritePrivateProfileString(m_ActionSectionStrings[i], szNum, pszFlagsValue, pszCmsFile))
  385. {
  386. hr = HRESULT_FROM_WIN32(GetLastError());
  387. CMTRACE1(TEXT("CustomActionList::WriteCustomActionsToCms -- unable to write flags, hr is 0x%x"), hr);
  388. }
  389. //
  390. // If description parameter is null or is only a temporary description, then delete the
  391. // description instead of writing it.
  392. //
  393. LPTSTR pszDescValue = NULL;
  394. if (pItem->szDescription[0] && !pItem->bTempDescription)
  395. {
  396. pszDescValue = pItem->szDescription;
  397. }
  398. MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, c_pszCmEntryConactDesc, iFileNum));
  399. if (0 == WritePrivateProfileString(m_ActionSectionStrings[i], szNum, pszDescValue, pszCmsFile))
  400. {
  401. hr = HRESULT_FROM_WIN32(GetLastError());
  402. CMTRACE1(TEXT("CustomActionList::WriteCustomActionsToCms -- unable to write description, hr is 0x%x"), hr);
  403. }
  404. }
  405. else
  406. {
  407. hr = HRESULT_FROM_WIN32(GetLastError());
  408. CMTRACE1(TEXT("CustomActionList::WriteCustomActionsToCms -- unable to write connect action, hr is 0x%x"), hr);
  409. }
  410. }
  411. else
  412. {
  413. CMASSERTMSG(FALSE, TEXT("WriteCustomActionsToCms -- custom action with empty program field!"));
  414. }
  415. pItem = pItem->Next;
  416. iFileNum++;
  417. }
  418. }
  419. }
  420. return hr;
  421. }
  422. */
  423. HRESULT CustomActionList::WriteCustomActionsToCms(TCHAR* pszCmsFile, TCHAR* pszShortServiceName, BOOL bUseTunneling)
  424. {
  425. HRESULT hr = S_OK;
  426. for (int i = 0; i < c_iNumCustomActionTypes; i++)
  427. {
  428. //
  429. // Clear out the section
  430. //
  431. MYVERIFY(0 != WritePrivateProfileSection(m_ActionSectionStrings[i], TEXT("\0\0"), pszCmsFile));
  432. //
  433. // Make sure that we have a linked list of actions to process and that if we
  434. // are writing PRETUNNEL actions that we are actually tunneling.
  435. //
  436. if (m_CustomActionHash[i] && (i != PRETUNNEL || (i == PRETUNNEL && bUseTunneling)))
  437. {
  438. int iFileNum = 0;
  439. CustomActionListItem* pItem = m_CustomActionHash[i];
  440. while (pItem)
  441. {
  442. if (pItem->szProgram[0])
  443. {
  444. //
  445. // Get just the filename of the program
  446. //
  447. TCHAR szName[MAX_PATH+1];
  448. if (pItem->bIncludeBinary)
  449. {
  450. wsprintf(szName, TEXT("%s\\%s"), pszShortServiceName, GetName(pItem->szProgram));
  451. }
  452. else
  453. {
  454. lstrcpyn(szName, pItem->szProgram, CELEMS(szName));
  455. }
  456. UINT uSizeNeeded = lstrlen(szName);
  457. LPTSTR pszSpace = CmStrchr(szName, TEXT(' '));
  458. BOOL bLongName = (NULL != pszSpace);
  459. if (bLongName)
  460. {
  461. uSizeNeeded = uSizeNeeded + 2; // for the two plus signs
  462. }
  463. if (pItem->szFunctionName[0])
  464. {
  465. uSizeNeeded = uSizeNeeded + lstrlen(pItem->szFunctionName) + 1;// add one for the comma
  466. }
  467. if (pItem->pszParameters && pItem->pszParameters[0])
  468. {
  469. uSizeNeeded = uSizeNeeded + lstrlen(pItem->pszParameters) + 1;// add one for the space
  470. }
  471. uSizeNeeded = (uSizeNeeded + 1) * sizeof(TCHAR);
  472. LPTSTR pszBuffer = (LPTSTR)CmMalloc(uSizeNeeded);
  473. if (pszBuffer)
  474. {
  475. if (bLongName)
  476. {
  477. pszBuffer[0] = TEXT('+');
  478. }
  479. lstrcat(pszBuffer, szName);
  480. if (bLongName)
  481. {
  482. lstrcat(pszBuffer, TEXT("+"));
  483. }
  484. if (pItem->szFunctionName[0])
  485. {
  486. lstrcat(pszBuffer, TEXT(","));
  487. lstrcat(pszBuffer, pItem->szFunctionName);
  488. }
  489. if (pItem->pszParameters && pItem->pszParameters[0])
  490. {
  491. lstrcat(pszBuffer, TEXT(" "));
  492. lstrcat(pszBuffer, pItem->pszParameters);
  493. }
  494. //
  495. // Now write the buffer string out to the cms file
  496. //
  497. TCHAR szNum[MAX_PATH+1];
  498. MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, TEXT("%d"), iFileNum));
  499. if (0 != WritePrivateProfileString(m_ActionSectionStrings[i], szNum, pszBuffer, pszCmsFile))
  500. {
  501. //
  502. // if dwFlags == 0 or bUseTunneling is FALSE then delete the flags line instead
  503. // of setting it. We only need the flags to tell us when to run a connect action
  504. // if we have the option of tunneling.
  505. //
  506. LPTSTR pszFlagsValue = NULL;
  507. if (0 != pItem->dwFlags && bUseTunneling)
  508. {
  509. MYVERIFY(CELEMS(szName) > (UINT)wsprintf(szName, TEXT("%u"), pItem->dwFlags));
  510. pszFlagsValue = szName;
  511. }
  512. MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, c_pszCmEntryConactFlags, iFileNum));
  513. if (0 == WritePrivateProfileString(m_ActionSectionStrings[i], szNum, pszFlagsValue, pszCmsFile))
  514. {
  515. hr = HRESULT_FROM_WIN32(GetLastError());
  516. CMTRACE1(TEXT("CustomActionList::WriteCustomActionsToCms -- unable to write flags, hr is 0x%x"), hr);
  517. }
  518. //
  519. // If description parameter is null or is only a temporary description, then delete the
  520. // description instead of writing it.
  521. //
  522. LPTSTR pszDescValue = NULL;
  523. if (pItem->szDescription[0] && !pItem->bTempDescription)
  524. {
  525. pszDescValue = pItem->szDescription;
  526. }
  527. MYVERIFY(CELEMS(szNum) > (UINT)wsprintf(szNum, c_pszCmEntryConactDesc, iFileNum));
  528. if (0 == WritePrivateProfileString(m_ActionSectionStrings[i], szNum, pszDescValue, pszCmsFile))
  529. {
  530. hr = HRESULT_FROM_WIN32(GetLastError());
  531. CMTRACE1(TEXT("CustomActionList::WriteCustomActionsToCms -- unable to write description, hr is 0x%x"), hr);
  532. }
  533. }
  534. else
  535. {
  536. hr = HRESULT_FROM_WIN32(GetLastError());
  537. CMTRACE1(TEXT("CustomActionList::WriteCustomActionsToCms -- unable to write connect action, hr is 0x%x"), hr);
  538. }
  539. CmFree(pszBuffer);
  540. }
  541. else
  542. {
  543. CMASSERTMSG(FALSE, TEXT("CustomActionList::WriteCustomActionsToCms -- Unable to allocate pszBuffer!"));
  544. }
  545. }
  546. else
  547. {
  548. CMASSERTMSG(FALSE, TEXT("WriteCustomActionsToCms -- custom action with empty program field!"));
  549. }
  550. pItem = pItem->Next;
  551. iFileNum++;
  552. }
  553. }
  554. }
  555. return hr;
  556. }
  557. //+----------------------------------------------------------------------------
  558. //
  559. // Function: CustomActionList::AddOrRemoveCmdl
  560. //
  561. // Synopsis: This function is designed ensure that the builtin custom action
  562. // cmdl is either in the custom action list or is removed from the
  563. // custom action list depending on the bAddCmdl flag. Thus if the
  564. // Flag is TRUE the connect action is added if it doesn't exist
  565. // already. If the bAddCmdl flag is FALSE then the custom action is
  566. // removed from the list. Also note that there is now two cmdl
  567. // variations that could exist in a profile. One for downloading
  568. // VPN updates and one for PBK updates. Thus we also have the
  569. // bForVpn flag that controls which version of the custom action
  570. // we are adding or removing.
  571. //
  572. // Arguments: HINSTANCE hInstance - instance handle to load string resources
  573. // BOOL bAddCmdl - whether cmdl should be added or deleted
  574. //
  575. // Returns: HRESULT - standard COM error codes
  576. //
  577. // History: quintinb Created Header 02/26/00
  578. //
  579. //+----------------------------------------------------------------------------
  580. HRESULT CustomActionList::AddOrRemoveCmdl(HINSTANCE hInstance, BOOL bAddCmdl, BOOL bForVpn)
  581. {
  582. HRESULT hr;
  583. CustomActionListItem* pItem = NULL;
  584. CustomActionListItem* pCurrent;
  585. CustomActionListItem* pFollower;
  586. if ((NULL == hInstance))
  587. {
  588. hr = E_INVALIDARG;
  589. goto exit;
  590. }
  591. //
  592. // cmdl32.exe
  593. //
  594. pItem = (CustomActionListItem*)CmMalloc(sizeof(CustomActionListItem));
  595. MYDBGASSERT(pItem);
  596. if (pItem)
  597. {
  598. UINT uDescId;
  599. if (bForVpn)
  600. {
  601. uDescId = IDS_CMDL_VPN_DESC;
  602. pItem->pszParameters = CmStrCpyAlloc(TEXT("/VPN %PROFILE%"));
  603. }
  604. else
  605. {
  606. uDescId = IDS_CMDL_DESC;
  607. pItem->pszParameters = CmStrCpyAlloc(TEXT("%PROFILE%"));
  608. }
  609. MYVERIFY(LoadString(hInstance, uDescId, pItem->szDescription, CELEMS(pItem->szDescription)));
  610. lstrcpy(pItem->szProgram, TEXT("cmdl32.exe"));
  611. pItem->Type = ONCONNECT;
  612. pItem->bBuiltInAction = TRUE;
  613. pItem->bTempDescription = TRUE;
  614. MYDBGASSERT(pItem->pszParameters);
  615. if (pItem->pszParameters)
  616. {
  617. hr = Find(hInstance, pItem->szDescription, pItem->Type, &pCurrent, &pFollower);
  618. if (FAILED(hr))
  619. {
  620. //
  621. // No cmdl32.exe. If bAddCmdl is TRUE then we need to add it, otherwise our job here is done.
  622. // If we are going to add it, lets make it the first in the list. The user can move it later
  623. // if they wish.
  624. //
  625. if (bAddCmdl)
  626. {
  627. pItem->Next = m_CustomActionHash[pItem->Type];
  628. m_CustomActionHash[pItem->Type] = pItem;
  629. pItem = NULL; // don't free pItem
  630. }
  631. hr = S_OK;
  632. }
  633. else
  634. {
  635. //
  636. // cmdl32.exe already exists and bAddCmdl is TRUE, nothing to do. If bAddCmdl is FALSE
  637. // and it already exists then we need to delete it.
  638. //
  639. if (bAddCmdl)
  640. {
  641. hr = S_FALSE;
  642. }
  643. else
  644. {
  645. hr = Delete(hInstance, pItem->szDescription, pItem->Type);
  646. }
  647. }
  648. }
  649. else
  650. {
  651. hr = E_OUTOFMEMORY;
  652. goto exit;
  653. }
  654. }
  655. else
  656. {
  657. hr = E_OUTOFMEMORY;
  658. goto exit;
  659. }
  660. exit:
  661. if (pItem)
  662. {
  663. CmFree(pItem->pszParameters);
  664. CmFree(pItem);
  665. }
  666. return hr;
  667. }
  668. HRESULT DuplicateCustomActionListItem(CustomActionListItem* pCustomAction, CustomActionListItem** ppNewItem)
  669. {
  670. HRESULT hr = S_OK;
  671. if (pCustomAction && ppNewItem)
  672. {
  673. *ppNewItem = (CustomActionListItem*)CmMalloc(sizeof(CustomActionListItem));
  674. if (*ppNewItem)
  675. {
  676. //
  677. // Duplicate the existing item
  678. //
  679. CopyMemory(*ppNewItem, pCustomAction, sizeof(CustomActionListItem));
  680. //
  681. // NULL out the Next pointer
  682. //
  683. (*ppNewItem)->Next = NULL;
  684. //
  685. // If we have a param string, that must also be duplicated since
  686. // it is an allocated string.
  687. //
  688. if (pCustomAction->pszParameters)
  689. {
  690. (*ppNewItem)->pszParameters = CmStrCpyAlloc(pCustomAction->pszParameters);
  691. if (NULL == (*ppNewItem)->pszParameters)
  692. {
  693. hr = E_OUTOFMEMORY;
  694. CmFree(*ppNewItem);
  695. *ppNewItem = NULL;
  696. }
  697. }
  698. }
  699. else
  700. {
  701. hr = E_OUTOFMEMORY;
  702. }
  703. }
  704. else
  705. {
  706. hr = E_INVALIDARG;
  707. *ppNewItem = NULL;
  708. CMASSERTMSG(FALSE, TEXT("DuplicateCustomActionListItem"));
  709. }
  710. return hr;
  711. }
  712. //+----------------------------------------------------------------------------
  713. //
  714. // Function: CustomActionList::Add
  715. //
  716. // Synopsis: This function adds the given custom action to the custom action
  717. // hash table. Note that add is for new items and returns an error
  718. // if an existing custom action of the same description and type
  719. // already exists. Also note that the CustomActionListItem passed in
  720. // is not just added to the hash table. Add creates its own memory
  721. // for the custom action objects and the caller should not expect
  722. // add to free the past in memory.
  723. //
  724. // Arguments: HINSTANCE hInstance - instance handle to load string resources
  725. // CustomActionListItem* pCustomAction - custom action structure to
  726. // add to the list of existing
  727. // custom actions
  728. //
  729. // Returns: HRESULT - standard COM error codes
  730. //
  731. // History: quintinb Created Header 02/26/00
  732. //
  733. //+----------------------------------------------------------------------------
  734. HRESULT CustomActionList::Add(HINSTANCE hInstance, CustomActionListItem* pCustomAction, LPCTSTR pszShortServiceName)
  735. {
  736. HRESULT hr = S_OK;
  737. MYDBGASSERT(hInstance);
  738. MYDBGASSERT(pCustomAction);
  739. MYDBGASSERT(pCustomAction->szProgram[0]);
  740. if ((NULL == hInstance) || (NULL == pCustomAction) || (TEXT('\0') == pCustomAction->szProgram[0]))
  741. {
  742. hr = E_INVALIDARG;
  743. goto exit;
  744. }
  745. //
  746. // First make sure that we have a description parameter because the description
  747. // and the Type uniquely identify a custom action
  748. //
  749. TCHAR szCmProxy[MAX_PATH+1];
  750. TCHAR szCmRoute[MAX_PATH+1];
  751. wsprintf(szCmRoute, TEXT("%s\\cmroute.dll"), pszShortServiceName);
  752. wsprintf(szCmProxy, TEXT("%s\\cmproxy.dll"), pszShortServiceName);
  753. if (TEXT('\0') == pCustomAction->szDescription[0])
  754. {
  755. if (IsCmDl(pCustomAction))
  756. {
  757. //
  758. // Cmdl32.exe as a post built-in custom action normally gets added through
  759. // AddOrRemoveCmdl. However, to allow the user to move the custom actions
  760. // around in the list, we want to add it here. Note, that we must distinguish
  761. // between the VPN download and the PBK download so that we get the description correct on each.
  762. //
  763. LPTSTR pszVpnSwitch = CmStrStr(pCustomAction->pszParameters, TEXT("/v"));
  764. UINT uDescStringId;
  765. if (NULL == pszVpnSwitch)
  766. {
  767. pszVpnSwitch = CmStrStr(pCustomAction->pszParameters, TEXT("/V"));
  768. }
  769. if (pszVpnSwitch)
  770. {
  771. uDescStringId = IDS_CMDL_VPN_DESC;
  772. }
  773. else
  774. {
  775. uDescStringId = IDS_CMDL_DESC;
  776. }
  777. pCustomAction->bBuiltInAction = TRUE;
  778. pCustomAction->bTempDescription = TRUE;
  779. MYVERIFY(LoadString(hInstance, uDescStringId, pCustomAction->szDescription, CELEMS(pCustomAction->szDescription)));
  780. }
  781. else
  782. {
  783. hr = FillInTempDescription(pCustomAction);
  784. MYDBGASSERT(SUCCEEDED(hr));
  785. }
  786. }
  787. else if (0 == lstrcmpi(pCustomAction->szProgram, szCmProxy))
  788. {
  789. if (ONCONNECT == pCustomAction->Type)
  790. {
  791. pCustomAction->bBuiltInAction = TRUE;
  792. MYVERIFY(LoadString(hInstance, IDS_CMPROXY_CON_DESC, pCustomAction->szDescription, CELEMS(pCustomAction->szDescription)));
  793. }
  794. else if (ONDISCONNECT == pCustomAction->Type)
  795. {
  796. pCustomAction->bBuiltInAction = TRUE;
  797. MYVERIFY(LoadString(hInstance, IDS_CMPROXY_DIS_DESC, pCustomAction->szDescription, CELEMS(pCustomAction->szDescription)));
  798. }
  799. }
  800. else if (0 == lstrcmpi(pCustomAction->szProgram, szCmRoute))
  801. {
  802. if (ONCONNECT == pCustomAction->Type)
  803. {
  804. pCustomAction->bBuiltInAction = TRUE;
  805. MYVERIFY(LoadString(hInstance, IDS_CMROUTE_DESC, pCustomAction->szDescription, CELEMS(pCustomAction->szDescription)));
  806. }
  807. }
  808. //
  809. // First figure out if we already have a list of connect actions for
  810. // the type specified. If not, then create one.
  811. //
  812. if (NULL == m_CustomActionHash[pCustomAction->Type])
  813. {
  814. hr = DuplicateCustomActionListItem(pCustomAction, &(m_CustomActionHash[pCustomAction->Type]));
  815. goto exit;
  816. }
  817. else
  818. {
  819. CustomActionListItem* pCurrent = NULL;
  820. CustomActionListItem* pFollower = NULL;
  821. //
  822. // Search for an existing record with the same description. If one exists return
  823. // an error that it already exists.
  824. //
  825. hr = Find(hInstance, pCustomAction->szDescription, pCustomAction->Type, &pCurrent, &pFollower);
  826. if (SUCCEEDED(hr))
  827. {
  828. hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
  829. goto exit;
  830. }
  831. //
  832. // If we got here, then we have a list but we don't have a matching entry. Thus
  833. // we must add a new entry to the end of the list
  834. //
  835. if (pFollower && (NULL == pFollower->Next))
  836. {
  837. hr = DuplicateCustomActionListItem(pCustomAction, &(pFollower->Next));
  838. goto exit;
  839. }
  840. else
  841. {
  842. CMASSERTMSG(FALSE, TEXT("CustomActionList::Add -- couldn't find place to add the new element!"));
  843. hr = E_UNEXPECTED;
  844. goto exit;
  845. }
  846. }
  847. exit:
  848. return hr;
  849. }
  850. //+----------------------------------------------------------------------------
  851. //
  852. // Function: CustomActionList::Edit
  853. //
  854. // Synopsis: This function is used to edit an existing action. The function
  855. // tries to replace the old action with the new one, keeping the
  856. // same place in the respective custom action list. However, since
  857. // the new item could be of a different type than the old item, this
  858. // isn't always possible. When the item changes type, it is deleted
  859. // from the old list and appended to the new custom action type list.
  860. // Also note, that when the caller is attempting to rename or re-type
  861. // an item, the function checks for collisions with existing items
  862. // of that name/type. If the caller tries to rename an item
  863. // to the same name/type as another existing item then the function returns
  864. // an error.
  865. //
  866. // Arguments: HINSTANCE hInstance - instance handle to load string resources
  867. // CustomActionListItem* pOldCustomAction - a custom action struct
  868. // containing at least the
  869. // description and type of
  870. // the item that is to be
  871. // editted.
  872. // CustomActionListItem* pNewCustomAction - the new data for the
  873. // custom action.
  874. //
  875. // Returns: HRESULT - standard COM error codes
  876. //
  877. // History: quintinb Created Header 02/26/00
  878. //
  879. //+----------------------------------------------------------------------------
  880. HRESULT CustomActionList::Edit(HINSTANCE hInstance, CustomActionListItem* pOldCustomAction, CustomActionListItem* pNewCustomAction, LPCTSTR pszShortServiceName)
  881. {
  882. MYDBGASSERT(hInstance);
  883. MYDBGASSERT(pOldCustomAction);
  884. MYDBGASSERT(pNewCustomAction);
  885. MYDBGASSERT(pNewCustomAction->szDescription[0]);
  886. MYDBGASSERT(pOldCustomAction->szDescription[0]);
  887. if ((NULL == hInstance) || (NULL == pOldCustomAction) || (NULL == pNewCustomAction) ||
  888. (TEXT('\0') == pOldCustomAction->szDescription[0]) || (TEXT('\0') == pNewCustomAction->szDescription[0]))
  889. {
  890. return E_INVALIDARG;
  891. }
  892. //
  893. // First try to find the old custom action
  894. //
  895. CustomActionListItem* pTemp = NULL;
  896. CustomActionListItem* pTempFollower = NULL;
  897. CustomActionListItem* pExistingItem = NULL;
  898. CustomActionListItem* pFollower = NULL;
  899. CustomActionListItem** ppPointerToFillIn = NULL;
  900. HRESULT hr = Find (hInstance, pOldCustomAction->szDescription, pOldCustomAction->Type, &pExistingItem, &pFollower);
  901. if (SUCCEEDED(hr))
  902. {
  903. //
  904. // Okay, we found the old custom action. If the type and desc are the same between the two actions,
  905. // then all we need to do is copy over the data and be done with it. However, if the user changed
  906. // the type or description then we need to double check that an action with the description and type
  907. // of the new action doesn't already exist (editting action XYZ of type Post-Connect
  908. // into action XYZ of type Pre-Connect when there already exists XYZ of type Pre-Connect).
  909. //
  910. if ((pOldCustomAction->Type == pNewCustomAction->Type) &&
  911. (0 == lstrcmpi(pExistingItem->szDescription, pNewCustomAction->szDescription)))
  912. {
  913. if (NULL == pFollower)
  914. {
  915. ppPointerToFillIn = &(m_CustomActionHash[pNewCustomAction->Type]);
  916. }
  917. else
  918. {
  919. ppPointerToFillIn = &(pFollower->Next);
  920. }
  921. hr = DuplicateCustomActionListItem(pNewCustomAction, ppPointerToFillIn);
  922. if (SUCCEEDED(hr))
  923. {
  924. (*ppPointerToFillIn)->Next = pExistingItem->Next;
  925. CmFree(pExistingItem->pszParameters);
  926. CmFree(pExistingItem);
  927. pExistingItem = NULL;
  928. }
  929. }
  930. else
  931. {
  932. hr = Find (hInstance, pNewCustomAction->szDescription, pNewCustomAction->Type, &pTemp, &pTempFollower);
  933. if (SUCCEEDED(hr))
  934. {
  935. //
  936. // If the caller really wants to do this, then have them delete the old custom action
  937. // and then call edit with the new custom action as both old and new.
  938. //
  939. hr = HRESULT_FROM_WIN32(ERROR_FILE_EXISTS);
  940. }
  941. else
  942. {
  943. //
  944. // If the types are different then it needs to go on a different sub list. If
  945. // only the name is different then we just need to copy it over.
  946. //
  947. if(pOldCustomAction->Type != pNewCustomAction->Type)
  948. {
  949. //
  950. // Delete the old action of type X
  951. //
  952. hr = Delete(hInstance, pOldCustomAction->szDescription, pOldCustomAction->Type);
  953. MYDBGASSERT(SUCCEEDED(hr));
  954. //
  955. // Add the new action of type Y
  956. //
  957. if (SUCCEEDED(hr))
  958. {
  959. hr = Add(hInstance, pNewCustomAction, pszShortServiceName);
  960. MYDBGASSERT(SUCCEEDED(hr));
  961. }
  962. }
  963. else
  964. {
  965. if (NULL == pFollower)
  966. {
  967. ppPointerToFillIn = &(m_CustomActionHash[pNewCustomAction->Type]);
  968. }
  969. else
  970. {
  971. ppPointerToFillIn = &(pFollower->Next);
  972. }
  973. hr = DuplicateCustomActionListItem(pNewCustomAction, ppPointerToFillIn);
  974. if (SUCCEEDED(hr))
  975. {
  976. (*ppPointerToFillIn)->Next = pExistingItem->Next;
  977. CmFree(pExistingItem->pszParameters);
  978. CmFree(pExistingItem);
  979. pExistingItem = NULL;
  980. }
  981. }
  982. }
  983. }
  984. }
  985. else
  986. {
  987. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  988. }
  989. return hr;
  990. }
  991. //+----------------------------------------------------------------------------
  992. //
  993. // Function: CustomActionList::Find
  994. //
  995. // Synopsis: This function searches the array of linked lists for an item with
  996. // the given type and description. If it finds the the item it returns
  997. // successfully and fills in the ppItem and ppFollower pointers with
  998. // pointers to the item itself and the item before the requested item,
  999. // respectively. If the item is the first item in the list, then
  1000. // *ppFollower will be NULL. Note that this function is internal to the
  1001. // class because it returns pointers to the classes internal data.
  1002. // Also note, that if we have a list, but don't find the desired item
  1003. // then *ppFollower returns the last item in the list. This is desired
  1004. // behavior since it allows Add to use *ppFollower to directly add a new
  1005. // item to the list.
  1006. //
  1007. // Arguments: HINSTANCE hInstance - instance handle for resources
  1008. // LPCTSTR pszDescription - description of the item to look for
  1009. // CustomActionTypes Type - type of the item to look for
  1010. // CustomActionListItem** ppItem - an OUT param that is filled in with
  1011. // a pointer to the item on a successful find
  1012. // CustomActionListItem** ppFollower - an OUT param that is filled in with
  1013. // a pointer to the item before the
  1014. // item in the list on a successful find
  1015. // (note that this is useful since it
  1016. // is a singly linked list). This
  1017. // param will be NULL if the item is the
  1018. // first item in the list on a successful
  1019. // find.
  1020. //
  1021. //
  1022. // Returns: HRESULT - standard COM error codes
  1023. //
  1024. // History: quintinb Created Header 02/26/00
  1025. //
  1026. //+----------------------------------------------------------------------------
  1027. HRESULT CustomActionList::Find(HINSTANCE hInstance, LPCTSTR pszDescription, CustomActionTypes Type, CustomActionListItem** ppItem, CustomActionListItem** ppFollower)
  1028. {
  1029. if ((NULL == hInstance) || (NULL == pszDescription) || (TEXT('\0') == pszDescription[0]) || (NULL == ppItem) || (NULL == ppFollower))
  1030. {
  1031. return E_INVALIDARG;
  1032. }
  1033. HRESULT hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1034. CustomActionListItem* pCurrent = m_CustomActionHash[Type];
  1035. TCHAR szDescWithBuiltInSuffix[MAX_PATH+1];
  1036. *ppFollower = NULL;
  1037. *ppItem = NULL;
  1038. LPTSTR pszBuiltInSuffix = CmLoadString(hInstance, IDS_BUILT_IN); // if we got a NULL pointer then just don't do the extra compare
  1039. MYDBGASSERT(pszBuiltInSuffix);
  1040. //
  1041. // Search the list to find the item
  1042. //
  1043. while (pCurrent)
  1044. {
  1045. if (0 == lstrcmpi(pCurrent->szDescription, pszDescription))
  1046. {
  1047. //
  1048. // We found the item
  1049. //
  1050. *ppItem = pCurrent;
  1051. hr = S_OK;
  1052. break;
  1053. }
  1054. else if (pszBuiltInSuffix && pCurrent->bBuiltInAction)
  1055. {
  1056. //
  1057. // This is a built in action, lets try adding the builtin string to the description
  1058. // and try the comparision again
  1059. //
  1060. wsprintf(szDescWithBuiltInSuffix, TEXT("%s%s"), pCurrent->szDescription, pszBuiltInSuffix);
  1061. if (0 == lstrcmpi(szDescWithBuiltInSuffix, pszDescription))
  1062. {
  1063. *ppItem = pCurrent;
  1064. hr = S_OK;
  1065. break;
  1066. }
  1067. else
  1068. {
  1069. *ppFollower = pCurrent;
  1070. pCurrent = pCurrent->Next;
  1071. }
  1072. }
  1073. else
  1074. {
  1075. *ppFollower = pCurrent;
  1076. pCurrent = pCurrent->Next;
  1077. }
  1078. }
  1079. CmFree(pszBuiltInSuffix);
  1080. return hr;
  1081. }
  1082. //+----------------------------------------------------------------------------
  1083. //
  1084. // Function: CustomActionList::Delete
  1085. //
  1086. // Synopsis: This function searches through the array of custom action lists
  1087. // to find an item with the given description and type. If it finds
  1088. // the item it deletes it from the list. If the item cannot be found
  1089. // an error is returned.
  1090. //
  1091. // Arguments: TCHAR* pszDescription - description of the item to look for
  1092. // CustomActionTypes Type - type of the item to look for
  1093. //
  1094. // Returns: HRESULT - standard COM error codes
  1095. //
  1096. // History: quintinb Created Header 02/26/00
  1097. //
  1098. //+----------------------------------------------------------------------------
  1099. HRESULT CustomActionList::Delete(HINSTANCE hInstance, TCHAR* pszDescription, CustomActionTypes Type)
  1100. {
  1101. HRESULT hr = S_OK;
  1102. if ((NULL == pszDescription) || (TEXT('\0') == pszDescription[0]))
  1103. {
  1104. return E_INVALIDARG;
  1105. }
  1106. CustomActionListItem* pCurrent = NULL;
  1107. CustomActionListItem* pFollower = NULL;
  1108. hr = Find(hInstance, pszDescription, Type, &pCurrent, &pFollower);
  1109. if (SUCCEEDED(hr))
  1110. {
  1111. //
  1112. // We found the item to delete
  1113. //
  1114. if (pFollower)
  1115. {
  1116. pFollower->Next = pCurrent->Next;
  1117. }
  1118. else
  1119. {
  1120. //
  1121. // It is the first item in the list
  1122. //
  1123. m_CustomActionHash[Type] = pCurrent->Next;
  1124. }
  1125. CmFree(pCurrent->pszParameters);
  1126. CmFree(pCurrent);
  1127. }
  1128. return hr;
  1129. }
  1130. //+----------------------------------------------------------------------------
  1131. //
  1132. // Function: CustomActionList::MoveUp
  1133. //
  1134. // Synopsis: Moves the custom action specified by the given description and type
  1135. // up one place in the linked list for the given type. Note that if
  1136. // the custom action is already at the top of its list, we return
  1137. // S_FALSE;
  1138. //
  1139. // Arguments: TCHAR* pszDescription - description of the custom action to move
  1140. // CustomActionTypes Type - type of the custom action to move
  1141. //
  1142. // Returns: HRESULT - standard COM error codes. Note that S_FALSE denotes that
  1143. // MoveUp succeeded but that the item was already at the
  1144. // head of its list and thus couldn't be moved.
  1145. //
  1146. // History: quintinb Created Header 02/26/00
  1147. //
  1148. //+----------------------------------------------------------------------------
  1149. HRESULT CustomActionList::MoveUp(HINSTANCE hInstance, TCHAR* pszDescription, CustomActionTypes Type)
  1150. {
  1151. if ((NULL == pszDescription) || (TEXT('\0') == pszDescription[0]))
  1152. {
  1153. return E_INVALIDARG;
  1154. }
  1155. HRESULT hr = E_UNEXPECTED;
  1156. CustomActionListItem* pCurrent = NULL;
  1157. CustomActionListItem* pFollower = NULL;
  1158. CustomActionListItem* pBeforeFollower = NULL;
  1159. hr = Find(hInstance, pszDescription, Type, &pCurrent, &pFollower);
  1160. if (SUCCEEDED(hr))
  1161. {
  1162. //
  1163. // We found the item to move up
  1164. //
  1165. if (pFollower)
  1166. {
  1167. //
  1168. // Now Find the item in front of pFollower
  1169. //
  1170. hr = Find(hInstance, pFollower->szDescription, pFollower->Type, &pFollower, &pBeforeFollower);
  1171. if (SUCCEEDED(hr))
  1172. {
  1173. if (pBeforeFollower)
  1174. {
  1175. pBeforeFollower->Next = pCurrent;
  1176. }
  1177. else
  1178. {
  1179. //
  1180. // pFollower is first in the list
  1181. //
  1182. m_CustomActionHash[Type] = pCurrent;
  1183. }
  1184. pFollower->Next = pCurrent->Next;
  1185. pCurrent->Next = pFollower;
  1186. hr = S_OK;
  1187. }
  1188. }
  1189. else
  1190. {
  1191. //
  1192. // It is the first item in the list, we cannot move it up
  1193. //
  1194. hr = S_FALSE;
  1195. }
  1196. }
  1197. return hr;
  1198. }
  1199. //+----------------------------------------------------------------------------
  1200. //
  1201. // Function: CustomActionList::MoveDown
  1202. //
  1203. // Synopsis: Moves the custom action specified by the given description and type
  1204. // down one place in the linked list for the given type. Note that if
  1205. // the custom action is already at the bottom of its list, we return
  1206. // S_FALSE;
  1207. //
  1208. // Arguments: TCHAR* pszDescription - description of the custom action to move
  1209. // CustomActionTypes Type - type of the custom action to move
  1210. //
  1211. // Returns: HRESULT - standard COM error codes. Note that S_FALSE denotes that
  1212. // MoveDown succeeded but that the item was already at the
  1213. // tail of its list and thus couldn't be moved.
  1214. //
  1215. // History: quintinb Created Header 02/26/00
  1216. //
  1217. //+----------------------------------------------------------------------------
  1218. HRESULT CustomActionList::MoveDown(HINSTANCE hInstance, TCHAR* pszDescription, CustomActionTypes Type)
  1219. {
  1220. if ((NULL == pszDescription) || (TEXT('\0') == pszDescription[0]))
  1221. {
  1222. return E_INVALIDARG;
  1223. }
  1224. HRESULT hr = E_UNEXPECTED;
  1225. CustomActionListItem* pCurrent = NULL;
  1226. CustomActionListItem* pFollower = NULL;
  1227. hr = Find(hInstance, pszDescription, Type, &pCurrent, &pFollower);
  1228. if (SUCCEEDED(hr))
  1229. {
  1230. //
  1231. // We found the item to move down
  1232. //
  1233. if (NULL == pCurrent->Next)
  1234. {
  1235. //
  1236. // The item is already last in its list
  1237. //
  1238. hr = S_FALSE;
  1239. }
  1240. else if (pFollower)
  1241. {
  1242. pFollower->Next = pCurrent->Next;
  1243. pCurrent->Next = pFollower->Next->Next;
  1244. pFollower->Next->Next = pCurrent;
  1245. }
  1246. else
  1247. {
  1248. //
  1249. // Then the item is first in the list
  1250. //
  1251. m_CustomActionHash[Type] = pCurrent->Next;
  1252. pCurrent->Next = m_CustomActionHash[Type]->Next;
  1253. m_CustomActionHash[Type]->Next = pCurrent;
  1254. }
  1255. }
  1256. return hr;
  1257. }
  1258. //+----------------------------------------------------------------------------
  1259. //
  1260. // Function: CustomActionList::AddCustomActionTypesToComboBox
  1261. //
  1262. // Synopsis: This function adds the custom action type strings (Pre-Connect,
  1263. // Post-Connect, etc.) to the given combo box. Note that whether
  1264. // tunneling is enabled or not and whether the All string is asked for
  1265. // or not affects the strings added to the combo.
  1266. //
  1267. // Arguments: HWND hDlg - Window handle of the dialog that contains the combobox
  1268. // UINT uCtrlId - combo box control ID to add the strings too
  1269. // HINSTANCE hInstance - instance handle used to load resource strings
  1270. // BOOL bUseTunneling - is this a tunneling profile?
  1271. // BOOL bAddAll - should we include the <All> selection in the list?
  1272. //
  1273. // Returns: HRESULT - standard COM error codes
  1274. //
  1275. // History: quintinb Created Header 02/26/00
  1276. //
  1277. //+----------------------------------------------------------------------------
  1278. HRESULT CustomActionList::AddCustomActionTypesToComboBox(HWND hDlg, UINT uCtrlId, HINSTANCE hInstance, BOOL bUseTunneling, BOOL bAddAll)
  1279. {
  1280. if ((0 == hDlg) || (0 == uCtrlId))
  1281. {
  1282. return E_INVALIDARG;
  1283. }
  1284. HRESULT hr = S_OK;
  1285. //
  1286. // Clear the combo list
  1287. //
  1288. SendDlgItemMessage(hDlg, uCtrlId, CB_RESETCONTENT, 0, (LPARAM)0); //lint !e534 CB_RESETCONTENT doesn't return anything useful
  1289. //
  1290. // Ensure the type strings are loaded
  1291. //
  1292. hr = EnsureActionTypeStringsLoaded(hInstance);
  1293. if (SUCCEEDED(hr))
  1294. {
  1295. //
  1296. // Setup the all display string, if needed
  1297. //
  1298. if (bAddAll)
  1299. {
  1300. SendDlgItemMessage(hDlg, uCtrlId, CB_ADDSTRING, 0, (LPARAM)m_pszAllTypeString);
  1301. }
  1302. //
  1303. // Setup the rest of the display strings
  1304. //
  1305. for (int i = 0; i < c_iNumCustomActionTypes; i++)
  1306. {
  1307. //
  1308. // Don't Add the PreTunnel String unless we are tunneling
  1309. //
  1310. if (i != PRETUNNEL || (i == PRETUNNEL && bUseTunneling))
  1311. {
  1312. SendDlgItemMessage(hDlg, uCtrlId, CB_ADDSTRING, 0, (LPARAM)m_ActionTypeStrings[i]);
  1313. }
  1314. }
  1315. }
  1316. else
  1317. {
  1318. CMASSERTMSG(FALSE, TEXT("CustomActionList::AddCustomActionTypesToComboBox -- Failed to load type strings"));
  1319. }
  1320. return hr;
  1321. }
  1322. //+----------------------------------------------------------------------------
  1323. //
  1324. // Function: CustomActionList::AddCustomActionsToListView
  1325. //
  1326. // Synopsis: This function adds actions of the given type to the given
  1327. // list view control. After adding the actions it sets the
  1328. // selection mark and highlight to the given value (defaulting
  1329. // to the first item in the list).
  1330. //
  1331. // Arguments: HWND hListView - window handle of the list view control
  1332. // HINSTANCE hInstance - instance handle of the exe, used for resources
  1333. // CustomActionTypes Type - type of custom action to add to the list
  1334. // view control, see the CustomActionTypes
  1335. // definition for more info
  1336. // BOOL bUseTunneling - whether the tunneling is enabled or not for
  1337. // the current profile. Determines whether
  1338. // PreTunnel actions should be shown in the
  1339. // ALL action view (and raises an error if
  1340. // PreTunnel is specified but FALSE is passed).
  1341. // int iItemToSelect - after the items are added to the list, the
  1342. // selection mark is set. This defaults to 0, but
  1343. // if the caller wants a specific index selected
  1344. // they can pass it in here. If the index is
  1345. // invalid then 0 is selected.
  1346. // BOOL bTypeInSecondCol - when TRUE the second column is filled with
  1347. // the type string instead of the program.
  1348. //
  1349. // Returns: HRESULT - standard COM error codes. Note that S_FALSE denotes that
  1350. // the function could not set the requested item index (iItemToSelect)
  1351. // as the selected item. Thus it set 0 as the selected item.
  1352. //
  1353. // History: quintinb Created Header 02/26/00
  1354. //
  1355. //+----------------------------------------------------------------------------
  1356. HRESULT CustomActionList::AddCustomActionsToListView(HWND hListView, HINSTANCE hInstance, CustomActionTypes Type, BOOL bUseTunneling, int iItemToSelect, BOOL bTypeInSecondCol)
  1357. {
  1358. if ((NULL == hListView) || (-1 > Type) || (c_iNumCustomActionTypes < Type) || (!bUseTunneling && PRETUNNEL == Type))
  1359. {
  1360. return E_INVALIDARG;
  1361. }
  1362. HRESULT hr = S_OK;
  1363. LVITEM lvItem = {0};
  1364. TCHAR szTemp[MAX_PATH+1];
  1365. CustomActionListItem* pCurrent;
  1366. //
  1367. // Clear all of the items in the list view
  1368. //
  1369. MYVERIFY(FALSE != ListView_DeleteAllItems(hListView));
  1370. hr = EnsureActionTypeStringsLoaded(hInstance);
  1371. if (FAILED(hr))
  1372. {
  1373. CMASSERTMSG(FALSE, TEXT("CustomActionList::AddCustomActionsToListView -- Failed to load type strings."));
  1374. return E_UNEXPECTED;
  1375. }
  1376. //
  1377. // Figure out what type of items to add to the list view
  1378. //
  1379. int iStart;
  1380. int iEnd;
  1381. int iTotalCount = 0;
  1382. if (ALL == Type)
  1383. {
  1384. iStart = 0;
  1385. iEnd = c_iNumCustomActionTypes;
  1386. }
  1387. else
  1388. {
  1389. iStart = Type;
  1390. iEnd = iStart + 1;
  1391. }
  1392. //
  1393. // Load the built in string suffix just in case we have some built in actions to display
  1394. //
  1395. LPTSTR pszBuiltInSuffix = CmLoadString(hInstance, IDS_BUILT_IN); // if we have a NULL then just don't append anything
  1396. MYDBGASSERT(pszBuiltInSuffix);
  1397. //
  1398. // Now add the items
  1399. //
  1400. for (int i = iStart; i < iEnd; i++)
  1401. {
  1402. //
  1403. // Don't display PreTunnel actions unless we are tunneling
  1404. //
  1405. if (!bUseTunneling && (PRETUNNEL == i))
  1406. {
  1407. pCurrent = NULL;
  1408. }
  1409. else
  1410. {
  1411. pCurrent = m_CustomActionHash[i];
  1412. }
  1413. while(pCurrent)
  1414. {
  1415. //
  1416. // Add the initial item
  1417. //
  1418. LPTSTR pszDescription;
  1419. TCHAR szDescription[MAX_PATH+1];
  1420. if (pszBuiltInSuffix && pCurrent->bBuiltInAction)
  1421. {
  1422. lstrcpy(szDescription, pCurrent->szDescription);
  1423. lstrcat(szDescription, pszBuiltInSuffix);
  1424. pszDescription = szDescription;
  1425. }
  1426. else
  1427. {
  1428. pszDescription = pCurrent->szDescription;
  1429. }
  1430. lvItem.mask = LVIF_TEXT;
  1431. lvItem.pszText = pszDescription;
  1432. lvItem.iItem = iTotalCount;
  1433. lvItem.iSubItem = 0;
  1434. if (-1 == ListView_InsertItem(hListView, &lvItem))
  1435. {
  1436. hr = HRESULT_FROM_WIN32(GetLastError());
  1437. CMTRACE2(TEXT("CustomActionList::AddCustomActionsToListView -- unable to add %s, hr 0x%x"), pCurrent->szDescription, hr);
  1438. }
  1439. //
  1440. // Now add the type of the item
  1441. //
  1442. lvItem.iSubItem = 1;
  1443. if (bTypeInSecondCol)
  1444. {
  1445. lvItem.pszText = m_ActionTypeStrings[pCurrent->Type];
  1446. }
  1447. else
  1448. {
  1449. if (pCurrent->bIncludeBinary)
  1450. {
  1451. lvItem.pszText = CmStrrchr(pCurrent->szProgram, TEXT('\\'));
  1452. if (lvItem.pszText)
  1453. {
  1454. //
  1455. // Advance past the slash
  1456. //
  1457. lvItem.pszText = CharNext(lvItem.pszText);
  1458. }
  1459. else
  1460. {
  1461. //
  1462. // We couldn't take out the shortservicename\
  1463. // Instead of erroring, lets show them the whole string, better than nothing.
  1464. //
  1465. lvItem.pszText = pCurrent->szProgram;
  1466. }
  1467. }
  1468. else
  1469. {
  1470. lvItem.pszText = pCurrent->szProgram;
  1471. }
  1472. }
  1473. if (0 == ListView_SetItem(hListView, &lvItem))
  1474. {
  1475. hr = HRESULT_FROM_WIN32(GetLastError());
  1476. CMTRACE2(TEXT("CustomActionList::AddCustomActionsToListView -- unable to add type for %s, hr 0x%x"), pCurrent->szDescription, hr);
  1477. }
  1478. pCurrent = pCurrent->Next;
  1479. iTotalCount++;
  1480. }
  1481. }
  1482. CmFree(pszBuiltInSuffix);
  1483. //
  1484. // Now that we have added everything to the list, set the cursor selection to the
  1485. // desired item in the list, if we have any.
  1486. //
  1487. int iCurrentCount = ListView_GetItemCount(hListView);
  1488. if (iCurrentCount)
  1489. {
  1490. //
  1491. // If we have enough items to satisfy iItemToSelect, then
  1492. // select the first item in the list.
  1493. //
  1494. if (iCurrentCount < iItemToSelect)
  1495. {
  1496. hr = S_FALSE;
  1497. iItemToSelect = 0;
  1498. }
  1499. //
  1500. // Select the item
  1501. //
  1502. SetListViewSelection(hListView, iItemToSelect);
  1503. }
  1504. return hr;
  1505. }
  1506. //+----------------------------------------------------------------------------
  1507. //
  1508. // Function: CustomActionList::GetExistingActionData
  1509. //
  1510. // Synopsis: This function looks up an action of the given type and
  1511. // description and then duplicates the item into the provided pointer.
  1512. // The function returns an error if it cannot find the requested item.
  1513. //
  1514. // Arguments: HINSTANCE hInstance - instance handle for resources
  1515. // LPCTSTR pszDescription - description of the item to look up
  1516. // CustomActionTypes Type - type of the item to lookup
  1517. // CustomActionListItem** ppCustomAction - pointer to hold the
  1518. // returned item, note
  1519. // it is the user responsibility
  1520. // to free this item
  1521. //
  1522. // Returns: HRESULT - standard COM error codes.
  1523. //
  1524. // History: quintinb Created Header 02/26/00
  1525. //
  1526. //+----------------------------------------------------------------------------
  1527. HRESULT CustomActionList::GetExistingActionData(HINSTANCE hInstance, LPCTSTR pszDescription, CustomActionTypes Type, CustomActionListItem** ppCustomAction)
  1528. {
  1529. if ((NULL == pszDescription) || (TEXT('\0') == pszDescription[0]) || (NULL == ppCustomAction))
  1530. {
  1531. return E_INVALIDARG;
  1532. }
  1533. //
  1534. // Find the existing entry
  1535. //
  1536. CustomActionListItem* pCurrent = NULL;
  1537. CustomActionListItem* pFollower = NULL;
  1538. HRESULT hr = Find(hInstance, pszDescription, Type, &pCurrent, &pFollower);
  1539. if (SUCCEEDED(hr))
  1540. {
  1541. hr = DuplicateCustomActionListItem(pCurrent, ppCustomAction);
  1542. }
  1543. return hr;
  1544. }
  1545. //+----------------------------------------------------------------------------
  1546. //
  1547. // Function: CustomActionList::GetTypeFromTypeString
  1548. //
  1549. // Synopsis: This function takes the inputted type string and compares it
  1550. // against the type strings it has loaded to tell the caller the
  1551. // numerical value of the type.
  1552. //
  1553. //
  1554. // Arguments: HINSTANCE hInstance - instance handle used to load strings
  1555. // TCHAR* pszTypeString - type string that the caller is looking for
  1556. // the numerical type of.
  1557. // CustomActionTypes* pType - pointer to recieve the type on success
  1558. //
  1559. // Returns: HRESULT - standard COM error codes.
  1560. //
  1561. // History: quintinb Created Header 02/26/00
  1562. //
  1563. //+----------------------------------------------------------------------------
  1564. HRESULT CustomActionList::GetTypeFromTypeString(HINSTANCE hInstance, TCHAR* pszTypeString, CustomActionTypes* pType)
  1565. {
  1566. if (NULL == pszTypeString || NULL == pType)
  1567. {
  1568. return E_INVALIDARG;
  1569. }
  1570. HRESULT hr = EnsureActionTypeStringsLoaded(hInstance);
  1571. if (SUCCEEDED(hr))
  1572. {
  1573. hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND);
  1574. for (int i = 0; i < c_iNumCustomActionTypes; i++)
  1575. {
  1576. if (0 == lstrcmpi(m_ActionTypeStrings[i], pszTypeString))
  1577. {
  1578. hr = S_OK;
  1579. *pType = (CustomActionTypes)i;
  1580. }
  1581. }
  1582. //
  1583. // Check for all
  1584. //
  1585. if (FAILED(hr))
  1586. {
  1587. if (0 == lstrcmpi(m_pszAllTypeString, pszTypeString))
  1588. {
  1589. hr = S_OK;
  1590. *pType = (CustomActionTypes)i;
  1591. }
  1592. }
  1593. }
  1594. return hr;
  1595. }
  1596. //+----------------------------------------------------------------------------
  1597. //
  1598. // Function: CustomActionList::GetTypeStringFromType
  1599. //
  1600. // Synopsis: This function returns the type string of the given numerical
  1601. // type. Note that the returned string is an allocated string that
  1602. // is the caller's responsibility to free. The function will not
  1603. // return a NULL string if the function succeeds.
  1604. //
  1605. //
  1606. // Arguments: HINSTANCE hInstance - instance handle used to load strings
  1607. // TCHAR* pszTypeString - type string that the caller is looking for
  1608. // the numerical type of.
  1609. // CustomActionTypes* pType - pointer to recieve the type on success
  1610. //
  1611. // Returns: HRESULT - standard COM error codes.
  1612. //
  1613. // History: quintinb Created Header 02/26/00
  1614. //
  1615. //+----------------------------------------------------------------------------
  1616. HRESULT CustomActionList::GetTypeStringFromType(HINSTANCE hInstance, CustomActionTypes Type, TCHAR** ppszTypeString)
  1617. {
  1618. if (NULL == ppszTypeString || (-1 > Type) || (c_iNumCustomActionTypes <= Type))
  1619. {
  1620. return E_INVALIDARG;
  1621. }
  1622. HRESULT hr = EnsureActionTypeStringsLoaded(hInstance);
  1623. if (SUCCEEDED(hr))
  1624. {
  1625. if (ALL == Type)
  1626. {
  1627. *ppszTypeString = CmStrCpyAlloc(m_pszAllTypeString);
  1628. }
  1629. else
  1630. {
  1631. *ppszTypeString = CmStrCpyAlloc(m_ActionTypeStrings[Type]);
  1632. }
  1633. if (NULL == ppszTypeString)
  1634. {
  1635. hr = E_OUTOFMEMORY;
  1636. }
  1637. }
  1638. return hr;
  1639. }
  1640. //+----------------------------------------------------------------------------
  1641. //
  1642. // Function: CustomActionList::EnsureActionTypeStringsLoaded
  1643. //
  1644. // Synopsis: This function ensures that all of the action type strings have
  1645. // been loaded from string resources. If any of the action type
  1646. // strings are NULL the function will try to load them. If the
  1647. // any of the loads fail, the function fails. Thus the caller is
  1648. // gauranteed to have all of the type strings available for use
  1649. // if this function succeeds. The loaded strings are freed by the
  1650. // class destructor. If the CmLoadString call fails, the function
  1651. // will try to use a copy of the Action Section strings instead.
  1652. //
  1653. //
  1654. // Arguments: HINSTANCE hInstance - instance handle used to load strings
  1655. //
  1656. // Returns: HRESULT - standard COM error codes.
  1657. //
  1658. // History: quintinb Created Header 02/26/00
  1659. //
  1660. //+----------------------------------------------------------------------------
  1661. HRESULT CustomActionList::EnsureActionTypeStringsLoaded(HINSTANCE hInstance)
  1662. {
  1663. HRESULT hr = E_OUTOFMEMORY;
  1664. //
  1665. // First load the All type string
  1666. //
  1667. if (NULL == m_pszAllTypeString)
  1668. {
  1669. //
  1670. // LoadString the string we will display to the user in
  1671. // the action type combo box for the current type.
  1672. //
  1673. m_pszAllTypeString = CmLoadString(hInstance, IDS_ALLCONACT);
  1674. if (NULL == m_pszAllTypeString)
  1675. {
  1676. CMASSERTMSG(FALSE, TEXT("EnsureActionTypeStringsLoaded -- Failed to load a all action display string."));
  1677. //
  1678. // Special case the all string because we don't have a section string for it
  1679. //
  1680. m_pszAllTypeString = CmStrCpyAlloc(TEXT("All"));
  1681. if (NULL == m_pszAllTypeString)
  1682. {
  1683. goto exit;
  1684. }
  1685. }
  1686. }
  1687. //
  1688. // Load the rest of the type display strings
  1689. //
  1690. for (int i = 0; i < c_iNumCustomActionTypes; i++)
  1691. {
  1692. if (NULL == m_ActionTypeStrings[i])
  1693. {
  1694. //
  1695. // LoadString the string we will display to the user in
  1696. // the action type combo box for the current type.
  1697. //
  1698. m_ActionTypeStrings[i] = CmLoadString(hInstance, BASE_ACTION_STRING_ID + i);
  1699. if (NULL == m_ActionTypeStrings[i])
  1700. {
  1701. CMASSERTMSG(FALSE, TEXT("EnsureActionTypeStringsLoaded -- Failed to load a custom action type display string."));
  1702. //
  1703. // Try to use the section name instead of the localized version, if that fails then bail
  1704. //
  1705. m_ActionTypeStrings[i] = CmStrCpyAlloc(m_ActionSectionStrings[i]);
  1706. if (NULL == m_ActionTypeStrings[i])
  1707. {
  1708. goto exit;
  1709. }
  1710. }
  1711. }
  1712. }
  1713. //
  1714. // If we got this far everything should be peachy
  1715. //
  1716. hr = S_OK;
  1717. exit:
  1718. return hr;
  1719. }
  1720. //+----------------------------------------------------------------------------
  1721. //
  1722. // Function: CustomActionList::AddExecutionTypesToComboBox
  1723. //
  1724. // Synopsis: This function adds the execution type strings (Direct connections only,
  1725. // Dialup connections only, etc.) to the given combobox. Note that if
  1726. // tunneling is disabled then the combo box is disabled after being
  1727. // filled in. This is because this choice is only relevant to tunneling
  1728. // profiles.
  1729. //
  1730. // Arguments: HWND hDlg - window handle of the dialog containing the combo box
  1731. // UINT uCtrlId - combo box control ID
  1732. // HINSTANCE hInstance - instance handle for loading string resources
  1733. // BOOL bUseTunneling - is this a tunneling profile?
  1734. //
  1735. // Returns: HRESULT - standard COM error codes.
  1736. //
  1737. // History: quintinb Created Header 02/26/00
  1738. //
  1739. //+----------------------------------------------------------------------------
  1740. HRESULT CustomActionList::AddExecutionTypesToComboBox(HWND hDlg, UINT uCtrlId, HINSTANCE hInstance, BOOL bUseTunneling)
  1741. {
  1742. HRESULT hr = E_OUTOFMEMORY;
  1743. INT_PTR nResult;
  1744. //
  1745. // Clear the combo list
  1746. //
  1747. SendDlgItemMessage(hDlg, uCtrlId, CB_RESETCONTENT, 0, (LPARAM)0); //lint !e534 CB_RESETCONTENT doesn't return anything useful
  1748. //
  1749. // Load the of the execution display strings
  1750. //
  1751. for (int i = 0; i < c_iNumCustomActionExecutionStates; i++)
  1752. {
  1753. if (NULL == m_ExecutionStrings[i])
  1754. {
  1755. //
  1756. // LoadString the string we will display to the user in
  1757. // the execution combo box on the custom action popup dialog
  1758. //
  1759. m_ExecutionStrings[i] = CmLoadString(hInstance, BASE_EXECUTION_STRING_ID + i);
  1760. if (NULL == m_ExecutionStrings[i])
  1761. {
  1762. CMASSERTMSG(FALSE, TEXT("AddExecutionTypesToComboBox -- Failed to load a custom action execution display string."));
  1763. goto exit;
  1764. }
  1765. }
  1766. //
  1767. // Add the string to the combo box
  1768. //
  1769. SendDlgItemMessage(hDlg, uCtrlId, CB_ADDSTRING, 0, (LPARAM)m_ExecutionStrings[i]);
  1770. }
  1771. //
  1772. // Pick the first item in the list by default
  1773. //
  1774. nResult = SendDlgItemMessage(hDlg, uCtrlId, CB_GETCOUNT, (WPARAM)0, (LPARAM)0);
  1775. if ((CB_ERR != nResult) && (nResult > 0))
  1776. {
  1777. MYVERIFY(CB_ERR != SendDlgItemMessage(hDlg, uCtrlId, CB_SETCURSEL, (WPARAM)0, (LPARAM)0));
  1778. }
  1779. //
  1780. // If we aren't tunneling, then the control should be disabled since we only
  1781. // have one type of connection available to the user ... dialup connections.
  1782. // However, we will set the flags to 0 at this point, indicating connect for
  1783. // all connections (to fit in with legacy behavior).
  1784. //
  1785. if (!bUseTunneling)
  1786. {
  1787. EnableWindow(GetDlgItem(hDlg, uCtrlId), FALSE);
  1788. }
  1789. //
  1790. // If we got this far everything should be peachy
  1791. //
  1792. hr = S_OK;
  1793. exit:
  1794. return hr;
  1795. }
  1796. //+----------------------------------------------------------------------------
  1797. //
  1798. // Function: CustomActionList::FillInTempDescription
  1799. //
  1800. // Synopsis: This function creates the temporary description used for a custom
  1801. // action if the user didn't specify one. The temporary description
  1802. // is the Program concatenated with the displayed parameters string
  1803. // (namely the function name and the parameters together).
  1804. //
  1805. // Arguments: HWND hDlg - window handle of the dialog containing the combo box
  1806. // UINT uCtrlId - combo box control ID
  1807. // HINSTANCE hInstance - instance handle for loading string resources
  1808. // BOOL bUseTunneling - is this a tunneling profile?
  1809. //
  1810. // Returns: HRESULT - standard COM error codes.
  1811. //
  1812. // History: quintinb Created Header 02/26/00
  1813. //
  1814. //+----------------------------------------------------------------------------
  1815. HRESULT CustomActionList::FillInTempDescription(CustomActionListItem* pCustomAction)
  1816. {
  1817. MYDBGASSERT(pCustomAction);
  1818. MYDBGASSERT(TEXT('\0') == pCustomAction->szDescription[0]);
  1819. if ((NULL == pCustomAction) || (TEXT('\0') != pCustomAction->szDescription[0]))
  1820. {
  1821. return E_INVALIDARG;
  1822. }
  1823. TCHAR* pszFileName;
  1824. pCustomAction->bTempDescription = TRUE;
  1825. if (pCustomAction->bIncludeBinary)
  1826. {
  1827. //
  1828. // We want just the filename (not the entire path) associated with the
  1829. // item if the user is including the binary.
  1830. //
  1831. pszFileName = CmStrrchr(pCustomAction->szProgram, TEXT('\\'));
  1832. if (pszFileName)
  1833. {
  1834. pszFileName = CharNext(pszFileName);
  1835. }
  1836. else
  1837. {
  1838. pszFileName = pCustomAction->szProgram;
  1839. }
  1840. }
  1841. else
  1842. {
  1843. pszFileName = pCustomAction->szProgram;
  1844. }
  1845. lstrcpyn(pCustomAction->szDescription, pszFileName, CELEMS(pCustomAction->szDescription));
  1846. UINT uNumCharsLeftInDesc = CELEMS(pCustomAction->szDescription) - lstrlen(pCustomAction->szDescription);
  1847. LPTSTR pszCurrent = pCustomAction->szDescription + lstrlen(pCustomAction->szDescription);
  1848. if (pCustomAction->szFunctionName[0] && uNumCharsLeftInDesc)
  1849. {
  1850. //
  1851. // If we have space left in the description add a space and the function name next
  1852. //
  1853. *pszCurrent = TEXT(' ');
  1854. uNumCharsLeftInDesc--;
  1855. pszCurrent++;
  1856. lstrcpyn(pszCurrent, pCustomAction->szFunctionName, uNumCharsLeftInDesc);
  1857. pszCurrent = pCustomAction->szDescription + lstrlen(pCustomAction->szDescription);
  1858. uNumCharsLeftInDesc = (UINT)(CELEMS(pCustomAction->szDescription) - (pszCurrent - pCustomAction->szDescription) - 1);// one for the NULL char
  1859. }
  1860. if (pCustomAction->pszParameters && pCustomAction->pszParameters[0] && uNumCharsLeftInDesc)
  1861. {
  1862. *pszCurrent = TEXT(' ');
  1863. uNumCharsLeftInDesc--;
  1864. pszCurrent++;
  1865. lstrcpyn(pszCurrent, pCustomAction->pszParameters, uNumCharsLeftInDesc);
  1866. }
  1867. return S_OK;
  1868. }
  1869. //+----------------------------------------------------------------------------
  1870. //
  1871. // Function: CustomActionList::MapIndexToFlags
  1872. //
  1873. // Synopsis: This function gives the caller the Flags value for the given
  1874. // combobox index.
  1875. //
  1876. // Arguments: int iIndex - combo index to retrieve the flags for
  1877. // DWORD* pdwFlags - DWORD pointer to receive the flags value
  1878. //
  1879. // Returns: HRESULT - standard COM error codes.
  1880. //
  1881. // History: quintinb Created Header 02/26/00
  1882. //
  1883. //+----------------------------------------------------------------------------
  1884. HRESULT CustomActionList::MapIndexToFlags(int iIndex, DWORD* pdwFlags)
  1885. {
  1886. if ((NULL == pdwFlags) || (c_iNumCustomActionExecutionStates <= iIndex) || (0 > iIndex))
  1887. {
  1888. return E_INVALIDARG;
  1889. }
  1890. *pdwFlags = (CustomActionExecutionStates)c_iExecutionIndexToFlagsMap[iIndex];
  1891. return S_OK;
  1892. }
  1893. //+----------------------------------------------------------------------------
  1894. //
  1895. // Function: CustomActionList::MapFlagsToIndex
  1896. //
  1897. // Synopsis: This function gives the caller the index value of the given flags
  1898. // value. Thus if you have a flags value, this function will tell you
  1899. // which combobox index to pick to get the string for that flags value.
  1900. //
  1901. // Arguments: DWORD dwFlags - flags value to lookup the index for
  1902. // int* piIndex - pointer to recieve the index value
  1903. //
  1904. // Returns: HRESULT - standard COM error codes.
  1905. //
  1906. // History: quintinb Created Header 02/26/00
  1907. //
  1908. //+----------------------------------------------------------------------------
  1909. HRESULT CustomActionList::MapFlagsToIndex(DWORD dwFlags, int* piIndex)
  1910. {
  1911. if ((NULL == piIndex) || (c_dwLargestExecutionState < dwFlags))
  1912. {
  1913. return E_INVALIDARG;
  1914. }
  1915. //
  1916. // The flags are based on a bit mask. First look for all connections (since its
  1917. // zero) and then start looking for the most specific connection types first
  1918. // (direct/dialup only before all dialup/tunnel). Also note that we give precedent
  1919. // to tunnel connections.
  1920. //
  1921. DWORD dwHighestBitSet;
  1922. if (ALL_CONNECTIONS == dwFlags)
  1923. {
  1924. dwHighestBitSet = 0;
  1925. }
  1926. else if (dwFlags & DIRECT_ONLY)
  1927. {
  1928. dwHighestBitSet = 1;
  1929. }
  1930. else if (dwFlags & DIALUP_ONLY)
  1931. {
  1932. dwHighestBitSet = 3;
  1933. }
  1934. else if (dwFlags & ALL_TUNNEL)
  1935. {
  1936. dwHighestBitSet = 4;
  1937. }
  1938. else if (dwFlags & ALL_DIALUP)
  1939. {
  1940. dwHighestBitSet = 2;
  1941. }
  1942. else
  1943. {
  1944. return E_INVALIDARG;
  1945. }
  1946. *piIndex = c_iExecutionFlagsToIndexMap[dwHighestBitSet];
  1947. return S_OK;
  1948. }
  1949. //+----------------------------------------------------------------------------
  1950. //
  1951. // Function: CustomActionList::GetListPositionAndBuiltInState
  1952. //
  1953. // Synopsis: This function searches for the item in question and returns to the
  1954. // caller whether the item has the following boolean properties:
  1955. // First in its custom action list
  1956. // Last in its custom action list
  1957. // A built in custom action
  1958. // Note that -1 (0xFFFFFFFF) is returned for a true value
  1959. // 0 for a false value
  1960. //
  1961. //
  1962. // Arguments: CustomActionListItem* pItem - item to look for (only desc and
  1963. // type are needed)
  1964. // int* piFirstInList - pointer to store whether this is the first
  1965. // item in the list or not
  1966. // int* piLastInList - pointer to store whether this is the last
  1967. // item in the list or not
  1968. // int* piIsBuiltIn - pointer to store whether this item is a built
  1969. // in custom action or not
  1970. //
  1971. // Returns: HRESULT - standard COM error codes.
  1972. //
  1973. // History: quintinb Created Header 02/26/00
  1974. //
  1975. //+----------------------------------------------------------------------------
  1976. HRESULT CustomActionList::GetListPositionAndBuiltInState(HINSTANCE hInstance, CustomActionListItem* pItem, int* piFirstInList,
  1977. int* piLastInList, int *piIsBuiltIn)
  1978. {
  1979. MYDBGASSERT(pItem);
  1980. MYDBGASSERT(piFirstInList);
  1981. MYDBGASSERT(piLastInList);
  1982. MYDBGASSERT(piIsBuiltIn);
  1983. if ((NULL == pItem) || (NULL == piFirstInList) || (NULL == piLastInList) || (NULL == piIsBuiltIn))
  1984. {
  1985. return E_INVALIDARG;
  1986. }
  1987. HRESULT hr;
  1988. CustomActionListItem* pCurrent = NULL;
  1989. CustomActionListItem* pFollower = NULL;
  1990. //
  1991. // Search for the item
  1992. //
  1993. hr = Find(hInstance, pItem->szDescription, pItem->Type, &pCurrent, &pFollower);
  1994. if (SUCCEEDED(hr))
  1995. {
  1996. *piFirstInList = (m_CustomActionHash[pItem->Type] == pCurrent) ? -1 : 0;
  1997. *piLastInList = (pCurrent && (NULL == pCurrent->Next)) ? -1 : 0;
  1998. *piIsBuiltIn = (pCurrent->bBuiltInAction) ? -1 : 0;
  1999. }
  2000. return hr;
  2001. }
  2002. //+----------------------------------------------------------------------------
  2003. //
  2004. // Function: CustomActionList::IsCmDl
  2005. //
  2006. // Synopsis: Checks to see if the passed in filename cmdl32.exe
  2007. //
  2008. // Arguments: LPTSTR szFileName - filename to check
  2009. //
  2010. // Returns: BOOL - returns TRUE if the dll is one of the cmdl dll's
  2011. //
  2012. // History: quintinb Created 11/24/97
  2013. //
  2014. //+----------------------------------------------------------------------------
  2015. BOOL CustomActionList::IsCmDl(CustomActionListItem* pItem)
  2016. {
  2017. MYDBGASSERT(pItem);
  2018. BOOL bRet = FALSE;
  2019. if (pItem && (ONCONNECT == pItem->Type))
  2020. {
  2021. LPTSTR pszFileName = CmStrrchr(pItem->szProgram, TEXT('\\'));
  2022. if (pszFileName)
  2023. {
  2024. pszFileName = CharNext(pszFileName);
  2025. }
  2026. else
  2027. {
  2028. pszFileName = pItem->szProgram;
  2029. }
  2030. if (0 == lstrcmpi(pszFileName, TEXT("cmdl32.exe")))
  2031. {
  2032. bRet = TRUE;
  2033. }
  2034. }
  2035. return bRet;
  2036. }
  2037. //+----------------------------------------------------------------------------
  2038. //
  2039. // Function: CustomActionListEnumerator::CustomActionListEnumerator
  2040. //
  2041. // Synopsis: Constructor for the CustomActionListEnumerator class. This function
  2042. // requires a CustomActionList to enumerate from.
  2043. //
  2044. // Arguments: CustomActionList* pActionListToWorkFrom - custom action list class
  2045. // to enumerate
  2046. //
  2047. // Returns: HRESULT - standard COM error codes.
  2048. //
  2049. // History: quintinb Created Header 02/26/00
  2050. //
  2051. //+----------------------------------------------------------------------------
  2052. CustomActionListEnumerator::CustomActionListEnumerator(CustomActionList* pActionListToWorkFrom)
  2053. {
  2054. MYDBGASSERT(pActionListToWorkFrom);
  2055. m_pActionList = pActionListToWorkFrom;
  2056. Reset();
  2057. }
  2058. //+----------------------------------------------------------------------------
  2059. //
  2060. // Function: CustomActionListEnumerator::Reset
  2061. //
  2062. // Synopsis: Resets the CustomActionListEnumerator class. Thus the user can
  2063. // restart the enumeration by resetting the class.
  2064. //
  2065. // Arguments: None
  2066. //
  2067. // Returns: HRESULT - standard COM error codes.
  2068. //
  2069. // History: quintinb Created Header 02/26/00
  2070. //
  2071. //+----------------------------------------------------------------------------
  2072. void CustomActionListEnumerator::Reset()
  2073. {
  2074. m_iCurrentList = 0;
  2075. m_pCurrentListItem = NULL;
  2076. }
  2077. //+----------------------------------------------------------------------------
  2078. //
  2079. // Function: CustomActionListEnumerator::GetNextIncludedProgram
  2080. //
  2081. // Synopsis: This function is the work horse of the enumerator. It gets the
  2082. // next item in the enumeration with an included program. This
  2083. // enumerator is useful for getting all of the files that need to be
  2084. // included in the profile.
  2085. //
  2086. // Arguments: TCHAR* pszProgram - string buffer to hold the next program
  2087. // DWORD dwBufferSize - size of the passed in buffer
  2088. //
  2089. // Returns: HRESULT - standard COM error codes.
  2090. //
  2091. // History: quintinb Created Header 02/26/00
  2092. //
  2093. //+----------------------------------------------------------------------------
  2094. HRESULT CustomActionListEnumerator::GetNextIncludedProgram(TCHAR* pszProgram, DWORD dwBufferSize)
  2095. {
  2096. HRESULT hr = S_FALSE;
  2097. CustomActionListItem* pItem;
  2098. if (pszProgram && dwBufferSize)
  2099. {
  2100. if (m_pActionList)
  2101. {
  2102. while (m_iCurrentList < c_iNumCustomActionTypes)
  2103. {
  2104. if (m_pCurrentListItem)
  2105. {
  2106. //
  2107. // We are in the middle of an enumeration, use pCurrentProgramFileNameItem
  2108. // as the next item to examine.
  2109. //
  2110. pItem = m_pCurrentListItem;
  2111. }
  2112. else
  2113. {
  2114. //
  2115. // We are just starting or we have exhausted the current list
  2116. //
  2117. pItem = m_pActionList->m_CustomActionHash[m_iCurrentList];
  2118. }
  2119. while (pItem)
  2120. {
  2121. if (pItem->bIncludeBinary)
  2122. {
  2123. //
  2124. // We have the next item to pass back
  2125. //
  2126. lstrcpyn(pszProgram, pItem->szProgram, dwBufferSize);
  2127. //
  2128. // Next time we look for an item, start with the next in the list
  2129. //
  2130. m_pCurrentListItem = pItem->Next;
  2131. //
  2132. // If m_pCurrentListItem is NULL, we are at the end of the list now
  2133. // and we want to increment m_iCurrentList so that we start at the
  2134. // next list for the next item or terminate properly if we are
  2135. // on the last list
  2136. //
  2137. if (NULL == m_pCurrentListItem)
  2138. {
  2139. m_iCurrentList++;
  2140. }
  2141. hr = S_OK;
  2142. goto exit;
  2143. }
  2144. pItem = pItem->Next;
  2145. }
  2146. m_pCurrentListItem = NULL;
  2147. m_iCurrentList++;
  2148. }
  2149. }
  2150. else
  2151. {
  2152. hr = E_UNEXPECTED;
  2153. }
  2154. }
  2155. else
  2156. {
  2157. hr = E_INVALIDARG;
  2158. }
  2159. exit:
  2160. return hr;
  2161. }