Как читать из ресурса версии в Visual C++

У меня есть ресурс версии в моих ресурсах в проекте C++, который содержит номер версии, авторские права и сведения о сборке. Есть ли простой способ получить доступ к этому во время выполнения, чтобы заполнить мое диалоговое окно help/about, поскольку в настоящее время я поддерживаю отдельные константные значения этой информации. В идеале решение должно работать для мобильных устройств Windows/CE и более ранних версий Visual C++ (6.0 и выше).


person SmacL    schedule 25.11.2008    source источник
comment
Обратите внимание, что использование _get_pgmptr() или _get_wpgmptr() лучше, чем GetModuleFileName(NULL, szFilename, MAX_PATH); избавит вас от ненужного выделения и прояснит ваше намерение.   -  person q12    schedule 08.01.2017


Ответы (7)


Это отредактированная версия моего исходного ответа.

bool GetProductAndVersion(CStringA & strProductName, CStringA & strProductVersion)
{
    // get the filename of the executable containing the version resource
    TCHAR szFilename[MAX_PATH + 1] = {0};
    if (GetModuleFileName(NULL, szFilename, MAX_PATH) == 0)
    {
        TRACE("GetModuleFileName failed with error %d\n", GetLastError());
        return false;
    }

    // allocate a block of memory for the version info
    DWORD dummy;
    DWORD dwSize = GetFileVersionInfoSize(szFilename, &dummy);
    if (dwSize == 0)
    {
        TRACE("GetFileVersionInfoSize failed with error %d\n", GetLastError());
        return false;
    }
    std::vector<BYTE> data(dwSize);

    // load the version info
    if (!GetFileVersionInfo(szFilename, NULL, dwSize, &data[0]))
    {
        TRACE("GetFileVersionInfo failed with error %d\n", GetLastError());
        return false;
    }

    // get the name and version strings
    LPVOID pvProductName = NULL;
    unsigned int iProductNameLen = 0;
    LPVOID pvProductVersion = NULL;
    unsigned int iProductVersionLen = 0;

    // replace "040904e4" with the language ID of your resources
    if (!VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductName"), &pvProductName, &iProductNameLen) ||
        !VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductVersion"), &pvProductVersion, &iProductVersionLen))
    {
        TRACE("Can't obtain ProductName and ProductVersion from resources\n");
        return false;
    }

    strProductName.SetString((LPCSTR)pvProductName, iProductNameLen);
    strProductVersion.SetString((LPCSTR)pvProductVersion, iProductVersionLen);

    return true;
}
person Mark Ransom    schedule 23.07.2009
comment
Где найти идентификатор языка? - person Jeff B; 23.03.2016
comment
@JeffB Я бы начал со страницы MSDN Константы и строки идентификатора языка. - person Mark Ransom; 23.03.2016
comment
Обе ваши последние две строки дают мне 'void ATL::CSimpleStringT<char,false>::SetString(const char *,int)' : cannot convert argument 1 from 'LPCTSTR' to 'const char *'.. я #include <atlstr.h> - person Patrizio Bertoni; 01.06.2016
comment
@PatrizioBertoni вместо этого используйте LPCSTR, извините. - person Mark Ransom; 01.06.2016
comment
Пример в VerQueryValue документации использует VerQueryValue с lpSubBlock = _T("\\VarFileInfo\\Translation"), чтобы получить информацию о переводе. - person Bondolin; 14.07.2016
comment
Полную таблицу идентификаторов языков можно найти здесь. 040904e4 эквивалентно 0409=США и 04e4=многоязычная кодировка (1252 в десятичной системе) - person ANTARA; 07.11.2016
comment
В этом коде есть ошибка. VerQueryValue возвращает длину строки, включая завершающий ноль. SetString добавляет завершающий ноль к строке. Это приводит к неожиданному поведению. Если вы добавите что-л. чтобы сказать strProductName, похоже, ничего не произошло, потому что теперь в середине строки стоит ноль. Функции, которые принимают строку с нулевым символом в конце, будут принимать только часть исходного имени продукта. Это должно быть либо: strProductName.SetString((LPCSTR)pvProductName, iProductNameLen -1 );, либо strProductName.SetString((LPCSTR)pvProductName);. - person Holger Böhnke; 10.04.2017
comment
@Holger, это совсем не очевидно из чтения документации, и код больше не доступен для тестирования. Я бы не стал делать эту коррекцию без тестирования. - person Mark Ransom; 10.04.2017
comment
@Mark: Тут ты совершенно прав. Это не очень хорошо задокументировано. Я пытался найти, почему моя конкатенация строк не работает. Это было совершенно в другом месте кода. Наконец я проследил это до функции VerQueryValue. Тут до меня дошло, что в строковых данных может быть дополнительный ноль. Обрезал это и вуаля, все заработало. Может быть, вы просто намекнули в своем решении, чтобы попросить людей посмотреть мой комментарий. Если вы просто используете строку отдельно, вы не заметите разницы. Как только вы += используете другую строку или используете оператор +, появляется симптом. - person Holger Böhnke; 12.04.2017
comment
работает под WinCE (Windows Compact Embedded) 2013 отлично. Спасибо. - person flexo; 19.09.2017
comment
Когда я попробовал это, я могу получить только первый символ. Я пробовал отлаживать с помощью Visual Studio. - person Snippy Valson; 02.07.2019

Чтобы получить независимый от языка результат для изменения ответа Марка:

   // replace "040904e4" with the language ID of your resources
    !VerQueryValue(&data[0], _T("\\StringFileInfo\\040904e4\\ProductVersion"), &pvProductVersion, &iProductVersionLen))
{
    TRACE("Can't obtain ProductName and ProductVersion from resources\n");
    return false;
}

