标签 Windows API 下的文章

Windows 8.1 GetVersionEx返回6.2.9200 Bug?

某程序需要判断当前Windows系统的版本号。Windows系统的版本号格式为:majorVersion.minorVersion.BuildNumber.

Windows 8 RTM的版本号的6.2.9200 Windows 8.1 Preview的版本好是6.3.9431

使用以下代码在Win8下面运行正常的显示为6.2.9200 , 但是接下来在Win8.1下面测试尽然还是6.2.9200。而通过Win8.1自带的命令行systeminfo(采用WMI方式)返回的是正确的版本号。

OSVERSIONINFO osvi;
osvi.dwOSVersionInfoSize = sizeof(osvi);
GetVersionEx(&osvi);
sprintf(szBuf,"OS Version %d.%d.%d", osvi.dwMajorVersion, osvi.dwMinorVersion, osvi.dwBuildNumber);
MessageBox(hWnd, szBuf, szBuf, MB_OK);

google了一下,MSDN论坛上有个帖子遇到同样的问题。实际上这个问题并非bug,而是微软有意为之。如果程序的目标运行平台不需要支持Win8.1,那么GetVersion(Ex)就给你返回6.2,除非通过App manifests方式指定程序支持Win8.1系统:

<?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=SXS_ASSEMBLY_NAME
version=SXS_ASSEMBLY_VERSION
processorArchitecture=SXS_PROCESSOR_ARCHITECTURE
/>
<description> my app exe </description>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel
level="asInvoker"
uiAccess="false"
/>
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<app>
* <!-- 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}"/>
</app>
</compatibility>
</assembly>

在Win8.1系统以后GetVersion(Ex)被放到兼容层(shim)里面,这样GetVersion(Ex)并不一定会返回系统真实的版本号。取而代之可以采用VersionHelpers方式获取真实的系统版本号。

C++
#include <VersionHelpers.h>

if (!IsWindows8OrGreater())
{
MessageBox(NULL, "You need at least Windows 8", "Version Not Supported", MB_OK);
}

关于此话题可以参看Windows and Windows Server compatibility cookbook: Windows 8Windows 8.1 Preview, and Windows Server 2012 R2 Preview关于versioning章节。

error C2061: syntax error : identifier '_Outptr_result_buffer_'

环境

Windows 7 x64, Visual Studio 2010, SDK为VS2010自带的v7.0A

现象

编译#include了dismapi.h代码报错:

error C2061: syntax error : identifier 'Outptr_result_buffer'

原理

dismapi.h头文件函数原型是这样:

HRESULT WINAPI
DismGetImageInfo(
_In_ PCWSTR ImageFilePath,
_Outptr_result_buffer_ (*Count) DismImageInfo** ImageInfo,
_Out_ UINT* Count
);

在参数名前面的是参数类型,参数类型前面是SAL Annotation.那么什么是SAL Annotation呢?MSDN解释是:

SAL is the Microsoft source code annotation language. By using source code annotations, you can make the intent behind your code explicit. These annotations also enable automated static analysis tools to analyze your code more accurately, with significantly fewer false positives and false negatives.

总体来说SAL是一种用来让代码显得意图明确的一种机制。同时可以方便静态分析工具自动分析代码,防止错误的使用函数。这些SAL批注定义在头文件sal.h里面(\VC\include\sal.h)。

