Tag Archives: Visual C++

HTML files generation using XML and XSLT with Microsoft XML DOM API

This short tutorial shows how easy it’s to generate reports in HTML pages using Microsoft XML DOM API together XML and XSLT.

XML (Extensible Markup Language) became a universal standard of encoding data in a format that is both human-readable and machine-readable. It’s widely used in business applications and even Microsoft Office uses it into internal file formats.
XSLT is used for XML documents decoration. Once we have data into a XML files, using the XSLT (Extensible Stylesheet Language Transformations) we can easily generate HTML and xHTML files. XSLT is a W3C recommendation still from 16. November 1999 and in the meantime, it was extended with a new version XSLT v2.0.

XSLT uses XPATH to get the XML’s tags information, complete the predefined temples and transform results into a .html document.
Each decent browser has support for XML and XSLT. All we have to do it’s to link two such files (.xml and .xslt) and once we execute the XML file the browser will generate and render our XHTML content.

// content of XML file

<!--?xml version="1.0" encoding="utf-8"?-->
<!--?xml-stylesheet href="sample_1.xslt" type="text/xsl"?-->

// the rest of the XML file

But in case we are writing non-browser applications the HTML generation becomes a bit complicated in case you are not satisfied with a hard-coded solution and want a flexible solution.

Using the Microsoft’s XML Core Services (MSXML) our job became a piece of cake. We focus once over the HTML generator and later in case we want to change something into our look and content we have to deal only with the .xml and .xslt files.

bool CHTMLGen::Generate(const std::wstring& sXmlFile,
                        const std::wstring& sXsltFile,
                        const std::wstring& sHTMLFile) {
  if (!PathFileExists(sXmlFile.c_str()) || !PathFileExists(sXsltFile.c_str())) {
    return false;
  }

  HRESULT hr;
  CComPtr pXml, pXslt;

  hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
                        IID_IXMLDOMDocument, (void**)&pXml);
  if (FAILED(hr))
    return false;

  VARIANT_BOOL bOkLoad;
  CComVariant varFile;

  varFile = sXmlFile.c_str();
  pXml->put_async(VARIANT_FALSE);
  hr = pXml->load(varFile, &bOkLoad);
  if (FAILED(hr) && (bOkLoad == VARIANT_FALSE))
    return false;

  hr = CoCreateInstance(CLSID_DOMDocument, NULL, CLSCTX_INPROC_SERVER,
                        IID_IXMLDOMDocument, (void**)&pXslt);
  if (FAILED(hr))
    return false;

  varFile = sXsltFile.c_str();
  pXslt->put_async(VARIANT_FALSE);
  hr = pXslt->load(varFile, &bOkLoad);
  if (FAILED(hr) && (bOkLoad == VARIANT_FALSE))
    return false;

  CComBSTR bsHtmlRes;
  std::wstring sHTMLRes = _T("");
  hr = pXml->transformNode(pXslt, &bsHtmlRes);
  if (SUCCEEDED(hr)) {
    CAtlString sRes(bsHtmlRes);
    if (!_tcsnicmp(sRes, _T(""));
      sHTMLRes += szEnd ? (szEnd + 2) : sRes;
  } else
    sHTMLRes += sRes;
 }

 return (_T("") != sHTMLRes) ? SaveFileContent(sHTMLFile, sHTMLRes) : false;
}

Because of using COM don’t forget proper the calls of CoInitialize() and CoUninitialize().
Here are two samples files generated with the test application using the upper method: sample_1, sample_2.

The combination of XML, XSLT and XPATH offers a very flexible way to generate HTML files. With such an approach even native application does not need to change in case we change the HTMLs look. Within the presented case the hard-coded solutions are avoided and most probably a new recompilation is not needed in case we want to change data content (XML) or the look (XSLT).
In case you want to add sophisticated HTML code (ex. colored, formatted, images, etc.) you need to convert that code into XHTML format before adding data into the .XSLT file.

demo application (3005 downloads)

SubclassWindow() method issues in projects base on MFC Feature Pack

