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

523 lines
16 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "help.h"
  4. #define DWORDUP(x) (((x)+3)&~3)
  5. #define VerKeyToValue(lpKey) (lpKey + DWORDUP(lstrlen(lpKey)+1))
  6. #pragma warning(disable: 4200) // zero size array in struct
  7. // magic undoced explort from version.dll
  8. STDAPI_(BOOL) VerQueryValueIndexW(const void *pBlock, LPTSTR lpSubBlock, DWORD dwIndex, void **ppBuffer, void **ppValue, PUINT puLen);
  9. #ifdef UNICODE
  10. #define VerQueryValueIndex VerQueryValueIndexW
  11. #endif
  12. typedef struct
  13. {
  14. WORD wTotLen;
  15. WORD wValLen;
  16. TCHAR szKey[];
  17. } SHELLVERBLOCK, *LPSHELLVERBLOCK;
  18. // Following code is copied from fileman\wfdlgs2.c
  19. // The following data structure associates a version stamp datum
  20. // name (which is not localized) with a string ID. This is so we
  21. // can show translations of these names to the user.
  22. struct vertbl {
  23. TCHAR const *pszName;
  24. short idString;
  25. };
  26. // Note that version stamp datum names are NEVER internationalized,
  27. // so the following literal strings are just fine.
  28. const struct vertbl vernames[] = {
  29. // For the first NUM_SPECIAL_STRINGS, the second column is the dialog ID.
  30. { TEXT("LegalCopyright"), IDD_VERSION_COPYRIGHT },
  31. { TEXT("FileDescription"), IDD_VERSION_DESCRIPTION },
  32. // For the rest, the second column is the string ID.
  33. { TEXT("FileVersion"), IDS_VN_FILEVERSION },
  34. { TEXT("Comments"), IDS_VN_COMMENTS },
  35. { TEXT("CompanyName"), IDS_VN_COMPANYNAME },
  36. { TEXT("InternalName"), IDS_VN_INTERNALNAME },
  37. { TEXT("LegalTrademarks"), IDS_VN_LEGALTRADEMARKS },
  38. { TEXT("OriginalFilename"), IDS_VN_ORIGINALFILENAME },
  39. { TEXT("PrivateBuild"), IDS_VN_PRIVATEBUILD },
  40. { TEXT("ProductName"), IDS_VN_PRODUCTNAME },
  41. { TEXT("ProductVersion"), IDS_VN_PRODUCTVERSION },
  42. { TEXT("SpecialBuild"), IDS_VN_SPECIALBUILD }
  43. };
  44. #define NUM_SPECIAL_STRINGS 2
  45. #define VERSTR_MANDATORY TEXT("FileVersion")
  46. typedef struct { // vp
  47. PROPSHEETPAGE psp;
  48. HWND hDlg;
  49. LPTSTR pVerBuffer; /* pointer to version data */
  50. TCHAR szVersionKey[60]; /* big enough for anything we need */
  51. struct _VERXLATE
  52. {
  53. WORD wLanguage;
  54. WORD wCodePage;
  55. } *lpXlate; /* ptr to translations data */
  56. int cXlate; /* count of translations */
  57. LPTSTR pszXlate;
  58. int cchXlateString;
  59. TCHAR szFile[MAX_PATH];
  60. } VERPROPSHEETPAGE, * LPVERPROPSHEETPAGE;
  61. #define VER_KEY_END 25 /* length of "\StringFileInfo\xxxxyyyy\" */
  62. /* (not localized) */
  63. #define MAXMESSAGELEN (50 + MAX_PATH * 2)
  64. /*
  65. Gets a particular datum about a file. The file's version info
  66. should have already been loaded by GetVersionInfo. If no datum
  67. by the specified name is available, NULL is returned. The name
  68. specified should be just the name of the item itself; it will
  69. be concatenated onto "\StringFileInfo\xxxxyyyy\" automatically.
  70. Version datum names are not localized, so it's OK to pass literals
  71. such as "FileVersion" to this function.
  72. Note that since the returned datum is in a global memory block,
  73. the return value of this function is LPSTR, not PSTR.
  74. */
  75. LPTSTR GetVersionDatum(LPVERPROPSHEETPAGE pvp, LPCTSTR pszName)
  76. {
  77. UINT cbValue = 0;
  78. LPTSTR lpValue;
  79. HRESULT hr;
  80. if (!pvp->pVerBuffer)
  81. return NULL;
  82. hr = StringCchCopy(pvp->szVersionKey + VER_KEY_END, ARRAYSIZE(pvp->szVersionKey) - VER_KEY_END, pszName);
  83. if(SUCCEEDED(hr))
  84. {
  85. VerQueryValue(pvp->pVerBuffer, pvp->szVersionKey, (void **)&lpValue, &cbValue);
  86. return (cbValue != 0) ? lpValue : NULL;
  87. }
  88. else
  89. {
  90. return NULL;
  91. }
  92. }
  93. /*
  94. Frees global version data about a file. After this call, all
  95. GetVersionDatum calls will return NULL. To avoid memory leaks,
  96. always call this before the main properties dialog exits.
  97. */
  98. void FreeVersionInfo(LPVERPROPSHEETPAGE pvp)
  99. {
  100. if (pvp->pVerBuffer)
  101. {
  102. GlobalFree(pvp->pVerBuffer);
  103. pvp->pVerBuffer = NULL;
  104. }
  105. if (pvp->pszXlate)
  106. {
  107. LocalFree((HLOCAL)(HANDLE)pvp->pszXlate);
  108. pvp->pszXlate = NULL;
  109. }
  110. pvp->lpXlate = NULL;
  111. }
  112. /*
  113. Initialize version information for the properties dialog. The
  114. above global variables are initialized by this function, and
  115. remain valid (for the specified file only) until FreeVersionInfo
  116. is called.
  117. The first language we try will be the first item in the
  118. "\VarFileInfo\Translations" section; if there's nothing there,
  119. we try the one coded into the IDS_FILEVERSIONKEY resource string.
  120. If we can't even load that, we just use English (040904E4). We
  121. also try English with a null codepage (04090000) since many apps
  122. were stamped according to an old spec which specified this as
  123. the required language instead of 040904E4.
  124. GetVersionInfo returns TRUE if the version info was read OK,
  125. otherwise FALSE. If the return is FALSE, the buffer may still
  126. have been allocated; always call FreeVersionInfo to be safe.
  127. pszPath is modified by this call (pszName is appended).
  128. */
  129. BOOL GetVersionInfo(LPVERPROPSHEETPAGE pvp, LPCTSTR pszPath)
  130. {
  131. UINT cbValue = 0;
  132. LPTSTR pszValue = NULL;
  133. DWORD dwHandle; /* version subsystem handle */
  134. DWORD dwVersionSize; /* size of the version data */
  135. FreeVersionInfo(pvp); /* free old version buffer */
  136. // cast const -> non const for bad API def
  137. dwVersionSize = GetFileVersionInfoSize((LPTSTR)pszPath, &dwHandle);
  138. if (dwVersionSize == 0L)
  139. return FALSE; /* no version info */
  140. pvp->pVerBuffer = GlobalAlloc(GPTR, dwVersionSize);
  141. if (pvp->pVerBuffer == NULL)
  142. return FALSE;
  143. // cast const -> non const for bad API def
  144. if (!GetFileVersionInfo((LPTSTR)pszPath, dwHandle, dwVersionSize, pvp->pVerBuffer))
  145. {
  146. return FALSE;
  147. }
  148. // Look for translations
  149. if (VerQueryValue(pvp->pVerBuffer, TEXT("\\VarFileInfo\\Translation"), (void **)&pvp->lpXlate, &cbValue)
  150. && cbValue)
  151. {
  152. pvp->cXlate = cbValue / sizeof(DWORD);
  153. pvp->cchXlateString = pvp->cXlate * 64; /* figure 64 chars per lang name */
  154. pvp->pszXlate = (LPTSTR)(void*)LocalAlloc(LPTR, pvp->cchXlateString*sizeof(TCHAR));
  155. // failure of above will be handled later
  156. }
  157. else
  158. {
  159. pvp->lpXlate = NULL;
  160. }
  161. // Try same language as this program
  162. if (LoadString(HINST_THISDLL, IDS_VN_FILEVERSIONKEY, pvp->szVersionKey, ARRAYSIZE(pvp->szVersionKey)))
  163. {
  164. if (GetVersionDatum(pvp, VERSTR_MANDATORY))
  165. {
  166. return TRUE;
  167. }
  168. }
  169. // Try first language this supports
  170. if (pvp->lpXlate)
  171. {
  172. if(FAILED(StringCchPrintf(pvp->szVersionKey, ARRAYSIZE(pvp->szVersionKey), TEXT("\\StringFileInfo\\%04X%04X\\"),
  173. pvp->lpXlate[0].wLanguage, pvp->lpXlate[0].wCodePage)))
  174. {
  175. goto errRet;
  176. }
  177. if (GetVersionDatum(pvp, VERSTR_MANDATORY)) /* a required field */
  178. {
  179. return TRUE;
  180. }
  181. }
  182. // try English, unicode code page
  183. if(FAILED(StringCchCopy(pvp->szVersionKey, ARRAYSIZE(pvp->szVersionKey),TEXT("\\StringFileInfo\\040904B0\\"))))
  184. {
  185. goto errRet;
  186. }
  187. if (GetVersionDatum(pvp, VERSTR_MANDATORY))
  188. {
  189. return TRUE;
  190. }
  191. // try English
  192. if(FAILED(StringCchCopy(pvp->szVersionKey, ARRAYSIZE(pvp->szVersionKey), TEXT("\\StringFileInfo\\040904E4\\"))))
  193. {
  194. goto errRet;
  195. }
  196. if (GetVersionDatum(pvp, VERSTR_MANDATORY))
  197. {
  198. return TRUE;
  199. }
  200. // try English, null codepage
  201. if(FAILED(StringCchCopy(pvp->szVersionKey, ARRAYSIZE(pvp->szVersionKey), TEXT("\\StringFileInfo\\04090000\\"))))
  202. {
  203. goto errRet;
  204. }
  205. if (GetVersionDatum(pvp, VERSTR_MANDATORY))
  206. {
  207. return TRUE;
  208. }
  209. // Could not find FileVersion info in a reasonable format
  210. errRet:
  211. GlobalFree(pvp->pVerBuffer);
  212. pvp->pVerBuffer = NULL;
  213. LocalFree(pvp->pszXlate);
  214. pvp->pszXlate = NULL;
  215. return FALSE;
  216. }
  217. /*
  218. Fills the version key listbox with all available keys in the
  219. StringFileInfo block, and sets the version value text to the
  220. value of the first item.
  221. */
  222. void FillVersionList(LPVERPROPSHEETPAGE pvp)
  223. {
  224. LPTSTR pszName;
  225. LPTSTR pszValue;
  226. TCHAR szStringBase[VER_KEY_END+1];
  227. int i, j, idx;
  228. TCHAR szMessage[MAXMESSAGELEN+1];
  229. UINT uOffset, cbValue;
  230. HWND hwndLB = GetDlgItem(pvp->hDlg, IDD_VERSION_KEY);
  231. ListBox_ResetContent(hwndLB);
  232. for (i=0; i<NUM_SPECIAL_STRINGS; ++i)
  233. {
  234. SetDlgItemText(pvp->hDlg, vernames[i].idString, szNULL);
  235. }
  236. pvp->szVersionKey[VER_KEY_END] = 0; /* don't copy too much */
  237. StringCchCopy(szStringBase, ARRAYSIZE(szStringBase), pvp->szVersionKey); /* copy to our buffer */
  238. szStringBase[VER_KEY_END - 1] = 0; /* strip the backslash */
  239. // Note: The Nt Version of version.dll has other exports. If/When they are
  240. // available in Win version then we can remove this section...
  241. // Get the binary file version from the VS_FIXEDFILEINFO
  242. {
  243. VS_FIXEDFILEINFO *pffi;
  244. if (VerQueryValue(pvp->pVerBuffer, TEXT("\\"), (void **)&pffi, &cbValue) && cbValue)
  245. {
  246. TCHAR szString[128];
  247. // display the binary version info, not the useless
  248. // string version (that can be out of sync)
  249. StringCchPrintf(szString, ARRAYSIZE(szString), TEXT("%d.%d.%d.%d"),
  250. HIWORD(pffi->dwFileVersionMS),
  251. LOWORD(pffi->dwFileVersionMS),
  252. HIWORD(pffi->dwFileVersionLS),
  253. LOWORD(pffi->dwFileVersionLS));
  254. SetDlgItemText(pvp->hDlg, IDD_VERSION_FILEVERSION, szString);
  255. }
  256. }
  257. //
  258. // Now iterate through all of the strings
  259. //
  260. for (j = 0; ; j++)
  261. {
  262. if (!VerQueryValueIndex(pvp->pVerBuffer, szStringBase, j, &pszName, &pszValue, &cbValue))
  263. break;
  264. for (i = 0; i < ARRAYSIZE(vernames); i++)
  265. {
  266. if (!lstrcmp(vernames[i].pszName, pszName))
  267. {
  268. break;
  269. }
  270. }
  271. if (i < NUM_SPECIAL_STRINGS)
  272. {
  273. SetDlgItemText(pvp->hDlg, vernames[i].idString, pszValue);
  274. }
  275. else
  276. {
  277. if (i == ARRAYSIZE(vernames) ||
  278. !LoadString(HINST_THISDLL, vernames[i].idString, szMessage, ARRAYSIZE(szMessage)))
  279. {
  280. StringCchCopy(szMessage, ARRAYSIZE(szMessage), pszName);
  281. }
  282. idx = ListBox_AddString(hwndLB, szMessage);
  283. if (idx != LB_ERR)
  284. {
  285. ListBox_SetItemData(hwndLB, idx, (DWORD_PTR)pszValue);
  286. }
  287. }
  288. }
  289. // Now look at the \VarFileInfo\Translations section and add an
  290. // item for the language(s) this file supports.
  291. if (pvp->lpXlate == NULL || pvp->pszXlate == NULL)
  292. return;
  293. if (!LoadString(HINST_THISDLL, (pvp->cXlate == 1) ? IDS_VN_LANGUAGE : IDS_VN_LANGUAGES,
  294. szMessage, ARRAYSIZE(szMessage)))
  295. return;
  296. idx = ListBox_AddString(hwndLB, szMessage);
  297. if (idx == LB_ERR)
  298. return;
  299. pvp->pszXlate[0] = 0;
  300. uOffset = 0;
  301. for (i = 0; i < pvp->cXlate; i++) {
  302. if (uOffset + 2 > (UINT)pvp->cchXlateString)
  303. break;
  304. if (i != 0) {
  305. StringCchCat(pvp->pszXlate, pvp->cchXlateString, TEXT(", "));
  306. uOffset += 2; // skip over ", "
  307. }
  308. if (VerLanguageName(pvp->lpXlate[i].wLanguage, pvp->pszXlate + uOffset, pvp->cchXlateString - uOffset) >
  309. (DWORD)(pvp->cchXlateString - uOffset))
  310. break;
  311. uOffset += lstrlen(pvp->pszXlate + uOffset);
  312. }
  313. pvp->pszXlate[pvp->cchXlateString - 1] = 0;
  314. ListBox_SetItemData(hwndLB, idx, (LPARAM)(LPTSTR)pvp->pszXlate);
  315. ListBox_SetCurSel(hwndLB, 0);
  316. FORWARD_WM_COMMAND(pvp->hDlg, IDD_VERSION_KEY, hwndLB, LBN_SELCHANGE, PostMessage);
  317. }
  318. //
  319. // Function: _UpdateVersionPrsht, private
  320. //
  321. // Descriptions:
  322. // This function fills fields of the "version" dialog box (a page of
  323. // a property sheet) with attributes of the associated file.
  324. //
  325. // Returns:
  326. // TRUE, if successfully done; FALSE, otherwise.
  327. //
  328. // History:
  329. // 01-06-93 Shrikant Created
  330. //
  331. BOOL _UpdateVersionPrsht(LPVERPROPSHEETPAGE pvp)
  332. {
  333. if (GetVersionInfo(pvp, pvp->szFile)) /* changes szPath */
  334. FillVersionList(pvp);
  335. return TRUE;
  336. }
  337. void _VersionPrshtCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify)
  338. {
  339. LPTSTR pszValue;
  340. int idx;
  341. switch (id)
  342. {
  343. case IDD_VERSION_KEY:
  344. if (codeNotify != LBN_SELCHANGE)
  345. {
  346. break;
  347. }
  348. idx = ListBox_GetCurSel(hwndCtl);
  349. pszValue = (LPTSTR)ListBox_GetItemData(hwndCtl, idx);
  350. if (pszValue)
  351. {
  352. SetDlgItemText(hwnd, IDD_VERSION_VALUE, pszValue);
  353. }
  354. break;
  355. }
  356. }
  357. // Array for context help:
  358. static const DWORD aVersionHelpIds[] = {
  359. IDD_VERSION_FILEVERSION, IDH_FPROP_VER_ABOUT,
  360. IDD_VERSION_DESCRIPTION, IDH_FPROP_VER_ABOUT,
  361. IDD_VERSION_COPYRIGHT, IDH_FPROP_VER_ABOUT,
  362. IDD_VERSION_FRAME, IDH_FPROP_VER_INFO,
  363. IDD_VERSION_KEY, IDH_FPROP_VER_INFO,
  364. IDD_VERSION_VALUE, IDH_FPROP_VER_INFO,
  365. 0, 0
  366. };
  367. BOOL_PTR CALLBACK _VersionPrshtDlgProc(HWND hDlg, UINT uMessage, WPARAM wParam, LPARAM lParam)
  368. {
  369. LPVERPROPSHEETPAGE pvp = (LPVERPROPSHEETPAGE)GetWindowLongPtr(hDlg, DWLP_USER);
  370. switch (uMessage)
  371. {
  372. case WM_INITDIALOG:
  373. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  374. pvp = (LPVERPROPSHEETPAGE)lParam;
  375. pvp->hDlg = hDlg;
  376. break;
  377. case WM_DESTROY:
  378. FreeVersionInfo(pvp); // free anything we created
  379. break;
  380. case WM_HELP:
  381. WinHelp((HWND) ((LPHELPINFO) lParam)->hItemHandle, NULL, HELP_WM_HELP,
  382. (ULONG_PTR) (LPTSTR) aVersionHelpIds);
  383. break;
  384. case WM_CONTEXTMENU: // right mouse click
  385. WinHelp((HWND) wParam, NULL, HELP_CONTEXTMENU,
  386. (ULONG_PTR) (LPTSTR) aVersionHelpIds);
  387. break;
  388. case WM_NOTIFY:
  389. switch (((NMHDR *)lParam)->code)
  390. {
  391. case PSN_SETACTIVE:
  392. _UpdateVersionPrsht(pvp);
  393. break;
  394. }
  395. break;
  396. case WM_COMMAND:
  397. HANDLE_WM_COMMAND(hDlg, wParam, lParam, _VersionPrshtCommand);
  398. break;
  399. default:
  400. return FALSE;
  401. }
  402. return TRUE;
  403. }
  404. //
  405. // creates a property sheet for the "version" page which shows version information.
  406. //
  407. STDAPI_(void) AddVersionPage(LPCTSTR pszFile, LPFNADDPROPSHEETPAGE pfnAddPage, LPARAM lParam)
  408. {
  409. DWORD dwAttr = GetFileAttributes(pszFile);
  410. if (0xFFFFFFFF != dwAttr && 0 == (dwAttr & FILE_ATTRIBUTE_OFFLINE) /*avoid HSM recall*/)
  411. {
  412. DWORD dwVerLen, dwVerHandle;
  413. VERPROPSHEETPAGE vp = {0};
  414. if(SUCCEEDED(StringCchCopy(vp.szFile, ARRAYSIZE(vp.szFile), pszFile))) // silent failure appears OK from above
  415. {
  416. dwVerLen = GetFileVersionInfoSize(vp.szFile, &dwVerHandle);
  417. if (dwVerLen)
  418. {
  419. HPROPSHEETPAGE hpage;
  420. vp.psp.dwSize = sizeof(VERPROPSHEETPAGE); // extra data
  421. vp.psp.dwFlags = PSP_DEFAULT;
  422. vp.psp.hInstance = HINST_THISDLL;
  423. vp.psp.pszTemplate = MAKEINTRESOURCE(DLG_VERSION);
  424. vp.psp.pfnDlgProc = _VersionPrshtDlgProc;
  425. hpage = CreatePropertySheetPage(&vp.psp);
  426. if (hpage)
  427. if (!pfnAddPage(hpage, lParam))
  428. DestroyPropertySheetPage(hpage);
  429. }
  430. }
  431. }
  432. }