Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stop Visual Studio 2015 from hard coding versioning

I'm currently working on a C++ VS2015 project that makes an executable.

I have a file version.h that simply defines a bunch of numbers.

#define VERSION_MAJOR               3
#define VERSION_MINOR               0
#define VERSION_REVISION            0
#define VERSION_BUILD               2
#define VER_FILE_VERSION            VERSION_MAJOR, VERSION_MINOR, VERSION_REVISION, VERSION_BUILD
...

This version.h is used in the .rc file of the project to define the version information of the executable.

The .rc file looks like

#include "resource.h"
#include "version.h"
...
VS_VERSION_INFO VERSIONINFO
 FILEVERSION VER_FILE_VERSION
 ...

So my problem is that every time I add a resource such that resource.h is modified, Visual Studio 2015 seems to remove the #include "version.h" in the .rc file and instead hard codes all the values found in "version.h".

Example .rc file of problem

#include "resource.h"
...
VS_VERSION_INFO VERSIONINFO
 FILEVERSION 3,0,0,2
 ...

So my question is, how do I stop VS2015 from doing this?

like image 616
H. Sock Avatar asked Jun 20 '16 15:06

H. Sock


1 Answers

You cannot actually stop Visual Studio's built-in resource editor from doing this. Any time that you use the resource editor (i.e., the GUI) to make changes to an item in a resource file, it will regenerate the resource file's code. This will clobber all sorts of manual tweaks made to a resource file, including substituting symbolic constants and/or arithmetic with literals, removing conditionally defined blocks of code, destroying careful manual formatting, and so on. As such, it is not a good idea to manually edit the resource file.

If you need to be able to make manual edits to a resource file that the resource editor will not clobber, then you can achieve that by adding a second resource file to your project. By convention, this has a .rc2 extension. Visual Studio's resource editor will never edit these resources directly, which means all editing must be done manually. But once you set it up correctly, all resources you put there will still be linked into your binary, making the end result equivalent.

This is pretty much the only way to get sane versioning, and I use it in my own projects. For example:

//
// MyProj.RC2 - resources Microsoft Visual C++ does not edit directly
//

#ifdef APSTUDIO_INVOKED
   #error this file is not editable by Microsoft Visual C++
#endif  // APSTUDIO_INVOKED


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

#include "Version.h"


VS_VERSION_INFO VERSIONINFO
 FILEVERSION     VERSION_MYPROJ_MAJOR,VERSION_MYPROJ_MINOR,VERSION_MYPROJ_REVISION,0
 PRODUCTVERSION  VERSION_MYPROJ_MAJOR,VERSION_MYPROJ_MINOR,VERSION_MYPROJ_REVISION,0
 FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
 FILEFLAGS 0x1L
#else
 FILEFLAGS 0x0L
#endif
 FILEOS 0x40004L
 FILETYPE 0x1L
 FILESUBTYPE 0x0L
BEGIN
    BLOCK "StringFileInfo"
    BEGIN
        BLOCK "040904b0"
        BEGIN
            VALUE "CompanyName", "Cody Gray"
            VALUE "FileDescription", "The World's Greatest Application"
            VALUE "FileVersion", VERSION_MYPROJ_FULL
            VALUE "InternalName", "MyProj"
            VALUE "LegalCopyright", "Copyrights are for suckers!"
            VALUE "OriginalFilename", "MyProj.exe"
            VALUE "ProductName", "MyProj"
            VALUE "ProductVersion", VERSION_MYPROJ_FULL
        END
    END
    BLOCK "VarFileInfo"
    BEGIN
        VALUE "Translation", 0x409, 1200
    END
END

You can put other resources here, too. For example, in one application that I'm working on now, I have a dialog box resource that I only want to get linked into "debug" builds (it is for configuring internal, debug-related options). I tried wrapping the dialog box's definition with #ifdef DEBUG, but the resource editor stripped that entire block out each time it ran, so that was a no-go. Instead, I moved the dialog box's definition into my .rc2 file, keeping it wrapped in the #ifdef. I lost the ability to edit it with the resource editor, but I don't care so much about that since I can edit it just as quickly by hand in the rare event that I need to make changes. And at least it doesn't get automatically stripped out.

The final key is getting this .rc2 file to be compiled by the resource editor and linked into your binary. To do that, you will need to edit your main resource file (.rc), but this change will get preserved. The part you want to edit are the TEXTINCLUDE resources. In my .rc file, that section looks something like this:

/////////////////////////////////////////////////////////////////////////////
//
// TEXTINCLUDE
//

1 TEXTINCLUDE 
BEGIN
    "Resource.h\0"
END

2 TEXTINCLUDE 
BEGIN
    "#ifndef APSTUDIO_INVOKED\r\n"
    "#include ""TargetVer.h""\r\n"
    "#endif\r\n"
    "#include ""AfxRes.h""\r\n"
    "#include ""VerRsrc.h""\r\n"
    "#include ""Version.h""\r\n"
    "\0"
END

3 TEXTINCLUDE 
BEGIN
    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
    "LANGUAGE 9, 1\r\n"
    "#include ""MyProj.rc2""     // non-Microsoft Visual C++ edited resources\r\n"
    "#include ""AfxRes.rc""      // standard components\r\n"
    "#endif\r\n"
    "\0"
END

#endif    // APSTUDIO_INVOKED

The important part is where MyProj.rc2 is #included in the 3rd TEXTINCLUDE section. Any text here is dumped directly into the resource file whenever the resource compiler runs, so at the end of your resource file, you'll see a familiar-looking auto-generated section like:

#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
LANGUAGE 9, 1
#include "MyProj.rc2"     // non-Microsoft Visual C++ edited resources
#include "AfxRes.rc"      // standard components
#endif

/////////////////////////////////////////////////////////////////////////////
#endif    // not APSTUDIO_INVOKED

It bears mentioning, in case you can't figure out how to put the pieces together from my examples here, that the MFC project template in Visual Studio sets this all up for you automatically, as described in this technical note.

like image 68
Cody Gray Avatar answered Nov 17 '22 13:11

Cody Gray