The Problem
Trying to paint a background image into client area of a MDI application build in VC++ 6.0 to VC++ 2005 IDE it’s not a difficult task.
In case you need, you can find easily good references. For instance, there are two references from Microsoft (KB129471 and KB103786) and one I prefer: a FAQ wrote by a friend of mine.

Unfortunately things are changing radically in case you’re following the same steps in a Visual C++ IDE that has MFC Feature Pack support. If you’re building from the scratch a VC++ 2008/VC++ 2010 a MDI project that has MFC Feature Pack support and you’re trying to apply sub-classing steps, you will have a big surprise in the moment you’re starting your application in debug mode. Effectively your application will crash in the moment you are trying to call SubclassWindow() in CMainFrame::OnCreate().

int CMainFrame::OnCreate(LPCREATESTRUCT lpCreateStruct) {
  if (CMDIFrameWndEx::OnCreate(lpCreateStruct) == -1)
    return -1;

  // ---- code ---

  // BANG! IN VS 2010 or VS 2008 with MFC Feature Pack
  // m_wndMDIClient.SubclassWindow(m_hWndMDIClient);
  // where m_wndMDIClient is an instance of CMDIClientWnd
  // (http://support.microsoft.com/kb/129471)

  // ------ code ----

  return 0;
}

Problem details
Starting with MFC Feature Pack CMDIFrameWndEx is the new CMainFrame’s parent class instead of CMDIFrameWnd and the problem acts inside of Attach() method:
because CWnd::FromHandlePermanent(HWND hWnd) looks up into a permanent handle map and in returns existing CWnd pointer.

Wnd* PASCAL CWnd::FromHandlePermanent(HWND hWnd) {
  CHandleMap* pMap = afxMapHWND();
  CWnd* pWnd = NULL;
  if (pMap != NULL) {
    // only look in the permanent map - does no allocations
    pWnd = (CWnd*)pMap->LookupPermanent(hWnd);
    ASSERT(pWnd == NULL || pWnd->m_hWnd == hWnd);
  }
  return pWnd;
}

CHandleMap is the wrapper that implements the mapping mechanism between the pointers of MFC wrapped classes and the Windows object handles. Internally, this class has to dictionaries (m_permanentMap and m_temporaryMap) implemented as CMapPtrToPtr, m_nHandles – the number of handles, m_nOffset – the offset of handles in the object and it has a m_pClass pointer of CRuntimeClass (a run time class associated with all MFC classes).
In case you’re interest in more details, you can find more information here.

We have a pointer to a CHandleMap instance that is assigned with the returned pointer of a handle map returned by afxMapHWND(). The returned pointer pWnd it’s assigned with the result returned by pMap->LookupPermanent(hWnd). LookupPermanet() effectively search into a the permanent hash map for exiting HANDLEs and in our case it find it.

inline CObject* CHandleMap::LookupPermanent(HANDLE h) {
  return (CObject*)m_permanentMap.GetValueAt((LPVOID)h);
}

where

void* CMapPtrToPtr::GetValueAt(void* key) const {
  // find value (or return NULL -- NULL values not different as a result)

  ENSURE(this);

  if (m_pHashTable == NULL)
    return NULL;

  UINT nHash = HashKey(key) % m_nHashTableSize;

  // see if it exists
  CAssoc* pAssoc;
  for (pAssoc = m_pHashTable[nHash]; pAssoc != NULL; pAssoc = pAssoc->pNext) {
    if (pAssoc->key == key)
      return pAssoc->value;
  }
  return NULL;
}

If the item having nHash key was found into m_pHashTable then the condition if (pAssoc->key == key) is TRUE because the attribute m_hWndMDIClient of CMDIFrameWnd is used yet.
So, effectively what LookupPermanent() has found in m_permanentMap map is m_hWndMDIClient. And because pMap->SetPermanent(m_hWnd = hWndNew, this) is one of the next call into Attach() method those ASSERTs are a must.
Even if those ASSERT() calls from Attach() are available only in debug mode (because of ASSERT() macro behavior) a release build would not save the situation. Soon or later you’ll get conflicts and the application will crash.

