2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
5 ////////////////////////////////////////////////////////////////////////////////
12 #include "assemblynativeresource.h"
16 #define CP_WINUNICODE 1200
19 #ifndef MAKEINTRESOURCE
20 #define MAKEINTRESOURCE MAKEINTRESOURCEW
36 for (i = 0; i < NUM_VALUES; i++)
38 for (i = 0; i < NUM_VALUES; i++)
59 for (i = 0; i < NUM_VALUES; i++)
61 for (i = 0; i < NUM_VALUES; i++)
72 //*****************************************************************************
73 // Initializes the structures with version information.
74 //*****************************************************************************
75 VOID Win32Res::SetInfo(
79 LPCWSTR szDescription,
84 LPCWSTR szProductVersion,
85 LPCWSTR szFileVersion,
91 _ASSERTE(szFile != NULL);
94 if (szIconName && szIconName[0] != 0)
95 m_Icon = szIconName; // a non-mepty string
97 #define NonNull(sz) (sz == NULL || *sz == W('\0') ? W(" ") : sz)
98 m_Values[v_Description] = NonNull(szDescription);
99 m_Values[v_Title] = NonNull(szTitle);
100 m_Values[v_Copyright] = NonNull(szCopyright);
101 m_Values[v_Trademark] = NonNull(szTrademark);
102 m_Values[v_Product] = NonNull(szProduct);
103 m_Values[v_ProductVersion] = NonNull(szProductVersion);
104 m_Values[v_Company] = NonNull(szCompany);
105 m_Values[v_FileVersion] = NonNull(szFileVersion);
112 VOID Win32Res::MakeResFile(const void **pData, DWORD *pcbData)
114 STANDARD_VM_CONTRACT;
116 static const RESOURCEHEADER magic = { 0x00000000, 0x00000020, 0xFFFF, 0x0000, 0xFFFF, 0x0000,
117 0x00000000, 0x0000, 0x0000, 0x00000000, 0x00000000 };
118 _ASSERTE(pData != NULL && pcbData != NULL);
122 m_pData = new BYTE[(sizeof(RESOURCEHEADER) * 3 + sizeof(EXEVERRESOURCE))];
125 m_pEnd = m_pData + sizeof(RESOURCEHEADER) * 3 + sizeof(EXEVERRESOURCE);
127 // inject the magic empty entry
128 Write( &magic, sizeof(magic) );
138 *pcbData = (DWORD)(m_pCur - m_pData);
145 * Writes the Icon resource into the RES file.
147 * RETURNS: TRUE on succes, FALSE on failure (errors reported to user)
149 VOID Win32Res::WriteIconResource()
151 STANDARD_VM_CONTRACT;
153 HandleHolder hIconFile = INVALID_HANDLE_VALUE;
154 WORD wTemp, wCount, resID = 2; // Skip 1 for the version ID
155 DWORD dwRead = 0, dwWritten = 0;
157 RESOURCEHEADER grpHeader = { 0x00000000, 0x00000020, 0xFFFF, (WORD)(size_t)RT_GROUP_ICON, 0xFFFF, 0x7F00, // 0x7F00 == IDI_APPLICATION
158 0x00000000, 0x1030, 0x0000, 0x00000000, 0x00000000 };
161 hIconFile = WszCreateFile( m_Icon, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,
162 FILE_FLAG_SEQUENTIAL_SCAN, NULL);
163 if (hIconFile == INVALID_HANDLE_VALUE) {
167 // Read the magic reserved WORD
168 if (ReadFile( hIconFile, &wTemp, sizeof(WORD), &dwRead, NULL) == FALSE) {
170 } else if (wTemp != 0 || dwRead != sizeof(WORD)) {
171 COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
174 // Verify the Type WORD
175 if (ReadFile( hIconFile, &wCount, sizeof(WORD), &dwRead, NULL) == FALSE) {
177 } else if (wCount != 1 || dwRead != sizeof(WORD)) {
178 COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
181 // Read the Count WORD
182 if (ReadFile( hIconFile, &wCount, sizeof(WORD), &dwRead, NULL) == FALSE) {
184 } else if (wCount == 0 || dwRead != sizeof(WORD)) {
185 COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
188 NewArrayHolder<ICONRESDIR> grp = new ICONRESDIR[wCount];
189 grpHeader.DataSize = 3 * sizeof(WORD) + wCount * sizeof(ICONRESDIR);
192 for (WORD i = 0; i < wCount; i++) {
194 DWORD icoPos, newPos;
195 RESOURCEHEADER icoHeader = { 0x00000000, 0x00000020, 0xFFFF, (WORD)(size_t)RT_ICON, 0xFFFF, 0x0000,
196 0x00000000, 0x1010, 0x0000, 0x00000000, 0x00000000 };
197 icoHeader.Name = resID++;
199 // Read the Icon header
200 if (ReadFile( hIconFile, &ico, sizeof(ICONDIRENTRY), &dwRead, NULL) == FALSE) {
203 else if (dwRead != sizeof(ICONDIRENTRY)) {
204 COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
207 _ASSERTE(sizeof(ICONRESDIR) + sizeof(WORD) == sizeof(ICONDIRENTRY));
208 memcpy(grp + i, &ico, sizeof(ICONRESDIR));
209 grp[i].IconId = icoHeader.Name;
210 icoHeader.DataSize = ico.dwBytesInRes;
212 NewArrayHolder<BYTE> icoBuffer = new BYTE[icoHeader.DataSize];
214 // Write the header to the RES file
215 Write( &icoHeader, sizeof(RESOURCEHEADER) );
217 // Position to read the Icon data
218 icoPos = SetFilePointer( hIconFile, 0, NULL, FILE_CURRENT);
219 if (icoPos == INVALID_SET_FILE_POINTER) {
222 newPos = SetFilePointer( hIconFile, ico.dwImageOffset, NULL, FILE_BEGIN);
223 if (newPos == INVALID_SET_FILE_POINTER) {
227 // Actually read the data
228 if (ReadFile( hIconFile, icoBuffer, icoHeader.DataSize, &dwRead, NULL) == FALSE) {
231 else if (dwRead != icoHeader.DataSize) {
232 COMPlusThrowHR(HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
235 // Because Icon files don't seem to record the actual Planes and BitCount in
236 // the ICONDIRENTRY, get the info from the BITMAPINFOHEADER at the beginning
238 grp[i].Planes = ((BITMAPINFOHEADER*)(BYTE*)icoBuffer)->biPlanes;
239 grp[i].BitCount = ((BITMAPINFOHEADER*)(BYTE*)icoBuffer)->biBitCount;
241 // Now write the data to the RES file
242 Write( (BYTE*)icoBuffer, icoHeader.DataSize );
244 // Reposition to read the next Icon header
245 newPos = SetFilePointer( hIconFile, icoPos, NULL, FILE_BEGIN);
246 if (newPos != icoPos) {
251 // inject the icon group
252 Write( &grpHeader, sizeof(RESOURCEHEADER) );
254 // Write the header to the RES file
255 wTemp = 0; // the reserved WORD
256 Write( &wTemp, sizeof(WORD) );
258 wTemp = RES_ICON; // the GROUP type
259 Write( &wTemp, sizeof(WORD) );
261 Write( &wCount, sizeof(WORD) );
263 // now write the entries
264 Write( grp, sizeof(ICONRESDIR) * wCount );
271 * Writes the version resource into the RES file.
273 * RETURNS: TRUE on succes, FALSE on failure (errors reported to user)
275 VOID Win32Res::WriteVerResource()
277 STANDARD_VM_CONTRACT;
279 WCHAR szLangCp[9]; // language/codepage string.
280 EXEVERRESOURCE VerResource;
283 bool bUseFileVer = false;
284 WCHAR rcFile[_MAX_PATH]; // Name of file without path
285 WCHAR rcFileExtension[_MAX_PATH]; // file extension
286 WCHAR rcFileName[_MAX_PATH]; // Name of file with extension but without path
289 SplitPath(m_szFile, 0, 0, 0, 0, rcFile, _MAX_PATH, rcFileExtension, _MAX_PATH);
291 wcscpy_s(rcFileName, COUNTOF(rcFileName), rcFile);
292 wcscat_s(rcFileName, COUNTOF(rcFileName), rcFileExtension);
294 static const EXEVERRESOURCE VerResourceTemplate = {
295 sizeof(EXEVERRESOURCE), sizeof(VS_FIXEDFILEINFO), 0, W("VS_VERSION_INFO"),
297 VS_FFI_SIGNATURE, // Signature
298 VS_FFI_STRUCVERSION, // structure version
299 0, 0, // file version number
300 0, 0, // product version number
301 VS_FFI_FILEFLAGSMASK, // file flags mask
304 VFT_APP, // file type
306 0, 0 // file date/time
308 sizeof(WORD) * 2 + 2 * HDRSIZE + KEYBYTES("VarFileInfo") + KEYBYTES("Translation"),
312 sizeof(WORD) * 2 + HDRSIZE + KEYBYTES("Translation"),
318 2 * HDRSIZE + KEYBYTES("StringFileInfo") + KEYBYTES("12345678"),
322 HDRSIZE + KEYBYTES("12345678"),
327 static const WCHAR szComments[] = W("Comments");
328 static const WCHAR szCompanyName[] = W("CompanyName");
329 static const WCHAR szFileDescription[] = W("FileDescription");
330 static const WCHAR szCopyright[] = W("LegalCopyright");
331 static const WCHAR szTrademark[] = W("LegalTrademarks");
332 static const WCHAR szProdName[] = W("ProductName");
333 static const WCHAR szFileVerResName[] = W("FileVersion");
334 static const WCHAR szProdVerResName[] = W("ProductVersion");
335 static const WCHAR szInternalNameResName[] = W("InternalName");
336 static const WCHAR szOriginalNameResName[] = W("OriginalFilename");
338 // If there's no product version, use the file version
339 if (m_Values[v_ProductVersion][0] == 0) {
340 m_Values[v_ProductVersion] = m_Values[v_FileVersion];
344 // Keep the two following arrays in the same order
346 static const LPCWSTR szKeys [MAX_KEY] = {
351 szInternalNameResName,
354 szOriginalNameResName,
358 LPCWSTR szValues [MAX_KEY] = { // values for keys
359 m_Values[v_Description], //compiler->assemblyDescription == NULL ? W("") : compiler->assemblyDescription,
360 m_Values[v_Company], // Company Name
361 m_Values[v_Title], // FileDescription //compiler->assemblyTitle == NULL ? W("") : compiler->assemblyTitle,
362 m_Values[v_FileVersion], // FileVersion
363 rcFileName, // InternalName
364 m_Values[v_Copyright], // Copyright
365 m_Values[v_Trademark], // Trademark
366 rcFileName, // OriginalName
367 m_Values[v_Product], // Product Name //compiler->assemblyTitle == NULL ? W("") : compiler->assemblyTitle,
368 m_Values[v_ProductVersion] // Product Version
371 memcpy(&VerResource, &VerResourceTemplate, sizeof(VerResource));
374 VerResource.vsFixed.dwFileType = VFT_DLL;
376 VerResource.vsFixed.dwFileType = VFT_APP;
378 // Extract the numeric version from the string.
379 m_Version[0] = m_Version[1] = m_Version[2] = m_Version[3] = 0;
380 int nNumStrings = swscanf_s(m_Values[v_FileVersion], W("%hu.%hu.%hu.%hu"), m_Version, m_Version + 1, m_Version + 2, m_Version + 3);
382 // Fill in the FIXEDFILEINFO
383 VerResource.vsFixed.dwFileVersionMS =
384 ((DWORD)m_Version[0] << 16) + m_Version[1];
386 VerResource.vsFixed.dwFileVersionLS =
387 ((DWORD)m_Version[2] << 16) + m_Version[3];
390 VerResource.vsFixed.dwProductVersionLS = VerResource.vsFixed.dwFileVersionLS;
391 VerResource.vsFixed.dwProductVersionMS = VerResource.vsFixed.dwFileVersionMS;
395 v[0] = v[1] = v[2] = v[3] = 0;
396 // Try to get the version numbers, but don't waste time or give any errors
397 // just default to zeros
398 nNumStrings = swscanf_s(m_Values[v_ProductVersion], W("%hu.%hu.%hu.%hu"), v, v + 1, v + 2, v + 3);
400 VerResource.vsFixed.dwProductVersionMS =
401 ((DWORD)v[0] << 16) + v[1];
403 VerResource.vsFixed.dwProductVersionLS =
404 ((DWORD)v[2] << 16) + v[3];
407 // There is no documentation on what units to use for the date! So we use zero.
408 // The Windows resource compiler does too.
409 VerResource.vsFixed.dwFileDateMS = VerResource.vsFixed.dwFileDateLS = 0;
411 // Fill in codepage/language -- we'll assume the IDE language/codepage
414 VerResource.langid = static_cast<WORD>(m_lcid);
416 VerResource.langid = MAKELANGID(LANG_NEUTRAL, SUBLANG_NEUTRAL);
417 VerResource.codepage = CP_WINUNICODE; // Unicode codepage.
419 swprintf_s(szLangCp, NumItems(szLangCp), W("%04x%04x"), VerResource.langid, VerResource.codepage);
420 wcscpy_s(VerResource.szLangCpKey, COUNTOF(VerResource.szLangCpKey), szLangCp);
422 // Determine the size of all the string blocks.
424 for (i = 0; i < MAX_KEY; i++) {
425 if (szValues[i] == NULL || wcslen(szValues[i]) == 0)
427 cbTmp = SizeofVerString( szKeys[i], szValues[i]);
428 if ((cbStringBlocks + cbTmp) > USHRT_MAX / 2)
429 COMPlusThrow(kArgumentException, W("Argument_VerStringTooLong"));
430 cbStringBlocks += (WORD) cbTmp;
433 if ((cbStringBlocks + VerResource.cbLangCpBlock) > USHRT_MAX / 2)
434 COMPlusThrow(kArgumentException, W("Argument_VerStringTooLong"));
435 VerResource.cbLangCpBlock += cbStringBlocks;
437 if ((cbStringBlocks + VerResource.cbStringBlock) > USHRT_MAX / 2)
438 COMPlusThrow(kArgumentException, W("Argument_VerStringTooLong"));
439 VerResource.cbStringBlock += cbStringBlocks;
441 if ((cbStringBlocks + VerResource.cbRootBlock) > USHRT_MAX / 2)
442 COMPlusThrow(kArgumentException, W("Argument_VerStringTooLong"));
443 VerResource.cbRootBlock += cbStringBlocks;
445 // Call this VS_VERSION_INFO
446 RESOURCEHEADER verHeader = { 0x00000000, 0x0000003C, 0xFFFF, (WORD)(size_t)RT_VERSION, 0xFFFF, 0x0001,
447 0x00000000, 0x0030, 0x0000, 0x00000000, 0x00000000 };
448 verHeader.DataSize = VerResource.cbRootBlock;
451 Write( &verHeader, sizeof(RESOURCEHEADER) );
453 // Write the version resource
454 Write( &VerResource, sizeof(VerResource) );
457 // Write each string block.
458 for (i = 0; i < MAX_KEY; i++) {
459 if (szValues[i] == NULL || wcslen(szValues[i]) == 0)
461 WriteVerString( szKeys[i], szValues[i] );
470 * Determines the size of a version string to the given stream.
471 * RETURNS: size of block in bytes.
473 WORD Win32Res::SizeofVerString(LPCWSTR lpszKey, LPCWSTR lpszValue)
475 STANDARD_VM_CONTRACT;
477 size_t cbKey, cbValue;
479 cbKey = (wcslen(lpszKey) + 1) * 2; // Make room for the NULL
480 cbValue = (wcslen(lpszValue) + 1) * 2;
482 cbValue = 4; // Empty strings need a space and NULL terminator (for Win9x)
483 if (cbKey + cbValue >= 0xFFF0)
484 COMPlusThrow(kArgumentException, W("Argument_VerStringTooLong"));
487 #pragma warning(push)
488 #pragma warning(disable:6305) // "Potential mismatch between sizeof and countof quantities"
491 return (WORD)(PadKeyLen(cbKey) + // key, 0 padded to DWORD boundary
492 PadValLen(cbValue) + // value, 0 padded to dword boundary
493 HDRSIZE); // block header.
500 /*----------------------------------------------------------------------------
502 * Writes a version string to the given file.
504 VOID Win32Res::WriteVerString( LPCWSTR lpszKey, LPCWSTR lpszValue)
506 STANDARD_VM_CONTRACT;
508 size_t cbKey, cbValue, cbBlock;
509 bool bNeedsSpace = false;
511 cbKey = (wcslen(lpszKey) + 1) * 2; // includes terminating NUL
512 cbValue = wcslen(lpszValue);
514 cbValue++; // make room for NULL
517 cbValue = 2; // Make room for space and NULL (for Win9x)
519 cbBlock = SizeofVerString(lpszKey, lpszValue);
521 NewArrayHolder<BYTE> pbBlock = new BYTE[(DWORD)cbBlock + HDRSIZE];
522 ZeroMemory(pbBlock, (DWORD)cbBlock + HDRSIZE);
524 _ASSERTE(cbValue < USHRT_MAX && cbKey < USHRT_MAX && cbBlock < USHRT_MAX);
526 // Copy header, key and value to block.
527 *(WORD *)((BYTE *)pbBlock) = (WORD)cbBlock;
528 *(WORD *)(pbBlock + sizeof(WORD)) = (WORD)cbValue;
529 *(WORD *)(pbBlock + 2 * sizeof(WORD)) = 1; // 1 = text value
530 // size = (cbBlock + HDRSIZE - HDRSIZE) / sizeof(WCHAR)
531 wcscpy_s((WCHAR*)(pbBlock + HDRSIZE), (cbBlock / sizeof(WCHAR)), lpszKey);
534 #pragma warning(push)
535 #pragma warning(disable:6305) // "Potential mismatch between sizeof and countof quantities"
539 *((WCHAR*)(pbBlock + (HDRSIZE + PadKeyLen(cbKey)))) = W(' ');
542 wcscpy_s((WCHAR*)(pbBlock + (HDRSIZE + PadKeyLen(cbKey))),
543 //size = ((cbBlock + HDRSIZE) - (HDRSIZE + PadKeyLen(cbKey))) / sizeof(WCHAR)
544 (cbBlock - PadKeyLen(cbKey))/sizeof(WCHAR),
553 Write( pbBlock, cbBlock);
558 VOID Win32Res::Write(LPCVOID pData, size_t len)
560 STANDARD_VM_CONTRACT;
562 if (m_pCur + len > m_pEnd) {
564 size_t newSize = (m_pEnd - m_pData);
566 // double the size unless we need more than that
572 LPBYTE pNew = new BYTE[newSize];
573 memcpy(pNew, m_pData, m_pCur - m_pData);
575 // Relocate the pointers
576 m_pCur = pNew + (m_pCur - m_pData);
578 m_pEnd = pNew + newSize;
582 memcpy(m_pCur, pData, len);