Can you use CMFCVisualManager with a dialog based application to change the applications appearance? If so how is it done?
The idea is to change the shape, colour etc. of controls such as push buttons using the MFC Feature Pack released with MSVC 2008.
No, can't be done, at least not if you're talking about the Feature Pack version. Version 10 of the BCGSoft libraries do have this functionality, see for example: http://www.bcgsoft.com/bcgcontrolbarpro-versions.htm and http://www.bcgsoft.com/images/SkinnedBuiltInDlgs.jpg. The MFC feature pack is more or less the previous version of the BCGSoft libraries, MS bought a license from them.
You need to add the Common Controls manifest to your project resources. Here is the code for the manifest file:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity
version="1.0.0.0"
processorArchitecture="X86"
name="Program Name"
type="win32"
/>
<description>Description of Program</description>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</assembly>
I think you can have some MFC-feature-pack features by implementing OnApplicationLook on your base CDialog (check Step 4 on this page). It might be better to implement the whole OnApplicationLook method, but you can test your application simply by adding this to OnInitDialog:
CMFCVisualManagerOffice2007::SetStyle(CMFCVisualManagerOffice2007::Office2007_Silver);
CMFCVisualManager::SetDefaultManager(RUNTIME_CLASS(CMFCVisualManagerOffice2007));
CDockingManager::SetDockingMode(DT_SMART);
RedrawWindow(NULL, NULL, RDW_ALLCHILDREN | RDW_INVALIDATE | RDW_UPDATENOW | RDW_FRAME | RDW_ERASE);
This is the least amount of code to enable the Visual Styles. You should be able to pop your CDialog into the frame easily. The IDR_MAINFRAME is a menu resource.
class CMFCApplication2Dlg : public CFrameWndEx
{
CMFCMenuBar bar;
public:
CMFCApplication2Dlg() : CFrameWndEx()
{
LoadFrame(IDR_MAINFRAME);
bar.Create(this);
}
};
class CMFCApplication2App : public CWinAppEx
{
public:
virtual BOOL InitInstance()
{
CWinAppEx::InitInstance();
CMFCVisualManagerOffice2007::SetStyle(
CMFCVisualManagerOffice2007::Office2007_ObsidianBlack);
CMFCVisualManager::SetDefaultManager(
RUNTIME_CLASS(CMFCVisualManagerOffice2007));
SetRegistryKey(_T("Local AppWizard-Generated Applications"));
m_pMainWnd = new CMFCApplication2Dlg();
m_pMainWnd->ShowWindow(SW_SHOW);
m_pMainWnd->UpdateWindow();
return TRUE;
}
};
CMFCApplication2App theApp;
Related
I'm a developper from the FOSS game 0 A.D. (https://play0ad.com) and I recently realised that our detection of the Windows version was failing for Windows 11, just like it did between Windows 8.1 and Windows 10.
It reports Windows 10.0.22000 which is technically the correct version, but we'd like it to report Windows 11 instead.
Here is the current code we use
https://github.com/0ad/0ad/blob/master/source/lib/sysdep/os/win/wposix/wutsname.cpp#L35
https://github.com/0ad/0ad/blob/master/source/lib/sysdep/os/win/wversion.cpp
https://github.com/0ad/0ad/blob/master/source/lib/sysdep/os/win/wversion.h
We're stuck with the Windows 7 SDK for compatibility reasons.
Easy solution would be to replace
if (osInfo.dwMajorVersion >= 10)
{
stream << "Win" << osInfo.dwMajorVersion;
}
by
if (osInfo.dwMajorVersion >= 10)
{
if (osInfo.dwMinorVersion > 22000)
stream << "Win" << 11;
else
stream << "Win" << osInfo.dwMajorVersion;
}
Is there a more robust/future proof solution.
Thanks in advance!
I'm not sure if this will work, but can you give this code a try? It uses the sysinfoapi.h file. According to the docs, it only works on Windows though.
#include <iostream>
#include <sysinfoapi.h>
void print_os_info()
{
OSVERSIONINFOEX info;
ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
GetVersionEx((LPOSVERSIONINFO)&info);
printf("Windows version: %u.%u\n", info.dwMajorVersion, info.dwMinorVersion);
}
int main()
{
print_os_info();
}
EDIT: Apparently applications not manifested for Windows 8.1 or Windows 10 will return the Windows 8 OS version value (6.2) according the the sysinfoapi.h docs\
EDIT AGAIN: I tried using a $(Filename).exe.manifest file with the contents
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity
type="win32"
name="Contoso.ExampleApplication.ExampleBinary"
version="1.2.3.4"
processorArchitecture="x86"
/>
<description>Contoso Example Application</description>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 and Windows 11 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
</application>
</compatibility>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<!--
UAC settings:
- app should run at same integrity level as calling process
- app does not need to manipulate windows belonging to
higher-integrity-level processes
-->
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"
/>
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
Which for me worked and made it print out Windows 10.0 instead of 6.2
Sources: Manifest File, sysinfoapi.h
Except the very weak, atleast for me, solution of considering Windows 10 builds greater than 22000, such as Windows 11, the only solution I found which is actually working is the WMIs Win32_OperatingSystem class - Caption property.
On my dev Windows 10 machine it gives the following string: Microsoft Windows 10 Pro.
On my other dev machine, with Windows 11 installed, the same function gives: Microsoft Windows 11 Pro.
The difference is in the string values - "10" vs "11" - but this is at least something far better than the "build greater than" solution.
C# and C++ work well.
I am able to retrieve the version number properly.
Copying my code below. Using windows.h should work instead of sysinfoapi.h
#include<windows.h>
#include<stdio.h>
int main() {
OSVERSIONINFOEX info;
ZeroMemory(&info, sizeof(OSVERSIONINFOEX));
info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
GetVersionEx((LPOSVERSIONINFO)&info);
printf("Windows version: %u.%u\n", info.dwMajorVersion, info.dwMinorVersion);
}
Alternate way to read version number is to use GetProcAddress. 'GetProcAddress' returns the address of specified DLL function.
#include<iostream>
#include<string>
#include <windows.h>
using namespace std;
void main() {
int osver= 0.0;
NTSTATUS(WINAPI *RtlGetVersion)(LPOSVERSIONINFOEXW);
OSVERSIONINFOEXW osInfo;
*(FARPROC*)&RtlGetVersion = GetProcAddress(GetModuleHandleA("ntdll"), "RtlGetVersion");
if (NULL != RtlGetVersion) {
osInfo.dwOSVersionInfoSize = sizeof(osInfo);
RtlGetVersion(&osInfo);
osver = osInfo.dwMajorVersion;
}
}
We have a .NET DLL that contains several COM classes and objects that partner companies can use. We currently use regasm to register the COM classes in the registry. This has worked for years.
We now want to get rid of the COM registration and use side by side assemblies. I am currently trying to make this transition, but I seem to fail on the finish line. We deliver a sample application in C++ that uses our COM classes, and I try to get it to run without COM registration.
.NET DLL
In our .NET DLL, I have a class like this:
namespace MyNamespace
{
[ComVisible(true)]
[Guid("ef828ade-b459-4446-80db-956715588601")]
[ClassInterface(ClassInterfaceType.None)]
[ProgId("MyVendor.MyClass")]
public partial class MyClass
{
}
}
Application assembly
The C++ sample application that shall work with this COM class has a manifest like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="x86"
publicKeyToken="6595b64144ccf1df"
language="*" />
</dependentAssembly>
</dependency>
<dependency>
<dependentAssembly>
<assemblyIdentity type="win32"
name="MyComDll.X"
version="1.0.0.0" />
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker"
uiAccess="false"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
</application>
</assembly>
DLL assembly
And the .NET DLL itself has a manifest to expose the COM class to the outside world:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity type="win32"
name="MyComDll.X"
version="1.0.0.0" />
<file name="MyComDll.dll">
<comClass description="MyVendor.MyClass"
clsid="{ef828ade-b459-4446-80db-956715588601}"
progid="MyVendor.MyClass.1"
threadingModel="Both">
<progid>MyVendor.MyClass</progid>
</comClass>
</file>
</assembly>
Using COM
I obviously use proxy names in my code samples above, but I made sure the all names in the original code are valid.
In our sample C++ code, we are getting the CLSID and creating the instance of the class this way:
HRESULT hr = CLSIDFromProgID(OLESTR("MyVendor.MyClass"), &clsid);
hr = CoCreateInstance(clsid, nullptr, CLSCTX_SERVER, IID_IDispatch, (void**)(&_pMyClass));
What goes wrong
I use some debug output to check the GUID that was returned from CLSIDFromProgID() and to check the return value. If the COM class is registered using regasm, the return value is S_OK and clsid is the same GUID that I specified in my .NET DLL for the class.
However, if I remove the COM registration and use the side by side assemblies with manifests as shown above, I also get the S_OK return value, but the GUID is something entirely different, and therefore I cannot create an instance of this class. I have not found the GUID I get from CLSIDFromProgID() anywhere - neither in the registry nor in my development solution.
Do you have any idea what I made have done wrong?
I know that my C++ sample application works in general very well with our COM objects, because it works flawless, if the COM classes are registered.
I checked every part of the manifests I created with Microsoft's specification and various guides.
I do not get any SideBySide errors in the Windows Event log, so I know that the manifests are properly formatted.
I do get an S_OK from CLSIDFromProgID(), so I know the ProgID is found - it just delivers the wrong CLSID.
I used the function StringFromCLSID() to compare the CLSID that was found by `CLSIDFromProgID().
First of all, I want to thank Joseph Willcoxson and Remy Lebeau for pushing me in the right direction!
I had a couple of minor issues in my manifests that I could resolve one after another. The big mistake I made was taking the assumption that a CLSID that I get from CLSIDFromProgID is the same as the one I specified in my .NET COM class. After it was pointed out that is not necessarily the case, I did not waste any further time and checked why calling CoCreateInstance failed. It turned several iterations of online research and testing to figure out the mistakes I made.
As Joseph pointed out, I should have used clrClass instead of comClass. I made mistakes configuring the clrClass. For instance, I used the same name for "name" that I used for "progId", but the "name" in a clrClass is the full namespace + class name.
Suppose I'm writing a multi-purpose dll which includes a function for getting the OS version:
void get_os_version(DWORD *major, DWORD *minor)
{
OSVERSIONINFOEX osvi;
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
osvi.dwOsVersionInfoSize = sizeof(OSVERSIONINFOEX);
// deprecated but easier to use for this example's sake
GetVersionEx((OSVERSIONINFO*)&osvi);
*major = osvi.dwMajorVersion;
*minor = osvi.dwMinorVersion;
}
For the Windows version to be retrieved correctly for versions higher than Windows 8, it is required to embed a manifest that specifies the supported platforms (see details here).
So I disable automatic generation of a manifest for my dll file using the /MANIFEST:NO flag when compiling, and instead add the following manifest:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="asInvoker" uiAccess="false"></requestedExecutionLevel>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
</application>
</compatibility>
</assembly>
, using the mt tool:
mt -manifest GetOsVersion.dll.manifest -outputresource:GetOsVersion.dll;#2
All good and no errors. Now to use the dll, I create a simple App.exe which loads the dll and calls its function:
int _tmain(int argc, _TCHAR* argv[])
{
DWORD major, minor;
get_os_version(&major, &minor);
printf("%d.%d\n", major, minor);
return 0;
}
But when running App.exe on Windows 10, surprise surprise, the output is:
6.2
, which is the version for Windows 8. If I apply the manifest to the App.exe too:
mt -manifest GetOsVersion.dll.manifest -outputresource:App.exe;#1
, the output is the expected one:
10.0
Why is this happening? And can I solve this problem without adding a manifest to the executable?
I don't have control over the applications that will use my library, but I still want to correctly retrieve the OS version.
An alternative method for determining the actual operating system version is documented on the MSDN page "Getting the System Version":
To obtain the full version number for the operating system, call the GetFileVersionInfo function on one of the system DLLs, such as Kernel32.dll, then call VerQueryValue to obtain the \\StringFileInfo\\<lang><codepage>\\ProductVersion subblock of the file version information.
This will work from a DLL regardless of whether the application has a manifest.
(Of course, there is a reason that GetVersionInfo and friends don't return the actual operating system version: programmers have a nasty tendency to misuse this information. You should seriously consider whether or not providing such a function in your DLL is really a good idea.)
Complementing the accepted answer, here is some starter code for anyone else who wants to implement it:
#pragma comment(lib, "version")
static void print_version()
{
DWORD buffer_size = GetFileVersionInfoSize(_T("kernel32.dll"), NULL);
if (buffer_size == 0)
{
// get error from GetLastError()
return;
}
VOID *buffer = malloc(buffer_size);
if (buffer == NULL)
{
// out of memory
return;
}
if (!GetFileVersionInfo(_T("kernel32.dll"), 0, buffer_size, buffer))
{
goto error;
}
VS_FIXEDFILEINFO *version = NULL;
UINT version_len = 0;
if (!VerQueryValue(buffer,
_T("\\"),
(LPVOID*)&version,
&version_len))
{
goto error;
}
_tprintf(_T("Version is: %u.%u\n"),
HIWORD(version->dwProductVersionMS),
LOWORD(version->dwProductVersionMS));
error:
free(buffer);
}
And the output on Windows 10 is:
Version is 10.0
App (executable) manifest MSDN page describes this matter rather explicitly:
The compatibility section of the app (executable) manifest introduced in Windows helps the operating system determine the versions of Windows an app was designed to target.
Compatibility (and other application settings, such as DPI awareness) are completely ignored if manifest is not an executable manifest. This makes sense because otherwise there would be possible manifest conflicts occurring between manifests in different dlls.
Most of the nodes in a manifest applies to the whole process and is only read from the main .exe module:
The compatibility section of the app (executable) manifest introduced in Windows helps the operating system determine the versions of Windows an app was designed to target.
You should use GetProcAddress and CoCreateInstance to check if the feature you need is present, not the Windows version.
With a little work, GetProcAddress can also be used to figure out what version you are on if you really need that information. Look at the minimum OS version of various kernel32 and user32 functions on MSDN...
Computer A runs Windows 7 x64. Computer B runs Windows 7 x86. I am using Eclipse, Ant and MinGW-w64 to compile the file on Computer A. The file runs fine on Computer A, but on Computer B I get the following error:
The version of this file is not compatible with the version of Windows
you're running. Check your computer's system information to see
whether you need an x86 (32-bit) or x64 (64-bit) version of the
program, and then contact the software publisher.
The program is one file, main.cpp
#include <windows.h>
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
MessageBox(NULL, "Goodbye, cruel world!", "Note", MB_OK);
return 0;
}
The ant script is as follows:
<?xml version="1.0" encoding="UTF-8" ?>
<project name="winsock" default="build">
<taskdef resource="cpptasks.tasks">
<classpath>
<pathelement location="E:\_dev\windows\MinGW\msys\home\windoze\projects\Winplay\lib\cpptasks.jar"/>
</classpath>
</taskdef>
<taskdef resource="net/sf/antcontrib/antlib.xml">
<classpath>
<pathelement location="E:\_dev\windows\MinGW\msys\home\windoze\projects\Winplay\lib\ant-contrib.jar" />
</classpath>
</taskdef>
<target name="build">
<mkdir dir="build" />
<mkdir dir="build/obj" />
<cc name="g++" objdir="build/obj" debug="${debug}">
<fileset dir="src" includes="*.cpp" />
<compiler name="g++">
<compilerarg value="-std=c++11" />
</compiler>
</cc>
<condition property="debugoption" value="-g -O0" else="-O2">
<isset property="debug" />
</condition>
<fileset dir="build/obj" id="objects" >
<include name="*.o" />
</fileset>
<pathconvert pathsep=" " property="objectslinker" refid="objects" />
<!-- Due to a bug in GppLinker.java in cpptasks.jar, we must exec g++ because GppLinker erroneously uses gcc, which breaks exception handling. -->
<exec command="g++ -std=c++11 -mwindows ${debugoption} -o build/winplay ${objectslinker}" failonerror="true" />
</target>
<target name="clean">
<delete dir="build" />
</target>
</project>
Why would the .exe work on my system, but not a system with the same Windows? Do I need to do something like statically link to the windows definitions that MinGW uses?
You are generating a 64-bit executable, and it's incompatible with any 32-bit version of Windows (notice that the opposite is not true, if you had generated a 32-bit executable, it would be compatible with 64-bits Windows...)
To generate a 32-bit version of your executable, check the "project options" on eclipse. You'll have to have -march=i686 somewhere in the antfile and the project options. It's possible that Eclipse has a checkbox/combobox for it on its interface...
I am using MFC with VS2005 and I want to add the shield icon to a button. I created a control member variable for the button and in InitDialog I have
// BCM_SETSHIELD
this->m_ctrlStartact.SendMessage(0x1600+0x000C, 0, TRUE);
this->m_ctrlStartact.SetButtonStyle(BS_FLAT);
However, the shield does not appear. According to the Manifest, I have the correct version of the commctrl:
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="X86"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
What can be the remaining problem?
SOLVED!!! For some reason I had the manifest file excluded from the build. Now it works :) :) – divB Jul 5 at 15:07