Trying to find where this has happened is not so complicated as long as we take in consider our CMainFrame class it’s derived from CMDIFrameWndEx a class that extends CMDIFrameWnd. If we are looking into CMDIFrameWndEx class implementation (AfxMDIClientAreaWnd.cpp) we will see that into this class SubclassWindow() method it’s called jet:

BOOL CMDIFrameWndEx::OnCreateClient(LPCREATESTRUCT lpcs,
                                    CCreateContext* pContext) {
  if (!CMDIFrameWnd::OnCreateClient(lpcs, pContext)) {
    return FALSE;
  }

  if (m_bDoSubclass) {
    m_wndClientArea.SubclassWindow(m_hWndMDIClient);  // this is it!
  }

  return TRUE;
}

Subclassing a CWnd derived instance that has already a mapped HWND item is an error and these ASSERTs try to avoid this from development moment. Having two different CWnd-derived objects with the same HWND is not possible – the only exception is CDC instances that have 2 HWNDs (m_hDC and m_hAttribDC).
Related to my issue, according to Steve Horne from Microsoft, “anything that uses the MFC Feature Pack will be using CMDIFrameWndEx which is a very different beast. It has this feature built it as you’ve found out”.
The worst part is that “If you were able to subclass the Ex client area, you’d probably end up breaking a lot of the FluentUI features.”
The VS 2008 / VS 2010 wizard generates and use a lot of Feature Pack FluendUI items.

A bad solution
An approach might be trying to adapt sub-classing idea directly into CMainFrame class. So, the steps might be:

  • No CMDIClientWnd instance is needed (as in existing tutorials). So no more SubclassWindow() call in CMainFrame::OnCreate().
  • Handle WM_ERASEBKGND, WM_SIZE and WM_PAINT on CMainFrame.
void CMainFrame::OnPaint() {
  CWnd* pWnd =
      CWnd::FromHandle(m_hWndMDIClient);  // returns a MFC object pointer
  // for the given handle if it is valid
  CPaintDC dc(pWnd);

  // the rest of the code
}

CWnd::FromHandle() acquires a pointer to an MFC object pointer from CHandleMap via afxMapHWND().

BOOL CMainFrame::OnEraseBkgnd(CDC* pDC) {
  return FALSE;
}

void CMainFrame::OnSize(UINT nType, int cx, int cy) {
  CMDIFrameWndEx::OnSize(nType, cx, cy);

  Invalidate();
}

At the very first time everything looked nice. But unfortunately I have to admit Steve Horne’s observations. In different situations (most on resizing or moving messages) some of the FluentUI items were not correctly painted (some Ribbon items painting issues – different cases).

So, a better solution is needed.

A good but not perfect solution
In my research, for projects base on MFC Feature Pack, there is no perfect solution for this issue. I mean something similarly with the good solutions that I mentioned in the beginning of this article but acts fine until the first IDE that use MFC Feature Pack.
As we have seen on top trying to subclass a window with an already mapped is not a good idea.
The solution is based on Joseph M. Newcomery’s idea, a well-known book writer and Microsoft Visual C++ MVP. Joe proposes “temporary” remapping only for the case we need – in my case painting actions. For the rest of the action the mapping process inside of framework continues in the classic way. It’s a “gross and ugly” solution but until having a better solution from Microsoft or others I consider it fine for my needs.

  • First step is to define a class CMDIClientWnd derived from CWnd and add WM_PAINT and WM_ERASEBKGND handle methods.
BOOL CMDIClientWnd::OnEraseBkgnd(CDC* pDC) {
  return FALSE;  // let OnPaint() to paint, only
}

void CMDIClientWnd::OnPaint() {
  CPaintDC dc(this);

  // effective painting stuff
}
  • Catch the WM_PAINT message in CMainFrame via PreTranslateMessage() before the message is dispatched for execution and calling our redraw method.
BOOL CMDIClientWnd::OnEraseBkgnd(CDC* pDC) {
  return FALSE;  // let OnPaint() to paint, only
}

void CMDIClientWnd::OnPaint() {
  CPaintDC dc(this);

  // effective painting stuff
}

Here is the RedrawClientArea() public method.

