11
Ian
2011

Editarea manuala a versiunilor binarelor din editorul de resurse al IDE-ului Visual Studio pentru proiecte mari ce necesita construirea de multe build-uri pentru kit-uri nu este o solutie viabila. Daca exista cateva zeci de proiecte in solutia noastra, la fiecare kit nou creeat ar trebuii sa editam de mana toate fisierele de resurse. Sau sa folosim un tool special pentru asta. Numai ca aceasta abordare nu e ceea mai flexibila si uneori poate da gres.

Pentru flexibilitatea schimbarilor proprietatilor de versiune a binarelor din proiectele noastre si pentru a evita editarea manuala din resurse la fiecare build se poate creea si include un fisier version.h cu definitiile versiunilor in fisierul .rc al proiectului.

Fisierul version.h trebuie sa includa doar niste constante de genul:

    #define PRODUCT_VERSION      4.3.2.198
    #define PRODUCT_VERSION_STR  "4.3.2.198"

Apoi, in fiecare fisier .rc pentru FileVersion si ProductVersion folosim aceste constante. Cand generam un nou kit, va trebui sa editam doar acest fisier si apoi pornim procesul de build.

Totul este OK pana in momentul in care adaugam noi elemente in resursele proiectului (de exemplu controale pe un template de dialog). Atunci, partea de automation din Visual Studio ne poate face o surpriza neplacuta: resetarea propietatilor de FileVersion si ProductVersion.

Pentru a evita aceasta problema si a edita intr-un singur loc aceata versiune, propun urmatorul workaround: cream un fisier version.h ce sa contina doar urmatoarele doua linii (de preferinta in radacina solutiei pentru a putea fi inclus usor daca avem mai multe proiecte), fisier ce va fi inclus in proiect:

    #define PRODUCT_VERSION      4.3.2.198
    #define PRODUCT_VERSION_STR  "4.3.2.198"

Folosind un editor text (ex. notepad.exe sau editorul din Visual Studio) includem urmatoarea sectiune de cod la sfarsitul fisierului de resurce .rc2 (res\your_project.rc2) - aceasta sectiune contine si includerea fisierului version.h.

// Neutral resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
#ifdef _WIN32
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
#pragma code_page(1252)
#endif //_WIN32

/////////////////////////////////////////////////////////////////////////////
//
// Version
//

#include "..\version.h"

VS_VERSION_INFO VERSIONINFO
 FILEVERSION PRODUCT_VERSION
 PRODUCTVERSION PRODUCT_VERSION
 FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x4L
 FILETYPE 0x1L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904e4"
        BEGIN
            VALUE "CompanyName", "TODO: <Company name>"
            VALUE "FileDescription", "TODO: <File description>"
            VALUE "FileVersion", PRODUCT_VERSION_STR
            VALUE "InternalName", "testResources.exe"
            VALUE "LegalCopyright", "TODO: (c) <Company name>.  All rights reserved."
            VALUE "OriginalFilename", "your_project.exe"
            VALUE "ProductName", "TODO: <Product name>"
            VALUE "ProductVersion", PRODUCT_VERSION_STR
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1252
    END
END
#endif    // Neutral resources
/////////////////////////////////////////////////////////////////////////////

Se editeaza blocul "040904e4" (textul albastru) ca si cum l-am fi editat din editorul de resurse. Dupa cum se poate observa, pentru proprietatile FileVersion si pentru ProductVersion eu folosesc acum constantele din version.h si acestea nu vor fi editate.

Din fisierul implicit de resurse your_project.rc, stergem sectiunea de dupa "// Version" (inclusiv comentariile sectiunii). In fisierul your_project.rc, dupa linia "3 TEXTINCLUDE" si inainte de "#define _AFX_NO_SPLITTER_RESOURCES\r\n" inseram urmatoarele linii:

    "\r\n"
    "#include ""res\\your_project.rc2""\r\n"
    "\0"

Acel bloc va arata asa:

3 TEXTINCLUDE 
BEGIN
    "\r\n"
    "#include ""res\\your_project.rc2""\r\n"
    "\0"
    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
    "#define _AFX_NO_OLE_RESOURCES\r\n"
    "#define _AFX_NO_TRACKER_RESOURCES\r\n"
    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
    "\r\n"
    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
    "LANGUAGE 9, 1\r\n"
    "#pragma code_page(1252)\r\n"
    "#include ""res\\your_project.rc2""  // non-Microsoft Visual C++ edited resources\r\n"
    "#include ""afxres.rc""     // Standard components\r\n"
    "#endif\r\n"
    "\0"
END

Nota: Nu uitati sa editati numele fisierului .rc2 cu numele corect al proiectului.

De asemenea, sectiunea "// Generated from the TEXTINCLUDE 3 resource." din your_project.rc va trebui sa contina doar acest include, restul liniilor din aceasta sectiune vor fi sterse:

#include "res\your_project.rc2"

In final salvam cele doua fisiere your_project.rc si your_project.rc, rebuilduim proiectul si verificam proprietatile binarului generat. La FileVersion vom avea versiunea majora (in cazul meu 4.0.0.0) iar la ProductVersion avem versiunea curenta de build (4.3.2.198).

Observatie

Odata aplicati acesti pasi, aceste proprietati nu mai pot fi editate din editorul de resurse vizual din Visual Studio, ci doar printr-un editor text. Daca nu ne-am definit altceva la String Table in editorul vizual vom mai vedea doar IDS_ABOUTBOX.