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.

1873 lines
52 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1999 - 2000
  6. //
  7. // File: utilityfunctions.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. // UtilityFunctions.cpp: implementation of the CUtilityFunctions class.
  11. //
  12. //////////////////////////////////////////////////////////////////////
  13. #ifndef NO_STRICT
  14. #ifndef STRICT
  15. #define STRICT 1
  16. #endif
  17. #endif /* NO_STRICT */
  18. #include <WINDOWS.H>
  19. #include <STDIO.H>
  20. #include <TCHAR.H>
  21. #include <stdlib.h>
  22. #include "Globals.h"
  23. #include "UtilityFunctions.h"
  24. #include "ModuleInfo.h"
  25. //////////////////////////////////////////////////////////////////////
  26. // Construction/Destruction
  27. //////////////////////////////////////////////////////////////////////
  28. typedef struct
  29. {
  30. LPTSTR tszEnvironmentVariable;
  31. LPTSTR tszRegistryKey;
  32. LPTSTR tszRegistryValue;
  33. } ENVBLOCK;
  34. ENVBLOCK g_tszEnvironmentVariables[] =
  35. {
  36. // Support for Exchange Server
  37. TEXT("EXCHSRVR"),
  38. TEXT("SOFTWARE\\Microsoft\\Exchange\\Setup"),
  39. TEXT("Services"),
  40. // Support for SNA Server
  41. TEXT("SNASERVER"),
  42. TEXT("SOFTWARE\\Microsoft\\Sna Server\\CurrentVersion"),
  43. TEXT("PathName"),
  44. // Support for SQL Server
  45. TEXT("SQLSERVER"),
  46. TEXT("SOFTWARE\\Microsoft\\MSSQLServer\\Setup"),
  47. TEXT("SQLPath"),
  48. // Support for SMS Server
  49. TEXT("SMSSERVER"),
  50. TEXT("SOFTWARE\\Microsoft\\SMS\\Identification"),
  51. TEXT("Installation Directory"),
  52. // Support for INETSRV Server
  53. TEXT("INETSRV"),
  54. TEXT("SOFTWARE\\Microsoft\\INetStp"),
  55. TEXT("InstallPath"),
  56. // Support for WSPSRV Server
  57. TEXT("WSPSRV"),
  58. TEXT("SYSTEM\\CurrentControlSet\\Services\\WSPSrv\\Parameters"),
  59. TEXT("InstallRoot"),
  60. NULL,
  61. NULL,
  62. NULL
  63. };
  64. CUtilityFunctions::CUtilityFunctions()
  65. {
  66. }
  67. CUtilityFunctions::~CUtilityFunctions()
  68. {
  69. }
  70. /*++
  71. Function Name
  72. LPTSTR CUtilityFunctions::ExpandPath(LPCTSTR tszInputPath)
  73. Routine Description:
  74. This routine copies the provided tszInputPath to a new
  75. buffer which is returned to the caller. Any environment variables (or
  76. pseudo-environment variables) included in the tszInputPath are expanded
  77. before being copied to the destination string.
  78. This routine allocates storage for the return string, it is the responsibility
  79. of the caller to release it.
  80. Arguments:
  81. [IN] LPCTSTR tszInputString - Input string
  82. Return Value:
  83. [OUT ] LPTSTR Returns the new string
  84. --*/
  85. LPTSTR CUtilityFunctions::ExpandPath(LPCTSTR tszInputPath)
  86. {
  87. // Pointer to our input path buffer
  88. LPCTSTR ptszInputPathPointer;
  89. // Buffer to hold pre-translated Environment Variable
  90. TCHAR tszEnvironmentVariableBuffer[MAX_PATH];
  91. // Buffer to hold translated environment variables
  92. TCHAR tszTranslatedEnvironmentVariable[MAX_PATH];
  93. LPTSTR ptszTranslatedEnvironmentVariablePointer;
  94. // Generic counter variable
  95. ULONG iCharIndex;
  96. // Buffer to hold Output Path
  97. LPTSTR tszOutputPathBuffer, ptszOutputPathPointer;
  98. ULONG iOutputPathBufferSize;
  99. if (!tszInputPath) {
  100. return(NULL);
  101. }
  102. // Setup our pointer to our input buffer
  103. ptszInputPathPointer = tszInputPath;
  104. #ifdef _DEBUG
  105. // This puts stress on the re-alloc code...
  106. iOutputPathBufferSize = MAX_PATH; // We need less stress here (numega has probs)
  107. #else
  108. iOutputPathBufferSize = _tcslen(tszInputPath) + MAX_PATH + 1;
  109. #endif
  110. // Create our output buffer...
  111. //#ifdef _DEBUG
  112. // _tprintf(TEXT("ExpandPath() - Output Buffer created\n"));
  113. //#endif
  114. ptszOutputPathPointer = tszOutputPathBuffer = new TCHAR[iOutputPathBufferSize];
  115. if (!tszOutputPathBuffer)
  116. {
  117. return(NULL);
  118. }
  119. DWORD iTranslatedCharacters = 0;
  120. // Loop through our input buffer until we're done...
  121. while( ptszInputPathPointer && *ptszInputPathPointer)
  122. {
  123. // We're searching for % to designate the start of an env. var...
  124. if (*ptszInputPathPointer == '%')
  125. {
  126. iCharIndex = 0;
  127. // Advance to just beyond the % character
  128. ptszInputPathPointer++;
  129. // While we have more environment variable chars...
  130. while (ptszInputPathPointer && *ptszInputPathPointer && *ptszInputPathPointer != '%')
  131. {
  132. // Copy the environment variable into our buffer
  133. tszEnvironmentVariableBuffer[iCharIndex++] = *ptszInputPathPointer++;
  134. }
  135. // Advanced to just beyond the closing % character
  136. ptszInputPathPointer++;
  137. // Null terminate our Environment Variable Buffer
  138. tszEnvironmentVariableBuffer[iCharIndex] = '\0';
  139. // Setup the Translated Env. Variable Buffer
  140. ptszTranslatedEnvironmentVariablePointer = tszTranslatedEnvironmentVariable;
  141. *ptszTranslatedEnvironmentVariablePointer = 0;
  142. // Translate the Environment Variables!
  143. iTranslatedCharacters = GetEnvironmentVariable( tszEnvironmentVariableBuffer, ptszTranslatedEnvironmentVariablePointer, MAX_PATH );
  144. // If we didn't translate anything... we need to look for this as a special env. variable...
  145. if (iTranslatedCharacters == 0)
  146. {
  147. bool fSpecialEnvironmentVariable = false;
  148. // Scan our special variables...
  149. for (int i = 0; g_tszEnvironmentVariables[i].tszEnvironmentVariable && !fSpecialEnvironmentVariable; i++)
  150. {
  151. if (!_tcsicmp(g_tszEnvironmentVariables[i].tszEnvironmentVariable,
  152. tszEnvironmentVariableBuffer) )
  153. {
  154. // MATCHES!!!!
  155. HKEY hKey;
  156. DWORD lpType = 0;
  157. LONG Results = ERROR_SUCCESS;
  158. DWORD lpcbData = MAX_PATH;
  159. BYTE outBuf[MAX_PATH];
  160. Results = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
  161. g_tszEnvironmentVariables[i].tszRegistryKey,
  162. 0,
  163. KEY_READ || KEY_QUERY_VALUE,
  164. &hKey);
  165. fSpecialEnvironmentVariable = (Results == ERROR_SUCCESS);
  166. if (Results != ERROR_SUCCESS)
  167. {
  168. _tprintf(TEXT("ERROR: Unable to open registry key [%s]\n"), g_tszEnvironmentVariables[i].tszRegistryKey);
  169. _tprintf(TEXT("ERROR: Unable to open registry key - Error = 0x%x\n"), Results);
  170. }
  171. if (fSpecialEnvironmentVariable)
  172. {
  173. // Now, read the value that has our install path...
  174. Results = RegQueryValueEx(
  175. hKey, // handle of key to query
  176. g_tszEnvironmentVariables[i].tszRegistryValue, // address of name of value to query
  177. NULL, // reserved
  178. &lpType, // address of buffer for value type
  179. outBuf, // address of data buffer
  180. &lpcbData); // address of data buffer size
  181. // Is it still succesful?
  182. fSpecialEnvironmentVariable = ( (Results == ERROR_SUCCESS) &&
  183. (lpType == REG_SZ) );
  184. if (Results != ERROR_SUCCESS)
  185. {
  186. _tprintf(TEXT("ERROR: Registry key opened [%s]\n"), g_tszEnvironmentVariables[i].tszRegistryKey);
  187. _tprintf(TEXT("ERROR: Unable to query registry value [%s]\n"), g_tszEnvironmentVariables[i].tszRegistryValue);
  188. _tprintf(TEXT("ERROR: Unable to query registry value - Error = 0x%x\n"), Results);
  189. }
  190. // Copy only if we got something?
  191. RegCloseKey(hKey);
  192. }
  193. if (fSpecialEnvironmentVariable)
  194. {
  195. // Copy the new data!!!
  196. _tcscpy(tszTranslatedEnvironmentVariable, (LPTSTR)outBuf);
  197. }
  198. }
  199. }
  200. if (!fSpecialEnvironmentVariable)
  201. {
  202. #ifdef _DEBUG
  203. _tprintf(TEXT("Unrecognized Environment variable found! [%%%s%%]\n"), tszEnvironmentVariableBuffer);
  204. #endif
  205. // Error copy the original environment variable provided back to the "translated env
  206. // buffer to be copied back below...
  207. _tcscpy(tszTranslatedEnvironmentVariable, TEXT("%"));
  208. _tcscat(tszTranslatedEnvironmentVariable, tszEnvironmentVariableBuffer);
  209. _tcscat(tszTranslatedEnvironmentVariable, TEXT("%"));
  210. }
  211. }
  212. // Iterate through the Translated Env. Variable Buffer, and copy to the output buffer
  213. while (ptszTranslatedEnvironmentVariablePointer && *ptszTranslatedEnvironmentVariablePointer)
  214. {
  215. // ISSUE-2000/07/24-GREGWI: Check to ensure this works properly *var++ changed to *(var++)
  216. // Copy a char
  217. *(ptszOutputPathPointer++) = *(ptszTranslatedEnvironmentVariablePointer++);
  218. // If our output buffer is full, we need to allocate a new buffer...
  219. if (ptszOutputPathPointer >= tszOutputPathBuffer + iOutputPathBufferSize)
  220. {
  221. // Bump up our new size by MAX_PATH
  222. iOutputPathBufferSize += MAX_PATH;
  223. // We need to enlarge the buffer our string is in...
  224. tszOutputPathBuffer = ReAlloc(tszOutputPathBuffer, &ptszOutputPathPointer, iOutputPathBufferSize);
  225. if (tszOutputPathBuffer == NULL)
  226. return NULL;
  227. }
  228. }
  229. }
  230. // Probe to see if we're pointing at a NULL... this can happen if we've just completed
  231. // environment variable expansion...
  232. if ( *ptszInputPathPointer == '\0')
  233. continue;
  234. // Before we copy the char we're looking at... we need to test
  235. // for a trailing backslash (\) on the end (which we'll silently remove)...
  236. if ( (*ptszInputPathPointer == '\\') && // Do we have a slash
  237. ( (*(ptszInputPathPointer+1) == ';') || (*(ptszInputPathPointer+1) == '\0') ) && // and the next char is a NULL or semi-colon?
  238. ( ptszInputPathPointer != tszInputPath ) && // and we're not on the first char
  239. ( *(ptszInputPathPointer-1) != ':' ) // and the previous char is not a colon...
  240. )
  241. {
  242. // Advance the pointer only... (remove the trailing slash)
  243. *(ptszInputPathPointer++);
  244. }
  245. else
  246. {
  247. // ISSUE-2000/07/24-GREGWI: Ensure this works.. changing *var++ to *(var++)
  248. // Copy a char from the input path, to the output path
  249. *(ptszOutputPathPointer++) = *(ptszInputPathPointer++);
  250. }
  251. // If our output buffer is full, we need to allocate a new buffer...
  252. if (ptszOutputPathPointer >= tszOutputPathBuffer + iOutputPathBufferSize)
  253. {
  254. // Bump up our new size by MAX_PATH
  255. iOutputPathBufferSize += MAX_PATH;
  256. // We need to enlarge the buffer our string is in...
  257. tszOutputPathBuffer = ReAlloc(tszOutputPathBuffer, &ptszOutputPathPointer, iOutputPathBufferSize);
  258. if (tszOutputPathBuffer == NULL)
  259. return NULL;
  260. }
  261. }
  262. // Null terminate our output buffer
  263. *ptszOutputPathPointer = '\0';
  264. // Return our results...
  265. return tszOutputPathBuffer;
  266. }
  267. bool CUtilityFunctions::ContainsWildCardCharacter(LPCTSTR tszPathToSearch)
  268. {
  269. if (!tszPathToSearch)
  270. return false;
  271. LPCTSTR ptszPointer = tszPathToSearch;
  272. while (*ptszPointer)
  273. {
  274. switch (*ptszPointer)
  275. {
  276. case '*':
  277. case '?':
  278. return true;
  279. }
  280. ptszPointer++;
  281. }
  282. return false;
  283. }
  284. /*
  285. bool CUtilityFunctions::IsDirectoryPath(LPCTSTR tszFilePath)
  286. {
  287. if (!tszFilePath)
  288. return false;
  289. WIN32_FIND_DATA lpFindFileData;
  290. HANDLE hFileOrDirectory = FindFirstFile(tszFilePath, &lpFindFileData);
  291. if (INVALID_HANDLE_VALUE == hFileOrDirectory)
  292. return false;
  293. FindClose(hFileOrDirectory);
  294. if (lpFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  295. return true;
  296. return false;
  297. }
  298. */
  299. void CUtilityFunctions::PrintMessageString(DWORD dwMessageId)
  300. {
  301. // Define a constant for our "private" buffer...
  302. enum {MESSAGE_BUFFER_SIZE = 1024};
  303. TCHAR tszMessageBuffer[MESSAGE_BUFFER_SIZE];
  304. DWORD dwBytes = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
  305. NULL,
  306. dwMessageId,
  307. 0,
  308. tszMessageBuffer,
  309. MESSAGE_BUFFER_SIZE,
  310. NULL);
  311. if (dwBytes)
  312. {
  313. // We got something!
  314. // Should we null terminate?
  315. if ( (dwBytes > 2) &&
  316. (tszMessageBuffer[dwBytes-2] == '\r') &&
  317. (tszMessageBuffer[dwBytes-1] == '\n') )
  318. {
  319. tszMessageBuffer[dwBytes-2] = 0; // Null terminate this puppy...
  320. }
  321. _tprintf(TEXT("Error = %d (0x%x)\n[%s]\n"), dwMessageId, dwMessageId, tszMessageBuffer);
  322. }
  323. }
  324. bool CUtilityFunctions::CopySymbolFileToSymbolTree(LPCTSTR tszImageModuleName, LPTSTR * lplptszOriginalPathToSymbolFile, LPCTSTR tszSymbolTreePath)
  325. {
  326. // Before we start to copying... is the source location already in the symbol tree we're going to build?
  327. int iLengthOfFileName = _tcslen(*lplptszOriginalPathToSymbolFile);
  328. int iLengthOfSymbolTreeToBuild = _tcslen(tszSymbolTreePath);
  329. if (_tcsnicmp(*lplptszOriginalPathToSymbolFile,
  330. tszSymbolTreePath,
  331. iLengthOfFileName < iLengthOfSymbolTreeToBuild ?
  332. iLengthOfFileName : iLengthOfSymbolTreeToBuild) )
  333. {
  334. // Okay, we need to take the original module name, and get the extension...
  335. TCHAR tszExtension[_MAX_EXT];
  336. TCHAR tszPathToCopySymbolFileTo[_MAX_PATH];
  337. _tsplitpath(tszImageModuleName, NULL, NULL, NULL, tszExtension);
  338. _tcscpy( tszPathToCopySymbolFileTo, tszSymbolTreePath);
  339. // This directory should exist already... let's tag on the extension directory (if one exists)...
  340. if (_tcsclen(tszExtension) > 1)
  341. {
  342. // Copy the extension (skipping the period)
  343. _tcscat( tszPathToCopySymbolFileTo, &tszExtension[1] );
  344. _tcscat( tszPathToCopySymbolFileTo, TEXT("\\") );
  345. // Now, we need to ensure this directory exists (we'll cache these checks so we don't need to
  346. // keep checking the same directory over and over...
  347. if (!g_lpDelayLoad->MakeSureDirectoryPathExists(tszPathToCopySymbolFileTo) )
  348. {
  349. _tprintf(TEXT("ERROR: Unable to create symbol subdirectory [%s]\n"), tszPathToCopySymbolFileTo );
  350. PrintMessageString(GetLastError());
  351. }
  352. }
  353. TCHAR tszSymbolFileName[_MAX_FNAME];
  354. TCHAR tszSymbolFileExt[_MAX_EXT];
  355. _tsplitpath(*lplptszOriginalPathToSymbolFile, NULL, NULL, tszSymbolFileName, tszSymbolFileExt);
  356. // Okay... it's time to copy the file!!!
  357. _tcscat( tszPathToCopySymbolFileTo, tszSymbolFileName );
  358. _tcscat( tszPathToCopySymbolFileTo, tszSymbolFileExt );
  359. // We don't know if the destination file exists.. but if it does, we'll change
  360. // the attributes (to remove the read-only bit at least
  361. DWORD dwFileAttributes = GetFileAttributes(tszPathToCopySymbolFileTo);
  362. if (dwFileAttributes != 0xFFFFFFFF)
  363. {
  364. if (dwFileAttributes & FILE_ATTRIBUTE_READONLY)
  365. {
  366. // The read-only attribute is set... we must remove it...
  367. dwFileAttributes = dwFileAttributes & (~FILE_ATTRIBUTE_READONLY);
  368. SetFileAttributes(tszPathToCopySymbolFileTo, dwFileAttributes);
  369. }
  370. }
  371. if ( CopyFile(*lplptszOriginalPathToSymbolFile, tszPathToCopySymbolFileTo, FALSE) )
  372. {
  373. // Success!!! Let's go ahead and give a visual indicator as to where we copied
  374. // the file from...
  375. _tprintf(TEXT("VERIFIED: [%s] copied to Symbol Tree\n"), *lplptszOriginalPathToSymbolFile);
  376. // Okay, since we've copied this to our symbol tree... we should update
  377. // our modulepath...
  378. // Nuke the old one...
  379. delete [] *lplptszOriginalPathToSymbolFile;
  380. // Copy in the new one...
  381. *lplptszOriginalPathToSymbolFile = CopyString(tszPathToCopySymbolFileTo);
  382. if (!*lplptszOriginalPathToSymbolFile)
  383. return false;
  384. } else
  385. {
  386. _tprintf(TEXT("ERROR: Unable to copy symbol file to [%s]\n"), tszPathToCopySymbolFileTo );
  387. PrintMessageString(GetLastError());
  388. }
  389. }
  390. return true;
  391. }
  392. /*++
  393. Function Name
  394. LPTSTR CUtilityFunctions::CopyString(LPCTSTR tszInputString)
  395. Routine Description:
  396. This routine copies the provided tszInputString to the destination address.
  397. This routine allocates storage for the string, it is the responsibility
  398. of the caller to release it.
  399. Arguments:
  400. [IN] LPCTSTR tszInputString - Input string
  401. Return Value:
  402. Returns the new string
  403. --*/
  404. LPTSTR CUtilityFunctions::CopyString(LPCTSTR tszInputString)
  405. {
  406. // Did we get a proper input string?
  407. if (!tszInputString)
  408. return NULL;
  409. LPTSTR tszDestinationString = new TCHAR[(_tcsclen(tszInputString)+1)];
  410. if (!tszDestinationString )
  411. return NULL;
  412. _tcscpy(tszDestinationString, tszInputString);
  413. return tszDestinationString;
  414. }
  415. LPTSTR CUtilityFunctions::CopyAnsiStringToTSTR(LPCSTR szInputString, LPTSTR tszOutputBuffer, unsigned int iBufferLength)
  416. {
  417. // Did we get a proper input string?
  418. if (!szInputString)
  419. return NULL;
  420. if (iBufferLength && !tszOutputBuffer)
  421. return NULL;
  422. LPTSTR tszDestinationString;
  423. #ifdef _UNICODE
  424. // Get the size of the source of the Ansi string...
  425. // Saving the value keeps MultiByteToWideChar from having to
  426. // calculate it twice...
  427. unsigned int cbMultiByte = strlen(szInputString);
  428. DWORD cbStringLength = MultiByteToWideChar( CP_ACP,
  429. MB_PRECOMPOSED,
  430. szInputString,
  431. cbMultiByte,
  432. NULL,
  433. 0);
  434. if (!cbStringLength)
  435. return NULL;
  436. // Do we need to allocate storage???
  437. if (iBufferLength == 0)
  438. {
  439. // Allocate storage
  440. tszDestinationString = new TCHAR[cbStringLength+1];
  441. if (!tszDestinationString)
  442. return NULL;
  443. } else
  444. {
  445. if ( cbStringLength+1 > iBufferLength )
  446. return NULL;
  447. // Set the two strings to the same buffer...
  448. tszDestinationString = tszOutputBuffer;
  449. }
  450. // Do the actual conversion
  451. cbStringLength = MultiByteToWideChar( CP_ACP,
  452. MB_PRECOMPOSED,
  453. szInputString,
  454. cbMultiByte,
  455. tszDestinationString,
  456. (iBufferLength == 0) ? cbStringLength+1 : iBufferLength);
  457. if (!cbStringLength)
  458. return NULL;
  459. tszDestinationString[cbStringLength] = '\0';
  460. #else
  461. unsigned int cbMultiByte = strlen(szInputString);
  462. if (iBufferLength == 0)
  463. {
  464. iBufferLength = strlen(szInputString)+1;
  465. tszDestinationString = new TCHAR[iBufferLength];
  466. if (!tszDestinationString)
  467. return NULL;
  468. } else
  469. {
  470. if (cbMultiByte+1 > iBufferLength)
  471. return NULL;
  472. // Set the two strings to the same buffer...
  473. tszDestinationString = tszOutputBuffer;
  474. }
  475. strncpy(tszDestinationString, szInputString, iBufferLength);
  476. #endif
  477. return tszDestinationString;
  478. }
  479. LPTSTR CUtilityFunctions::CopyUnicodeStringToTSTR(LPCWSTR wszInputString, LPTSTR tszOutputBuffer, unsigned int iBufferLength)
  480. {
  481. // Did we get a proper input string?
  482. if (!wszInputString)
  483. return NULL;
  484. // Check for proper buffers and lengths if provided...
  485. if (iBufferLength && !tszOutputBuffer)
  486. return NULL;
  487. LPTSTR tszDestinationString;
  488. #ifdef _UNICODE
  489. unsigned int cbMultiByte = wcslen(wszInputString);
  490. if (iBufferLength == 0)
  491. {
  492. tszDestinationString = new TCHAR[wcslen(wszInputString)+1];
  493. if (!tszDestinationString)
  494. return NULL;
  495. } else
  496. {
  497. if (cbMultiByte+1 > iBufferLength)
  498. return NULL;
  499. // Set the two strings to the same buffer...
  500. tszDestinationString = tszOutputBuffer;
  501. }
  502. wcscpy(tszDestinationString, wszInputString);
  503. #else
  504. int cchWideChar = wcslen(wszInputString);
  505. DWORD cbStringLength = WideCharToMultiByte( CP_ACP,
  506. 0,
  507. wszInputString,
  508. cchWideChar,
  509. NULL,
  510. 0,
  511. NULL,
  512. NULL);
  513. if (cbStringLength)
  514. return NULL;
  515. // Do we need to allocate storage???
  516. if (iBufferLength == 0)
  517. {
  518. tszDestinationString = new TCHAR[cbStringLength+1];
  519. if (!tszDestinationString)
  520. return NULL;
  521. } else
  522. {
  523. if ( cbStringLength+1 > iBufferLength )
  524. return NULL;
  525. // Set the two strings to the same buffer...
  526. tszDestinationString = tszOutputBuffer;
  527. }
  528. // Do the actual conversion...
  529. cbStringLength = WideCharToMultiByte( CP_ACP,
  530. 0,
  531. wszInputString,
  532. cchWideChar,
  533. tszDestinationString,
  534. (iBufferLength == 0) ? cbStringLength+1 : iBufferLength,
  535. NULL,
  536. NULL);
  537. if (!cbStringLength)
  538. return NULL;
  539. tszDestinationString[cbStringLength] = '\0';
  540. #endif
  541. return tszDestinationString;
  542. }
  543. //
  544. // CUtilityFunctions::CopyTSTRStringToAnsi()
  545. //
  546. // This routine copies from a TSTR source to an ANSI destination with optional allocation
  547. // of the destination buffer... the default is to allocate storage, but if you provide
  548. // a buffer length, we will assume it's available...
  549. //
  550. LPSTR CUtilityFunctions::CopyTSTRStringToAnsi(LPCTSTR tszInputString, LPSTR szOutputBuffer, unsigned int iBufferLength)
  551. {
  552. // Did we get a proper input string?
  553. if (!tszInputString)
  554. return NULL;
  555. if (iBufferLength && !szOutputBuffer)
  556. return NULL;
  557. LPSTR szDestinationString;
  558. #ifdef _UNICODE
  559. // Get the size of the source of the Unicode string...
  560. // Saving the value keeps WideCharToMultiByte from having to
  561. // calculate it twice...
  562. unsigned int cchWideChar = wcslen(tszInputString);
  563. // This is a probe to see how much we'll be copying...
  564. DWORD cbStringLength = WideCharToMultiByte( CP_ACP,
  565. 0,
  566. tszInputString,
  567. cchWideChar,
  568. NULL,
  569. 0,
  570. NULL,
  571. NULL);
  572. if (!cbStringLength)
  573. return NULL;
  574. // Do we need to allocate storage???
  575. if (iBufferLength == 0)
  576. {
  577. // Allocate storage
  578. szDestinationString = new char[cbStringLength+1];
  579. if (!szDestinationString)
  580. return NULL;
  581. } else
  582. {
  583. if ( cbStringLength+1 > iBufferLength )
  584. return NULL;
  585. // Set the two strings to the same buffer...
  586. szDestinationString = szOutputBuffer;
  587. }
  588. // Do the actual conversion
  589. cbStringLength = WideCharToMultiByte( CP_ACP,
  590. 0,
  591. tszInputString,
  592. cchWideChar,
  593. szDestinationString,
  594. (iBufferLength == 0) ? cbStringLength+1 : iBufferLength,
  595. NULL,
  596. NULL);
  597. if (!cbStringLength)
  598. return NULL;
  599. szDestinationString[cbStringLength] = '\0';
  600. #else
  601. unsigned int cchAnsiChar = strlen(tszInputString);
  602. if (iBufferLength == 0)
  603. {
  604. szDestinationString = new char[cchAnsiChar+1]; // One extra for the NULL
  605. if (!szDestinationString)
  606. return NULL;
  607. } else
  608. {
  609. if (cchAnsiChar+1 > iBufferLength)
  610. return NULL;
  611. // Set the two strings to the same buffer...
  612. szDestinationString = szOutputBuffer;
  613. }
  614. strcpy(szDestinationString, tszInputString);
  615. #endif
  616. return szDestinationString;
  617. }
  618. LPWSTR CUtilityFunctions::CopyTSTRStringToUnicode(LPCTSTR tszInputString)
  619. {
  620. // Did we get a proper input string?
  621. if (!tszInputString)
  622. return NULL;
  623. LPWSTR wszDestinationString;
  624. #ifdef _UNICODE
  625. wszDestinationString = new TCHAR[wcslen(tszInputString)+1];
  626. if (!wszDestinationString)
  627. return NULL;
  628. wcscpy(wszDestinationString, tszInputString);
  629. #else
  630. int cbMultiByte = strlen(tszInputString);
  631. DWORD cbStringLength = MultiByteToWideChar( CP_ACP,
  632. MB_PRECOMPOSED,
  633. tszInputString,
  634. cbMultiByte,
  635. NULL,
  636. 0);
  637. if (!cbStringLength)
  638. return NULL;
  639. wszDestinationString = new wchar_t[cbStringLength+1];
  640. if (!wszDestinationString)
  641. return NULL;
  642. cbStringLength = MultiByteToWideChar( CP_ACP,
  643. MB_PRECOMPOSED,
  644. tszInputString,
  645. cbMultiByte,
  646. wszDestinationString,
  647. cbStringLength+1);
  648. wszDestinationString[cbStringLength] = '\0';
  649. #endif
  650. return wszDestinationString;
  651. }
  652. /*++
  653. HANDLE CUtilityFunctions::FindDebugInfoFileEx2(
  654. [IN] LPTSTR tszFileName,
  655. [IN] LPTSTR SymbolPath,
  656. [IN] PFIND_DEBUG_FILE_CALLBACK Callback,
  657. [IN] PVOID CallerData
  658. Routine Description:
  659. The rules are, the name of the DBG/PDB file being searched for is provided,
  660. and when found the routine returns a file handle to it... if a callback is
  661. provided then the callback is invoked and a decision is made to return the
  662. file handle based on the callback response...
  663. Arguments:
  664. tszFileName - Supplies a symbol name to search for.
  665. SymbolPath - semi-colon delimited
  666. DebugFilePath -
  667. Callback - May be NULL. Callback that indicates whether the Symbol file is valid, or whether
  668. the function should continue searching for another Symbol file.
  669. The callback returns TRUE if the Symbol file is valid, or FALSE if the function should
  670. continue searching.
  671. CallerData - May be NULL. Data passed to the callback.
  672. Return Value:
  673. The handle for the DBG/PDB file if any...
  674. In an effort to emulate FindDebugInfoFile(), this function will return 0 on failure or
  675. the handle to the file otherwise...
  676. --*/
  677. HANDLE CUtilityFunctions::FindDebugInfoFileEx2(LPTSTR tszFileName, LPTSTR SymbolPath, PFIND_DEBUG_FILE_CALLBACK Callback, PVOID CallerData)
  678. {
  679. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  680. bool fProcessPath = true;
  681. bool fScavengeSuccessful = false;
  682. LPTSTR tszSymbolPathStart, tszSymbolPathEnd;
  683. tszSymbolPathStart = SymbolPath;
  684. // Find the end of the path
  685. tszSymbolPathEnd = _tcschr( tszSymbolPathStart, ';' );
  686. // If tszSymbolPathEnd is non-zero, then there is another path following...
  687. if (tszSymbolPathEnd)
  688. *tszSymbolPathEnd = '\0'; // Change the ';' to a Null temporarily...
  689. while (fProcessPath)
  690. {
  691. //#ifdef _DEBUG
  692. // _tprintf(TEXT("\n\nProcessing Path [%s]\n"), tszSymbolPathStart);
  693. //#endif
  694. // Begin the "madness"... ;)
  695. // Search until we make a perfect hit... construct the directory path...
  696. TCHAR tszSymbolPath[_MAX_PATH];
  697. // Copy what we have...
  698. _tcscpy(tszSymbolPath, tszSymbolPathStart);
  699. // We should have at least a few chars to search on...
  700. if (_tcslen(tszSymbolPath) < 2)
  701. {
  702. // Repair the search string if needed...
  703. if (tszSymbolPathEnd)
  704. {
  705. *tszSymbolPathEnd = ';';
  706. }
  707. break;
  708. };
  709. fScavengeSuccessful = ScavengeForSymbolFiles(tszSymbolPath, tszFileName, Callback, CallerData, &FileHandle, 1);
  710. // Repair the search string now!
  711. if (tszSymbolPathEnd)
  712. {
  713. *tszSymbolPathEnd = ';';
  714. }
  715. // If were successful on our hunt or there is no symbol path left to search... break...
  716. if (fScavengeSuccessful || !tszSymbolPathEnd)
  717. {
  718. break;
  719. } else
  720. {
  721. // Advance to next string
  722. tszSymbolPathStart = tszSymbolPathEnd + 1;
  723. tszSymbolPathEnd = _tcschr( tszSymbolPathStart, ';' );
  724. if (tszSymbolPathEnd)
  725. {
  726. *tszSymbolPathEnd = '\0';
  727. };
  728. }
  729. }
  730. return ( (FileHandle == INVALID_HANDLE_VALUE) ? 0 : FileHandle);
  731. }
  732. /*++
  733. bool CUtilityFunctions::ScavengeForSymbolFiles(
  734. [IN] LPCTSTR tszSymbolPathStart,
  735. [IN] LPCTSTR tszSymbolToSearchFor,
  736. [IN] PFIND_DEBUG_FILE_CALLBACK Callback,
  737. [IN] PVOID CallerData,
  738. [OUT] LPHANDLE lpFileHandle,
  739. [IN] int iRecurseDepth )
  740. Routine Description:
  741. This routine is used to perform a recursive search for a Symbol File
  742. (tszSymbolToSearchFor). The routine will do a depth search, looking
  743. for the symbol at a current depth before going into sub-directories...
  744. If a Callback function is provided, then it will be invoked when the
  745. file we're looking for (by name) has been successfully opened. It is
  746. unknown to this routine, however, if the file we found is actually the
  747. correct one... the callback function's responsibility is to perform this
  748. evaluation and return to use success/failure. If failure (then we continue
  749. searching), if success (or no callback) then we return the filehandle
  750. associated with the file we found.
  751. It is the responsibility of the caller of this function to close any file
  752. handle returned.
  753. Arguments:
  754. tszSymbolPathStart - This is the directory to search
  755. tszSymbolToSearchFor - This is the symbol we're searching for
  756. Callback - May be NULL. This is a function used to evaluate if the symbol found is correct.
  757. CallerData - May be NULL. This data is passed to the callback (it is typically a CModuleInfo *)
  758. lpfileHandle - This is the file handle for the file found (if any)
  759. iRecurseDepth - This is the current depth of our search (defaults to 0)
  760. Return Value:
  761. The handle for the DBG/PDB file if any...
  762. --*/
  763. bool CUtilityFunctions::ScavengeForSymbolFiles(LPCTSTR tszSymbolPathStart, LPCTSTR tszSymbolToSearchFor, PFIND_DEBUG_FILE_CALLBACK Callback, PVOID CallerData, LPHANDLE lpFileHandle, int iRecurseDepth )
  764. {
  765. bool fSuccess = false;
  766. // Bale if we're in too deep...
  767. if (iRecurseDepth > MAX_RECURSE_DEPTH)
  768. return fSuccess;
  769. TCHAR tszFileBuffer[MAX_PATH+1];
  770. //
  771. // First, we'll look to see if we can open the file we're looking for AT this directory location
  772. //
  773. _tcscpy(tszFileBuffer, tszSymbolPathStart);
  774. if (tszFileBuffer[_tcslen(tszFileBuffer)] != '\\') // Do we need a backslash separator?
  775. _tcscat(tszFileBuffer, TEXT("\\"));
  776. _tcscat(tszFileBuffer, tszSymbolToSearchFor);
  777. if (g_lpProgramOptions->fDebugSearchPaths())
  778. {
  779. _tprintf(TEXT("DBG/PDB Search - Search here [%s]\n"), tszFileBuffer);
  780. }
  781. // Attempt to open the file...
  782. *lpFileHandle = CreateFile( tszFileBuffer,
  783. GENERIC_READ,
  784. (FILE_SHARE_READ | FILE_SHARE_WRITE),
  785. NULL,
  786. OPEN_EXISTING,
  787. 0,
  788. NULL
  789. );
  790. // Did we open it?
  791. if (*lpFileHandle != INVALID_HANDLE_VALUE)
  792. {
  793. // Yes!
  794. // If no callback... then we need to exit on out...
  795. if (!Callback)
  796. {
  797. // Assume success (well... we found a symbol file you asked for)
  798. fSuccess = true;
  799. } else
  800. {
  801. fSuccess = (TRUE == Callback(*lpFileHandle, tszFileBuffer, CallerData));
  802. }
  803. // Return from here only on success!
  804. if (fSuccess)
  805. return fSuccess;
  806. }
  807. // We either did NOT find the file, or we found a file but it was not the right one...
  808. //
  809. // Second, we search for sub-directories, invoking this function for each sub-dir we find...
  810. //
  811. TCHAR drive[_MAX_DRIVE];
  812. TCHAR dir[_MAX_DIR];
  813. TCHAR fname[_MAX_FNAME];
  814. TCHAR ext[_MAX_EXT];
  815. //
  816. // Compose the path to search...
  817. //
  818. _tcscpy(tszFileBuffer, tszSymbolPathStart);
  819. if (tszFileBuffer[_tcslen(tszFileBuffer)-1] != '\\') // Do we need a backslash separator?
  820. _tcscat(tszFileBuffer, TEXT("\\"));
  821. _tcscat(tszFileBuffer, TEXT("*.*"));
  822. // We want the component parts for later (so we can compose the full path)
  823. _tsplitpath(tszFileBuffer, drive, dir, fname, ext);
  824. WIN32_FIND_DATA lpFindFileData;
  825. // Okay, begin the search...
  826. HANDLE hFileOrDirectoryHandle = FindFirstFile(tszFileBuffer, &lpFindFileData);
  827. while ( INVALID_HANDLE_VALUE != hFileOrDirectoryHandle )
  828. {
  829. if (lpFindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
  830. {
  831. // Check to see if we've got the . or .. directories!
  832. if ( ( 0 == _tcscmp(lpFindFileData.cFileName, TEXT(".")) ) ||
  833. ( 0 == _tcscmp(lpFindFileData.cFileName, TEXT("..")) )
  834. )
  835. {
  836. goto getnextmodule;
  837. }
  838. // Compose the path to the directory...
  839. _tmakepath(tszFileBuffer, drive, dir, NULL, NULL);
  840. _tcscat(tszFileBuffer, lpFindFileData.cFileName);
  841. // Look to see if we can find the file we're after!
  842. fSuccess = ScavengeForSymbolFiles(tszFileBuffer, tszSymbolToSearchFor, Callback, CallerData, lpFileHandle, iRecurseDepth+1 );
  843. // On success from ScavengeForSymbolFiles, we have a file handle (hopefully the right one).
  844. // We want to terminate our recursive search
  845. if (fSuccess)
  846. break;
  847. };
  848. getnextmodule:
  849. if (!FindNextFile(hFileOrDirectoryHandle, &lpFindFileData))
  850. break;
  851. }
  852. if ( INVALID_HANDLE_VALUE != hFileOrDirectoryHandle )
  853. FindClose(hFileOrDirectoryHandle);
  854. return fSuccess;
  855. }
  856. LPTSTR CUtilityFunctions::ReAlloc(LPTSTR tszOutputPathBuffer, LPTSTR * ptszOutputPathPointer, size_t size)
  857. {
  858. // Save our old size... and position in the buffer...
  859. UINT iOldOutputPathBufferSize = (*ptszOutputPathPointer)-tszOutputPathBuffer;
  860. // Allocate our new bigger buffer...
  861. LPTSTR ptszNewOutputPathBuffer = new TCHAR[size];
  862. // Did we fail to allocate the new buffer?
  863. if (ptszNewOutputPathBuffer == NULL)
  864. return(NULL);
  865. #ifdef _DEBUG
  866. // This bogus code is here to protect a string copy which should always work...
  867. // but Numega Bounds Checker will sometimes AV in here... and we have to protect
  868. // ourselves or else it will appear we're leaking way above...
  869. __try
  870. {
  871. #endif
  872. // Now, we should copy from the old to the new buffer...
  873. _tcsncpy(ptszNewOutputPathBuffer, tszOutputPathBuffer, iOldOutputPathBufferSize);
  874. #ifdef _DEBUG
  875. } __except(EXCEPTION_EXECUTE_HANDLER)
  876. {
  877. _tprintf(TEXT("ReAlloc() - Exception Hit during stringcopy!!!\n"));
  878. }
  879. #endif
  880. // Calculate our position in our new buffer
  881. *ptszOutputPathPointer = ptszNewOutputPathBuffer + iOldOutputPathBufferSize;
  882. // Delete the old buffer
  883. delete [] tszOutputPathBuffer;
  884. return ptszNewOutputPathBuffer;
  885. }
  886. bool CUtilityFunctions::UnMungePathIfNecessary(LPTSTR tszPossibleBizarrePath)
  887. {
  888. /*
  889. // We have three known odd-ball cases...
  890. \SystemRoot\System32\smss.exe
  891. \??\C:\WINNT\system32\winlogon.exe
  892. \WINNT\System32\ntoskrnl.exe
  893. */
  894. if (tszPossibleBizarrePath[0] != '\\')
  895. return false; // Isn't a bizarre path (one we know about anyway)...
  896. // Setup Variables to Use
  897. TCHAR tszTempPath[_MAX_PATH], tszExpandedSystemRoot[_MAX_PATH];
  898. const TCHAR tszSystemRoot[] = TEXT("\\SystemRoot");
  899. const unsigned int iSystemRootLength = _tcslen(tszSystemRoot);
  900. const TCHAR tszNameSpace[] = TEXT("\\??\\");
  901. const unsigned int iNameSpaceLength = _tcslen(tszNameSpace);
  902. ExpandEnvironmentStrings(TEXT("%systemroot%"), tszExpandedSystemRoot, _MAX_PATH);
  903. const unsigned int iExpandedSystemRoot = _tcslen(tszExpandedSystemRoot);
  904. /*
  905. #ifdef _DEBUG
  906. _tprintf(TEXT("Bizarre module path found! [%s]\n"), tszPossibleBizarrePath);
  907. #endif
  908. */
  909. if ( _tcsnicmp(tszPossibleBizarrePath, tszSystemRoot, iSystemRootLength) == 0)
  910. { // We have a match...
  911. /*
  912. #ifdef _DEBUG
  913. _tprintf(TEXT("Matches [%s] sequence...\n"), tszSystemRoot);
  914. #endif
  915. */
  916. // We simply replace \systemroot with %systemroot% and expand the
  917. // environement variables
  918. LPTSTR tszPointer = tszPossibleBizarrePath;
  919. for (unsigned int i = 0; i < iSystemRootLength; i++)
  920. {
  921. // Advance by the name space length...
  922. tszPointer = CharNext(tszPointer);
  923. }
  924. _tcscpy(tszTempPath, TEXT("%systemroot%"));
  925. _tcscat(tszTempPath, tszPointer);
  926. ExpandEnvironmentStrings(tszTempPath, tszPossibleBizarrePath, _MAX_PATH);
  927. /*
  928. #ifdef _DEBUG
  929. _tprintf(TEXT("Bizarre module path changed to [%s]\n"), tszPossibleBizarrePath);
  930. #endif
  931. */
  932. } else
  933. if (_tcsnicmp(tszPossibleBizarrePath, tszNameSpace, iNameSpaceLength) == 0)
  934. { // We have a match...
  935. /*
  936. #ifdef _DEBUG
  937. _tprintf(TEXT("Matches [%s] sequence...\n"), tszNameSpace);
  938. #endif
  939. */
  940. // We simply remove the \??\ sequence from the namespace...
  941. LPTSTR tszPointer = tszPossibleBizarrePath;
  942. for (unsigned int i = 0; i < iNameSpaceLength; i++)
  943. {
  944. // Advance by the name space length...
  945. tszPointer = CharNext(tszPointer);
  946. }
  947. // We have to do this double copy since the strings would overlap
  948. _tcscpy(tszTempPath, tszPointer);
  949. _tcscpy(tszPossibleBizarrePath, tszTempPath);
  950. /*
  951. #ifdef _DEBUG
  952. _tprintf(TEXT("Bizarre module path changed to [%s]\n"), tszPossibleBizarrePath);
  953. #endif
  954. */
  955. } else
  956. if (( iExpandedSystemRoot > 2) && _tcsnicmp(tszPossibleBizarrePath, &tszExpandedSystemRoot[2], iExpandedSystemRoot-2) == 0)
  957. { // We need to match on the SystemRoot (without the SystemDrive)
  958. /*
  959. #ifdef _DEBUG
  960. _tprintf(TEXT("Matches [%s] sequence...\n"), tszSystemRoot);
  961. #endif
  962. */
  963. // This little algorithm assumes that the Drive Letter is a single char...
  964. _tcscpy(tszTempPath, tszExpandedSystemRoot);
  965. _tcscat(tszTempPath, &tszPossibleBizarrePath[iExpandedSystemRoot-2]);
  966. _tcscpy(tszPossibleBizarrePath, tszTempPath);
  967. /*
  968. #ifdef _DEBUG
  969. _tprintf(TEXT("Bizarre module path changed to [%s]\n"), tszPossibleBizarrePath);
  970. #endif
  971. */
  972. }
  973. return true;
  974. }
  975. bool CUtilityFunctions::FixupDeviceDriverPathIfNecessary(LPTSTR tszPossibleBaseDeviceDriverName, unsigned int iBufferLength)
  976. {
  977. TCHAR drive[_MAX_DRIVE], dir[_MAX_DIR], fname[_MAX_FNAME], ext[_MAX_EXT];
  978. // First, split the device driver name up into it's component parts...
  979. _tsplitpath(tszPossibleBaseDeviceDriverName, drive, dir, fname, ext);
  980. // Second, look to see if it's missing the drive and dir...
  981. if ( _tcsicmp(drive, TEXT("")) || _tcsicmp(dir, TEXT("")) )
  982. return true;
  983. // Third, create a new path... assuming that we'll find device drivers in the %systemroot%\system32\drivers directory
  984. TCHAR tszTempBuffer[_MAX_PATH];
  985. _tcscpy(tszTempBuffer, TEXT("%systemroot%\\system32\\drivers\\"));
  986. _tcscat(tszTempBuffer, tszPossibleBaseDeviceDriverName);
  987. ExpandEnvironmentStrings(tszTempBuffer, tszPossibleBaseDeviceDriverName, iBufferLength);
  988. return true;
  989. }
  990. // This function is provided in a Windows 2000 (NT 5.0) Version of IMAGEHLP.DLL. By adding
  991. // this function manually, I should run fine on NT 4.0 (and possibly Win9x)
  992. /*++
  993. Routine Description:
  994. The rules are:
  995. if Filename doesn't have a .dbg extension
  996. Look for
  997. 1. <SymbolPath>\Symbols\<ext>\<filename>.dbg
  998. 2. <SymbolPath>\Symbols\<ext>\<filename>.sym
  999. 3. <SymbolPath>\<ext>\<filename>.dbg
  1000. 4. <SymbolPath>\<ext>\<filename>.sym
  1001. 5. <SymbolPath>\<filename>.dbg
  1002. 6. <SymbolPath>\<filename>.sym
  1003. 7. <FileNamePath>\<filename>.dbg
  1004. 8. <FileNamePath>\<filename>.sym
  1005. if it does, skip the .sym lookup.
  1006. Arguments:
  1007. tszFileName - Supplies a file name in one of three forms: fully qualified,
  1008. <ext>\<filename>.dbg, or just filename.dbg
  1009. SymbolPath - semi-colon delimited
  1010. DebugFilePath -
  1011. Callback - May be NULL. Callback that indicates whether the Symbol file is valid, or whether
  1012. the function should continue searching for another Symbol file.
  1013. The callback returns TRUE if the Symbol file is valid, or FALSE if the function should
  1014. continue searching.
  1015. CallerData - May be NULL. Data passed to the callback.
  1016. Return Value:
  1017. The name of the Symbol file (either .dbg or .sym) and a handle to that file.
  1018. --*/
  1019. HANDLE CUtilityFunctions::FindDebugInfoFileEx(LPTSTR tszFileName, LPTSTR SymbolPath, LPTSTR DebugFilePath, PFIND_DEBUG_FILE_CALLBACK Callback, PVOID CallerData)
  1020. {
  1021. return fnFindDebugInfoFileEx(tszFileName,
  1022. SymbolPath,
  1023. DebugFilePath,
  1024. Callback,
  1025. CallerData,
  1026. 0);
  1027. }
  1028. /*++
  1029. Routine Description:
  1030. The rules are:
  1031. Look for
  1032. 1. <SymbolPath>\Symbols\<ext>\<filename>.dbg
  1033. 3. <SymbolPath>\<ext>\<filename>.dbg
  1034. 5. <SymbolPath>\<filename>.dbg
  1035. 7. <FileNamePath>\<filename>.dbg
  1036. Arguments:
  1037. tszFileName - Supplies a file name in one of three forms: fully qualified,
  1038. <ext>\<filename>.dbg, or just filename.dbg
  1039. SymbolPath - semi-colon delimited
  1040. DebugFilePath -
  1041. Callback - May be NULL. Callback that indicates whether the Symbol file
  1042. is valid, or whether
  1043. the function should continue searching for another Symbol file.
  1044. The callback returns TRUE if the Symbol file is valid, or FALSE if
  1045. the function should
  1046. continue searching.
  1047. CallerData - May be NULL. Data passed to the callback.
  1048. Flag - indicates that PDBs shouldn't be searched for
  1049. Return Value:
  1050. The name of the Symbol file (either .dbg or .sym) and a handle to that file.
  1051. --*/
  1052. HANDLE
  1053. CUtilityFunctions::fnFindDebugInfoFileEx(
  1054. IN LPTSTR tszFileName,
  1055. IN LPTSTR SymbolPath,
  1056. OUT LPTSTR DebugFilePath,
  1057. IN PFIND_DEBUG_FILE_CALLBACK Callback,
  1058. IN PVOID CallerData,
  1059. IN DWORD flag
  1060. )
  1061. {
  1062. HANDLE FileHandle = INVALID_HANDLE_VALUE;
  1063. LPTSTR ExpSymbolPath = NULL, SymPathStart, PathEnd;
  1064. DWORD ShareAttributes, cnt;
  1065. LPTSTR InitialPath = NULL, Sub1 = NULL, Sub2 = NULL;
  1066. TCHAR FilePath[_MAX_PATH + 1];
  1067. TCHAR Drive[_MAX_DRIVE], Dir[_MAX_DIR], FilePart[_MAX_FNAME], Ext[_MAX_EXT];
  1068. TCHAR *ExtDir;
  1069. BOOL found = FALSE;
  1070. BOOL symsrv = TRUE;
  1071. bool fDebugSearchPaths = g_lpProgramOptions->fDebugSearchPaths();
  1072. //if (OSVerInfo.dwPlatformId == VER_PLATFORM_WIN32_NT)
  1073. if (g_lpProgramOptions->IsRunningWindowsNT())
  1074. {
  1075. ShareAttributes = (FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE);
  1076. } else {
  1077. ShareAttributes = (FILE_SHARE_READ | FILE_SHARE_WRITE);
  1078. }
  1079. __try {
  1080. *DebugFilePath = '\0';
  1081. // Step 1. What do we have?
  1082. _tsplitpath(tszFileName, Drive, Dir, FilePart, Ext);
  1083. if (!_tcsicmp(Ext, TEXT(".dbg"))) {
  1084. // We got a filename of the form: ext\filename.dbg. Dir holds the extension already.
  1085. ExtDir = Dir;
  1086. } else {
  1087. // Otherwise, skip the period and null out the Dir.
  1088. ExtDir = CharNext(Ext);
  1089. }
  1090. ExpSymbolPath = ExpandPath(SymbolPath);
  1091. SymPathStart = ExpSymbolPath;
  1092. cnt = 0;
  1093. do {
  1094. PathEnd = _tcschr( SymPathStart, ';' );
  1095. if (PathEnd) {
  1096. *PathEnd = '\0';
  1097. }
  1098. if (!_tcsnicmp(SymPathStart, TEXT("SYMSRV*"), 7)) {
  1099. *DebugFilePath = 0;
  1100. if (symsrv && CallerData)
  1101. {
  1102. _tcscpy(FilePath, FilePart);
  1103. _tcscat(FilePath, TEXT(".dbg"));
  1104. CModuleInfo * lpModuleInfo = (CModuleInfo *)CallerData;
  1105. if (fDebugSearchPaths)
  1106. {
  1107. _tprintf(TEXT("DBG Search - SYMSRV [%s,0x%x,0x%x]\n"),
  1108. SymPathStart,
  1109. lpModuleInfo->GetPEImageTimeDateStamp(),
  1110. lpModuleInfo->GetPEImageSizeOfImage());
  1111. }
  1112. GetSymbolFileFromServer(SymPathStart,
  1113. FilePath,
  1114. lpModuleInfo->GetPEImageTimeDateStamp(),
  1115. lpModuleInfo->GetPEImageSizeOfImage(),
  1116. 0,
  1117. DebugFilePath);
  1118. symsrv = FALSE;
  1119. }
  1120. } else {
  1121. switch (cnt) {
  1122. case 0: // <SymbolPath>\symbols\<ext>\<filename>.ext
  1123. InitialPath = SymPathStart;
  1124. Sub1 = TEXT("symbols");
  1125. Sub2 = ExtDir;
  1126. break;
  1127. case 1: // <SymbolPath>\<ext>\<filename>.ext
  1128. InitialPath = SymPathStart;
  1129. Sub1 = TEXT("");
  1130. Sub2 = ExtDir;
  1131. break;
  1132. case 2: // <SymbolPath>\<filename>.ext
  1133. InitialPath = SymPathStart;
  1134. Sub1 = TEXT("");
  1135. Sub2 = TEXT("");
  1136. break;
  1137. case 3: // <FileNamePath>\<filename>.ext - A.K.A. what was passed to us
  1138. InitialPath = Drive;
  1139. Sub1 = TEXT("");
  1140. Sub2 = Dir;
  1141. // this stops us from checking out everything in the sympath
  1142. cnt++;
  1143. break;
  1144. }
  1145. // build fully-qualified filepath to look for
  1146. _tcscpy(FilePath, InitialPath);
  1147. EnsureTrailingBackslash(FilePath);
  1148. _tcscat(FilePath, Sub1);
  1149. EnsureTrailingBackslash(FilePath);
  1150. _tcscat(FilePath, Sub2);
  1151. EnsureTrailingBackslash(FilePath);
  1152. _tcscat(FilePath, FilePart);
  1153. _tcscpy(DebugFilePath, FilePath);
  1154. _tcscat(DebugFilePath, TEXT(".dbg"));
  1155. }
  1156. // try to open the file
  1157. if (*DebugFilePath) {
  1158. //#ifdef _DEBUG
  1159. // _tprintf(TEXT("FindDebugInfoFileEx-> Looking for %s... "), DebugFilePath);
  1160. //#endif
  1161. if (fDebugSearchPaths)
  1162. {
  1163. _tprintf(TEXT("DBG Search - Search here [%s]\n"), DebugFilePath);
  1164. }
  1165. FileHandle = CreateFile(DebugFilePath,
  1166. GENERIC_READ,
  1167. ShareAttributes,
  1168. NULL,
  1169. OPEN_EXISTING,
  1170. 0,
  1171. NULL);
  1172. // if the file opens, bail from this loop
  1173. if (FileHandle != INVALID_HANDLE_VALUE)
  1174. {
  1175. found = TRUE;
  1176. // If a callback exists... call it...
  1177. if (!Callback)
  1178. {
  1179. break;
  1180. } else if (Callback(FileHandle, DebugFilePath, CallerData))
  1181. {
  1182. break;
  1183. } else {
  1184. //#ifdef _DEBUG
  1185. // _tprintf(TEXT("mismatched timestamp\n"));
  1186. //#endif
  1187. CloseHandle(FileHandle);
  1188. FileHandle = INVALID_HANDLE_VALUE;
  1189. }
  1190. }
  1191. // if file is open, bail from this loop too - else continue
  1192. if (FileHandle != INVALID_HANDLE_VALUE)
  1193. break;
  1194. }
  1195. // go to next item in the sympath
  1196. if (PathEnd) {
  1197. *PathEnd = ';';
  1198. SymPathStart = PathEnd + 1;
  1199. symsrv = TRUE;
  1200. } else {
  1201. SymPathStart = ExpSymbolPath;
  1202. cnt++;
  1203. }
  1204. } while (cnt < 4);
  1205. } __except(EXCEPTION_EXECUTE_HANDLER)
  1206. {
  1207. if (FileHandle != INVALID_HANDLE_VALUE) {
  1208. CloseHandle(FileHandle);
  1209. }
  1210. FileHandle = INVALID_HANDLE_VALUE;
  1211. }
  1212. if (ExpSymbolPath)
  1213. {
  1214. delete [] ExpSymbolPath;
  1215. ExpSymbolPath = NULL;
  1216. }
  1217. if (FileHandle == INVALID_HANDLE_VALUE)
  1218. {
  1219. FileHandle = NULL;
  1220. DebugFilePath[0] = '\0';
  1221. }
  1222. if (!FileHandle // if we didn't get the right file...
  1223. && found // but we found some file...
  1224. && (flag & fdifRECURSIVE)) // and we were told to run recursively...
  1225. {
  1226. // try again without timestamp checking
  1227. FileHandle = fnFindDebugInfoFileEx(tszFileName,
  1228. SymbolPath,
  1229. FilePath,
  1230. NULL,
  1231. 0,
  1232. flag);
  1233. if (FileHandle && FileHandle != INVALID_HANDLE_VALUE)
  1234. _tcscpy(DebugFilePath, FilePath);
  1235. }
  1236. return FileHandle;
  1237. }
  1238. void
  1239. CUtilityFunctions::EnsureTrailingBackslash(
  1240. LPTSTR tsz
  1241. )
  1242. {
  1243. int i;
  1244. i = _tcslen(tsz);
  1245. if (!i)
  1246. return;
  1247. if (tsz[i - 1] == '\\')
  1248. return;
  1249. tsz[i] = '\\';
  1250. tsz[i + 1] = '\0';
  1251. }
  1252. bool CUtilityFunctions::GetSymbolFileFromServer(
  1253. LPCTSTR tszServerInfo,
  1254. LPCTSTR tszFileName,
  1255. DWORD num1,
  1256. DWORD num2,
  1257. DWORD num3,
  1258. LPTSTR tszFilePath
  1259. )
  1260. {
  1261. TCHAR tszSrvName[_MAX_PATH * 2];
  1262. LPTSTR tszParams;
  1263. bool rc = false;
  1264. LPSTR szParams = NULL;
  1265. char szFilePath[_MAX_PATH+1]; // Temporary file buffer (for returned data)
  1266. LPSTR szFileName = NULL;
  1267. // It's a little extra work... but let's extract the SYMSRV DLL name
  1268. // so we can see if we're already loaded (by name)..
  1269. // Copy the new server name in...
  1270. _tcscpy(tszSrvName, &tszServerInfo[7]);
  1271. if (!(*tszSrvName))
  1272. goto cleanup;
  1273. // Parameters start after the separator
  1274. tszParams = _tcschr(tszSrvName, '*');
  1275. if (!tszParams)
  1276. goto cleanup;
  1277. // Null terminate and advance...
  1278. *(tszParams++) = '\0';
  1279. // Determine if this SYMSRV compatible DLL is the same
  1280. // as the one we have loaded already... (if one is loaded)
  1281. if( !g_lpDelayLoad->GetCurrentSymSrvDLL() ||
  1282. _tcscmp(g_lpDelayLoad->GetCurrentSymSrvDLL(), tszSrvName))
  1283. {
  1284. if (!g_lpDelayLoad->Initialize_SYMSRV(tszSrvName))
  1285. goto cleanup;
  1286. }
  1287. tszParams = _tcschr(tszServerInfo, '*');
  1288. if (!tszParams)
  1289. goto cleanup;
  1290. tszParams = _tcschr(tszParams+1, '*');
  1291. if (!tszParams)
  1292. goto cleanup;
  1293. // Ready to make the call... need to convert to lame ANSI for the function call...
  1294. szParams = CopyTSTRStringToAnsi(tszParams+1);
  1295. szFileName = CopyTSTRStringToAnsi(tszFileName);
  1296. rc = (TRUE == g_lpDelayLoad->SymbolServer(szParams, szFileName, num1, num2, num3, szFilePath));
  1297. if (rc)
  1298. {
  1299. tszFilePath = CopyAnsiStringToTSTR(szFilePath, tszFilePath, _MAX_PATH+1);
  1300. }
  1301. cleanup:
  1302. if (szParams)
  1303. delete [] szParams;
  1304. if (szFileName)
  1305. delete [] szFileName;
  1306. return rc;
  1307. }
  1308. //
  1309. // Taken from UTF8.CPP (from the VC Linker code)
  1310. //
  1311. #define BIT7(a) ((a) & 0x80)
  1312. #define BIT6(a) ((a) & 0x40)
  1313. #define LOWER_6_BIT(u) ((u) & 0x003f)
  1314. #define HIGH_SURROGATE_START 0xd800
  1315. #define LOW_SURROGATE_START 0xdc00
  1316. ////////////////////////////////////////////////////////////////////////////
  1317. //
  1318. // UTF8ToUnicode
  1319. //
  1320. // Maps a UTF-8 character string to its wide character string counterpart.
  1321. //
  1322. // 02-06-96 JulieB Created.
  1323. ////////////////////////////////////////////////////////////////////////////
  1324. size_t CUtilityFunctions::UTF8ToUnicode(
  1325. LPCSTR lpSrcStr,
  1326. LPWSTR lpDestStr,
  1327. size_t cchDest)
  1328. {
  1329. return UTF8ToUnicodeCch(lpSrcStr, strlen(lpSrcStr) + 1, lpDestStr, cchDest);
  1330. }
  1331. #pragma warning( push )
  1332. #pragma warning( disable : 4244 ) // conversion from 'int' to 'unsigned short', possible loss of data
  1333. size_t CUtilityFunctions::UTF8ToUnicodeCch(
  1334. LPCSTR lpSrcStr,
  1335. size_t cchSrc,
  1336. LPWSTR lpDestStr,
  1337. size_t cchDest)
  1338. {
  1339. int nTB = 0; // # trail bytes to follow
  1340. size_t cchWC = 0; // # of Unicode code points generated
  1341. LPCSTR pUTF8 = lpSrcStr;
  1342. DWORD dwSurrogateChar = 0; // Full surrogate char
  1343. BOOL bSurrogatePair = FALSE; // Indicate we'r collecting a surrogate pair
  1344. char UTF8;
  1345. while ((cchSrc--) && ((cchDest == 0) || (cchWC < cchDest)))
  1346. {
  1347. //
  1348. // See if there are any trail bytes.
  1349. //
  1350. if (BIT7(*pUTF8) == 0)
  1351. {
  1352. //
  1353. // Found ASCII.
  1354. //
  1355. if (cchDest)
  1356. {
  1357. lpDestStr[cchWC] = (WCHAR)*pUTF8;
  1358. }
  1359. bSurrogatePair = FALSE;
  1360. cchWC++;
  1361. }
  1362. else if (BIT6(*pUTF8) == 0)
  1363. {
  1364. //
  1365. // Found a trail byte.
  1366. // Note : Ignore the trail byte if there was no lead byte.
  1367. //
  1368. if (nTB != 0)
  1369. {
  1370. //
  1371. // Decrement the trail byte counter.
  1372. //
  1373. nTB--;
  1374. if (bSurrogatePair)
  1375. {
  1376. dwSurrogateChar <<= 6;
  1377. dwSurrogateChar |= LOWER_6_BIT(*pUTF8);
  1378. if (nTB == 0)
  1379. {
  1380. if (cchDest)
  1381. {
  1382. if ((cchWC + 1) < cchDest)
  1383. {
  1384. lpDestStr[cchWC] = (WCHAR)
  1385. (((dwSurrogateChar - 0x10000) >> 10) + HIGH_SURROGATE_START);
  1386. lpDestStr[cchWC+1] = (WCHAR)
  1387. ((dwSurrogateChar - 0x10000)%0x400 + LOW_SURROGATE_START);
  1388. }
  1389. }
  1390. cchWC += 2;
  1391. bSurrogatePair = FALSE;
  1392. }
  1393. }
  1394. else
  1395. {
  1396. //
  1397. // Make room for the trail byte and add the trail byte
  1398. // value.
  1399. //
  1400. if (cchDest)
  1401. {
  1402. lpDestStr[cchWC] <<= 6;
  1403. lpDestStr[cchWC] |= LOWER_6_BIT(*pUTF8);
  1404. }
  1405. if (nTB == 0)
  1406. {
  1407. //
  1408. // End of sequence. Advance the output counter.
  1409. //
  1410. cchWC++;
  1411. }
  1412. }
  1413. }
  1414. else
  1415. {
  1416. // error - not expecting a trail byte
  1417. bSurrogatePair = FALSE;
  1418. }
  1419. }
  1420. else
  1421. {
  1422. //
  1423. // Found a lead byte.
  1424. //
  1425. if (nTB > 0)
  1426. {
  1427. //
  1428. // Error - previous sequence not finished.
  1429. //
  1430. nTB = 0;
  1431. bSurrogatePair = FALSE;
  1432. cchWC++;
  1433. }
  1434. else
  1435. {
  1436. //
  1437. // Calculate the number of bytes to follow.
  1438. // Look for the first 0 from left to right.
  1439. //
  1440. UTF8 = *pUTF8;
  1441. while (BIT7(UTF8) != 0)
  1442. {
  1443. UTF8 <<= 1;
  1444. nTB++;
  1445. }
  1446. //
  1447. // If this is a surrogate unicode pair
  1448. //
  1449. if (nTB == 4)
  1450. {
  1451. dwSurrogateChar = UTF8 >> nTB;
  1452. bSurrogatePair = TRUE;
  1453. }
  1454. //
  1455. // Store the value from the first byte and decrement
  1456. // the number of bytes to follow.
  1457. //
  1458. if (cchDest)
  1459. {
  1460. lpDestStr[cchWC] = UTF8 >> nTB;
  1461. }
  1462. nTB--;
  1463. }
  1464. }
  1465. pUTF8++;
  1466. }
  1467. //
  1468. // Make sure the destination buffer was large enough.
  1469. //
  1470. if (cchDest && cchSrc != (size_t)-1)
  1471. {
  1472. SetLastError(ERROR_INSUFFICIENT_BUFFER);
  1473. return 0;
  1474. }
  1475. //
  1476. // Return the number of Unicode characters written.
  1477. //
  1478. return cchWC;
  1479. }
  1480. #pragma warning( pop )