void CMainFrame::RedrawClientArea() {
  CMDIClientWnd wnd_cl;

  wnd_cl.Attach(m_wndClientArea.Detach());
  wnd_cl.Invalidate();
  wnd_cl.UpdateWindow();
  m_wndClientArea.Attach(wnd_cl.Detach());
}

So we create locally an instance of CMDIClientWnd and we attach it internally to ChandleMap::m_permanetMap via Attach(), not before detaching m_wndClientArea (an CMDIClientAreaWnd instance, attribute in CMDIFrameWndEx and as we have seen before it subclass the CMDIFrameWndEx in CMDIFrameWndEx::OnCreateClient()).

The idea is that our CMDIClientWnd instance temporary replace m_wndClientArea instance of CMDIClientAreaWnd right before effective WM_PAINT message is dispatched via PreTranslateMessage().

  • Include your new class header (ex. MDIClientWnd.h) in MainFrm.cpp and call RedrawClientArea() in CMainFrame::OnSize().
void CMainFrame::OnSize(UINT nType, int cx, int cy) {
  CMDIFrameWndEx::OnSize(nType, cx, cy);

  RedrawClientArea();  // repaint on WM_SIZE
}
  • If the child frames window is not tabbed style (when all client area is hidden) and the client area is still visible than we have to call RedrawClientArea() method from WM_MOVE and WM_SIZE handler of CChildFrame and we have to include MainFrm.h into ChildFrame.cpp.
void CChildFrame::OnMove(int x, int y) {
  CMDIChildWndEx::OnMove(x, y);

  CMainFrame* pMainFrame = (CMainFrame*)GetParentFrame();
  ASSERT(pMainFrame);

  pMainFrame->RedrawClientArea();
}

void CChildFrame::OnSize(UINT nType, int cx, int cy) {
  CMDIChildWndEx::OnSize(nType, cx, cy);

  CMainFrame* pMainFrame = (CMainFrame*)GetParentFrame();
  ASSERT(pMainFrame);

  pMainFrame->RedrawClientArea();
}
  • Additionally, in order to make sure the painting message is received by main frame at application’s starting moment and your image is correctly painted from the beginning, please call pMainFrame->Invalidate() after pMainFrame->UpdateWindow() in InitInstance() method of your application class. Otherwise, if your application it’s starting with no opened document (for instance new document), your picture will appear only in the moment a WM_PAINT message is generated in CMainFrame (for instance when you resize your application, select the menu, etc).

A disadvantage of this approach is that the interest message (WM_PAINT) is not handled inside the class of m_wndClientArea, but the good point is that the rest of the messages are left at the correct class of the framework and will work correctly.
Demo application (3862 downloads)

Adventures with _chkstk

Called by the compiler when you have more than one page of local variables in your function.
_chkstk Routine is a helper routine for the C compiler. For x86 compilers, _chkstk Routine is called when the local variables exceed 4K bytes; for x64 compilers, it is 8K.

That’s all that you get from _chkstk()’s msdn web page. Nothing more…

Overview
A process starts with a fixed stack space. The top of a stack is pointed to by the ESP register (Extended Stack Pointer) and this is a decrementing pointer. Every function calls results in a stack created for the function inside this Process Stack. Every thread function has its own stack. The stack is a downward growing array. When a function starts, the default stack reservation size is 1 MB.
This is contrasting with the heap’s size, whether theoretically increases to a limit of 4 GB on 32bits OS. See more information here.

Every thread under Windows gets its own block of contiguous memory, and while function calls are made, the stack pointer is increasing and decreasing. In contrast, a different thread within the same process might get a different block of contiguous memory – its own stack. When a context switch occurs, the current thread’s ESP (along with the IP and other registers) are saved in the thread’s context structure, and restored when the thread is activated the next time.
To specify a different default stack reservation size for all threads and fibers, use the STACKSIZE statement in the module definition (.def) file. To change the initially committed stack space, use the dwStackSize parameter of the CreateThread, CreateRemoteThread, or CreateFiber function.
Most stack problems occur in overflows of existing stacks, as their sizes are fixed and they cannot be expanded.