我本机使用的Windows SDK v7.0A使用的是SAL,但是DismApi.h和DismApi.lib是从Windows 8 ADK拷贝过来,而Windows 8 ADK使用的是SAL2。恰好_Outptr_result_buffer_这货是SAL2里面定义的(不信你可以到\VC\include\sal.h查找发现没有_Outptr_result_buffer_,而\Include\shared\sal.h就有定义。

解决方案

让VS2010工程使用Windows 8 SDK里面的头文件和库文件。或者使用VS2012

UuidFromString failed return code 0x000006a9

Let's check out the code below:

DEVNODE     dnSibling, dnChild;
CONFIGRET cr;
TCHAR GuidString[MAX_GUID_STRING_LEN];
ULONG ulSize = sizeof(GuidString);
GUID Guid;
INT iIndex;
BOOL bRet = FALSE;
RPC_STATUS rpcStatus;
cr = CM_Get_DevNode_Registry_Property_Ex(dn, CM_DRP_CLASSGUID, NULL, GuidString, &ulSize, 0, m_hMachine);
if (CR_SUCCESS != cr)
{
dnSibling = NULL;
break;
}
rpcStatus = UuidFromString((RPC_WSTR)&GuidString, &Guid);
bRet = SetupDiGetClassImageIndex(&m_ImageListData, &Guid, &iIndex);
if (bRet)
{
...
}

CM_Get_DevNode_Registry_Property_Ex function return a GUID style string GuidString like {4d36e97d-e325-11ce-bfc1-08002be10318}. and then I use UuidFromString to convert GUID style string to GUID data type. but rpcStatus return failed code:0x000006a9 . MSDN has explanation for this error code:

0x000006A9 RPC_S_INVALID_STRING_UUID The string UUID is invalid.

the problem is GUID shouldn't be like {4d36e97d-e325-11ce-bfc1-08002be10318} but 4d36e97d-e325-11ce-bfc1-08002be10318 which has no "{" and "}".

So, corrected code should be :

GuidString[MAX_GUID_STRING_LEN - 2] = _T('\0');
rpcStatus = UuidFromString((RPC_WSTR)&GuidString[1], &Guid);

WS_CAPTION样式造成duilib程序标题栏按钮无法“隐藏”

duilib里面有个测试项目叫做TestApp。按照官方发布的“Duilib入门文档.doc”。用以下代码可以停止非客户区域的响应:

LRESULT HandleMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
    {
        if( uMsg == WM_CREATE ) {
            //....
        }
        else if( uMsg == WM_DESTROY ) {
            //...
        }
        else if( uMsg == WM_NCACTIVATE ) {
            if( !::IsIconic(m_hWnd) ) {
                return (wParam == 0) ? TRUE : FALSE;
            }
        }
        else if( uMsg == WM_NCCALCSIZE ) {
            return 0;
        }
        else if( uMsg == WM_NCPAINT ) {
            return 0;
        }
        //...
}

可是再点击界面右上角的时候会把系统的标题栏按钮给弹出来,而且奇怪的是要点击按钮位置的靠左边20px才会出来。不知道为什么。

如上图,点击红色按钮的位置,最小化按钮就出来了。有个方法可以临时的解决此问题,就是响应WM_NCHITTEST消息,将系统按钮显示的区域表示为HTCAPTION而非HTCLIENT。

我编译了duilib的其他几个项目发现即便是没有HTCAPTION区域,系统按钮也照样不会弹出来。所以肯定另有蹊跷。果不其然,在HttpDownloader项目中发现如下代码:

LRESULT CMainDlg::OnCreate(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
	LONG styleValue = ::GetWindowLong(*this, GWL_STYLE);
	styleValue &= ~WS_CAPTION;
	::SetWindowLong(*this, GWL_STYLE, styleValue | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
	//.....
}

以上代码目的是去掉了WS_CAPTION属性。果然将以上代码贴到TestApp响应位置解决了此问题。系统按钮再也没有乱弹出来了。

CreateRemoteThread 返回 ERROR_ACCESS_DENIED (5) 访问拒绝错误

Question

本地测试一个远程DLL注入的例子。发现注入系统默认的notepad.exe失败。

单步调试到这句:

HANDLE hRemoteThread = CreateRemoteThread(hProcess,0, 0,(DWORD(__stdcall *)( void *))GetProcAddress(hKernel32,"LoadLibraryA"),pTargetArg, 0, &dwThreadID);
if(hRemoteThread == NULL)
{
    dwErr = GetLastError();
    break;
}

dwErr是5(ERROR_ACCESS_DENIED )。

Solution

原因是本机是Windows 7 x64系统。Win+R输入notepad运行的是C:\Windows\System32\notepad.exe,这个路径下的binary是x64平台的。运行C:\Windows\SysWow64\notepad.exe x86平台的exe注入成功。

由此可知,32bit exe注入64bit exe肯定失败。至于64bit exe注入32bit exe没有测试,估计应该不行。