To

UINT                uiVerLen = 0;
VS_FIXEDFILEINFO*   pFixedInfo = 0;     // pointer to fixed file info structure
// get the fixed file info (language-independent) 
if(VerQueryValue(&data[0], TEXT("\\"), (void**)&pFixedInfo, (UINT *)&uiVerLen) == 0)
{
    return false;
}

 strProductVersion.Format("%u.%u.%u.%u", 
    HIWORD (pFixedInfo->dwProductVersionMS),
    LOWORD (pFixedInfo->dwProductVersionMS),
    HIWORD (pFixedInfo->dwProductVersionLS),
    LOWORD (pFixedInfo->dwProductVersionLS));
person EdM    schedule 07.03.2011
comment
Это получает другую информацию из ответа Марка Рэнсома. Вы получаете четыре целых числа из строки верхнего уровня PRODUCTVERSION в файле ресурсов; однако ответ Маркса извлекает строку ProductVersion из-под блока StringFileInfo. - person M.M; 02.02.2018

Что-то вроде может помочь вам начать, возможно:

TCHAR moduleName[MAX_PATH+1];
(void)GetModuleFileName(AfxGetInstanceHandle(), moduleName, MAX_PATH);
DWORD dummyZero;
DWORD versionSize = GetFileVersionInfoSize(moduleName, &dummyZero);
if(versionSize == 0)
{
    return NULL;
}
void* pVersion = malloc(versionSize);
if(pVersion == NULL)
{
    return NULL;
}
if(!GetFileVersionInfo(moduleName, NULL, versionSize, pVersion))
{
    free(pVersion);
    return NULL;
}

UINT length;
VS_FIXEDFILEINFO* pFixInfo;
VERIFY(VerQueryValue(pVersionInfo, const_cast<LPTSTR>("\\"), (LPVOID*)&pFixInfo, &length));
person Will Dean    schedule 25.11.2008

Что-то вроде этого даст вам необработанный доступ к данным ресурса и поможет вам начать:

HRSRC res = ::FindResource(NULL, MAKEINTRESOURCE(MY_VERSION_ID), RT_VERSION);
DWORD size = ::SizeofResource(NULL, res);
HGLOBAL mem = ::LoadResource(NULL, res);
LPVOID raw_data = ::LockResource(mem);
...
::FreeResource(mem);
person Rob    schedule 25.11.2008

Остерегаться! Использование FindResource..LockResource неверно. Иногда это срабатывает (как это было в моей небольшой демонстрационной программе), а иногда вызывает нарушение прав доступа (пример: производственный код, для которого я делал демонстрацию).

В документации VerQueryValue() указано, что вместо этого следует вызывать GetFileVersionInfoSize и GetFileVersionInfo. Раймонд Чен объясняет, см. http://blogs.msdn.com/oldnewthing/archive/2006/12/26/1365215.aspx

person Leo    schedule 24.02.2010

Хорошо, немного погуглив, нашел следующий на КодГуру. В основном этот подход использует объект CFileVersionInfo для доступа к любому заданному файлу. Должно быть интересно посмотреть, работает ли он с текущим файлом .EXE и с Windows CE.

person SmacL    schedule 25.11.2008

Иногда я получаю нарушение прав доступа при использовании VerQueryValueA. Но я никогда не получал эту ошибку при использовании VerQueryValueW. Я думаю, что что-то не так с VerQueryValueA в version.dll. Поэтому я использую VerQueryValueW вместо VerQueryValueA даже в проектах Multi-byte Character Encoding. Вот мой код функции ReadVersion

person Vitaly    schedule 31.08.2012