_chkstk() increases the stack when needed by committing some of the pages previously reserved for the stack. If there is no more physical memory available for committed pages, _chkstk fails. When you enter a function (VC++ with the stack checking enabled), it will call the _chkstk located in CHKSTK.ASM. This function does a stack page probing and causes the necessary pages of memory to be allocated using the guard page scheme, if possible. In this function, it is stated that when it encounters _XCPT_GUARD_PAGE_VIOLATION, the OS will attempt to allocate another guarded page and if it encounters _XCPT_UNABLE_TO_GROW_STACK then it’s a stack overflow error. When _XCPT_UNABLE_TO_GROW_STACK is encountered, the stack is not yet set up properly, that is why, that it will not call the catch because calling it will use invalid stack variables which will again cause another exception.

Case – Too many or too big variables on stack
As I said on top, the function stack size is 1 MB. If you miss that and you’re trying to define and use internally an array like this:

int arr[4000][200];

When you’ll compile with VC++ compiler in debug mode, you will have a big surprise: the application is crashing on _chkstk() at the moment the _chkstk() tries to create new memory page on stack and fails.
The output window shows next message:
First-chance exception at 0x004116e7 in testApp.exe: 0xC00000FD: Stack overflow.
Unhandled exception at 0x004116e7 in testApp.exe: 0xC00000FD: Stack overflow.

This happens because the 1MB limit is overloaded even on a win32 OS: 4000*200*4 = 3.2MB (approx.).
Same story if you define many local variables and their stack usage overloads the 1MB limit. Off-course the thread stack size can be changed but think once again if it is really needed to do that.
If you really need this big array, then the best solution to avoid this crash is using the heap.

int **arr = new int*[4000];
for(int i=0; i<4000; ++i) {
  arr[i] = new int[200];
}

// and finally don't forget to delete
for(int i=0;i<4000; ++i) {
  delete[] arr[i];
}
delete[] arr;

Case – Recursive functions
If you have an infinite recursion, then you will gate the same stack overflow error and the application crashes in _chkstk.asm. Recursive function is not the subject of this article so I don’t go in deep… Here it is a good example of what happens with recursive functions.
The solution is to avoid using recursive functions as much as possible and try to implement an iterative function.

Case – A stack corruption
I have started looking over _chkstk() function at the moment when I got few bugs with crashes with some similarly details. I had to analyze some .dump files and solve a few bugs that contained a call stack with _chkstk() on top.
Most of the .dump files call stack contained a second similarly thing: the call of a thread function (so called ThreadFoo()) that was running in a thread’s pool.
At that moment I started to research why _chkstk() fails and my first track was to debug the stack overflows. I followed a MSDN debugging tutorial and unfortunately I didn’t find something strange. I checked if the local stack variables are not so big in order to fill the ThreadFoo() function’s stack, and it did not.
Then a new study of ThreadFoo() function has followed in order to detect the internal functions calls that can fail in some circumstances. I stopped to some trace function calls and I studied deeply. Those trace functions were defined in an external debug class and each time when a new trace file was added it used an internal buffer (TCHAR szBuff[2048] = _T(“”);).
The writing of this buffer was done using: swprintf(). As we know, this function is unsafe and is not recommended to use. As long as the content of these trace lines was dynamically build (in some cases those line may contain even dynamically build SQL queries that failed) then the length of these trace lines could be higher than 2048 bytes and then guess what: a stack corruption appears! UPS! The stack pointer will be corrupted (the classic stack overflow case).

So I have implemented and used the next macros:

#if defined(UNICODE) || defined(_UNICODE)
#define usprintf(x, ...) \
   _snwprintf(x, _countof(x)-1, ##__VA_ARGS__); \
   x[_countof(x)-1] = 0
#else
#define usprintf(x, ...) \
   _snprintf(x, _countof(x)-1, ##__VA_ARGS__); \
   x[_countof(x)-1] = 0
#endif

Now, if we’re using the safe macro we will have no issues.

int x = 23;
TCHAR szMsg[2048] = {0};
usprintf(szMsg, L"Error: %d", x);

A safety alternative way to that buffer was the heap using but the heap access is not fast as the stack access, so I preferred this approach (in a business application every millisecond’s matters for the log system).
After that fixed, I met no other stack corruptions in ThreadFoo() and other code areas.

Even if the top of the call stack was _chkstk() this was not the function that failed. The error appeared because of that stack corruption and _chkstk() has just detected.

Conclusion
If your code produces a stack overflow, then you have to rethink your design in right away:

  • If you see _chkstk() on the top of call stack, check if you have no stack corruptions – stack overflow.
  • Don’t try to manipulate the stack by yourself. The default 1MB stack size is basically enough
  • Allocate dynamically if you’re using big arrays
  • If you have recursive functions producing a stack overflow, re-write them using loops (a tip: it is a proven fact that any recursive functions can be programmed non-recursive)

References
Set stack size
Thread Stack Size
_chkstk Routine
Stack (data structure)
Debugging a Stack Overflow – tutorial
Visual C++ apps crashing in _chkstk() under load
Optimization Note (C++) 1: push, pop, call _chkstk
What is Recursion?

Flexible changes for product version properties – Visual C++ binaries

Manually editing of binary files version in the resource editor of Visual Studio IDE is not a viable solution. If we have dozens of projects in our solution, then for each kit building we should need manual resources file edit. Otherwise, we can use a special tool that does this thing for us.
Unfortunately this approach is not the most flexible and could fail.

For our flexible binaries properties changes and in order to avoid manual edit for each rebuild we can create and include a header file (version.h) that contains some constants of product version and file version of our project (.rc files).

#define PRODUCT_VERSION 4.3.2.198
#define PRODUCT_VERSION_STR "4.3.2.198"

Then, for each .rc file wherever we have FileVersion and ProductVersion we have to use this constants.
When we will build a new kit, we have to change only these constants and then to start the kit building process. Everything is fine until we add new controls in our projects resource files. Then, because of Visual Studio IDE automation we can get an unlikely surprise: the FileVersion and the ProductVersion properties could be reset to 1.0.0.0.

In order to avoid this issue and edit the version only in a single place I propose the following workaround:

  • Create a version.h header file that have to contain these constants only (as on top). I should recommend to create it into the root solution in order of being easy included in all the projects.
  • Include this file in the project you need to use.
  • Use a text editor (ex. notepad.exe) and include next code section at the end of .rc2 resource file of your project (res\your_project.rc2) – this section contains the include version.h file section, too.
// Neutral resources
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEU)
#ifdef _WIN32
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL
#pragma code_page(1252)
#endif //_WIN32
/////////////////////////////////////////////////////////////////////////////
// Versio
//
#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 0x0
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904e4"
BEGIN
VALUE "CompanyName", "TODO: "
VALUE "FileDescription", "TODO: "
VALUE "FileVersion", PRODUCT_VERSION_STR
VALUE "InternalName", "testResources.exe"
VALUE "LegalCopyright", "TODO: (c) . All rights reserved."
VALUE "OriginalFilename", "your_project.exe"
VALUE "ProductName", "TODO: "
VALUE "ProductVersion", PRODUCT_VERSION_STR
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1252
END
END
#endif // Neutral resources
/////////////////////////////////////////////////////////////////////////////
  • Edit “040904e4” block code with same data as if we should edit in a resources editor and use the version.h‘s file defined constants. As you can see in my example, for the FileVersion and ProductVersion properties I use my version.h constants. These properties will not be edited anymore.
  • Delete “// Version” section from default resource file your_project.rc (including comments – recommended).
  • Insert next lines into your_project.rc file after “3 TEXTINCLUDE BEGIN” and before “#define _AFX_NO_SPLITTER_RESOURCES\r\n“:
\r\n""#include ""res\\your_project.rc2""\r\n""\0"
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

Don’t forget to edit .rc2 file name with the right file name of your project.

  • In your_project.rc file the section “// Generated from the TEXTINCLUDE 3 resource.” have to contain only next declaration:

#include "res\your_project.rc2"

The rest of the section’s lines have to be deleted.

  • We save both resources files: your_project.rc and your_project.rc2.
  • Rebuild the project and check the new generated binary properties. In the FileVersion we will have the major version (in my case 4.0.0.0) and in ProductVersion we have the current build version (4.3.2.198).

Demo application - AutoProductVersion (2147 downloads)