Unicode NSIS调用nsProcess插件报错

!insertmacro: nsProcess::FindProcess
Invalid command: nsProcess::_FindProcess
Error in macro nsProcess::FindProcess on macroline 1
Error in script "C:\tunps.com.nsi" on line 236 -- aborting creation process

下载nsProcess 1.6.7,官方已经标明支持UNICODE,所以我想配合Unicode NSIS使用应该是没有问题的。我将包里面的\Include\nsProcess.nsh复制到Unicode NSIS程序目录下的Include目录。然后将\Plugin\nsProcessW.dll放入Unicode NSIS程序目录下的Plugin目录。结果编译nsi报以上错误信息。

因为没有看下载nsProcess里面的README就直接开始用了。所以没有察觉到需要将nsProcessW.dll重命名为nsProcess.dll的步骤。打开nsProcess.nsh头文件:

!define nsProcess::FindProcess `!insertmacro nsProcess::FindProcess`
!macro nsProcess::FindProcess _FILE _ERR
    nsProcess::_FindProcess /NOUNLOAD `${_FILE}`
    Pop ${_ERR}
!macroend
!define nsProcess::KillProcess `!insertmacro nsProcess::KillProcess`
!macro nsProcess::KillProcess _FILE _ERR
    nsProcess::_KillProcess /NOUNLOAD `${_FILE}`
    Pop ${_ERR}
!macroend
!define nsProcess::CloseProcess `!insertmacro nsProcess::CloseProcess`
!macro nsProcess::CloseProcess _FILE _ERR
    nsProcess::_CloseProcess /NOUNLOAD `${_FILE}`
    Pop ${_ERR}
!macroend
!define nsProcess::Unload `!insertmacro nsProcess::Unload`
!macro nsProcess::Unload
    nsProcess::_Unload
!macroend

nsProcess DLL的实际上导出函数的名字是_FindProcess, _KillProcess, _CloseProcess, _Unload 前面的nsProcess::这个是根据文件名来变化的。所以解决这个问题有两个办法。

  1. 官方的方法:将nsProcessW.dll重命名为nsProcess.dll
  2. 另外一个方法是不修改nsProcessW.dll,将nsProcess.nsh文件里面的文本 nsProcess::_ 批量替换为nsProcessW::_

最后我想说看文档真的很重要啊!!

InstallScript注册表WOW64重定向

Windows 64位系统兼容32位的技术叫Wow64。windows下面的syswow64为system32的32位模式的位置。注册表HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node模拟HKEY_LOCAL_MACHINE\SOFTWARE的32位模式。

32位的InstallShield程序默认开启了32位重定向。如果需要关闭文件的重定向需要在代码前后加入以下两句:

Disable(WOW64FSREDIRECTION);//关闭64 bit redirection
...//copying files to system32 directory
Enable(WOW64FSREDIRECTION);//重新打开64 bit redirection

注册表设置重定向则有所不同。需要设置一个InstallScript全局常量[REGDB_OPTIONS][2]

REGDB_OPTIONS = REGDB_OPTIONS | REGDB_OPTION_WOW64_64KEY;//禁用注册表重定向
REGDB_OPTIONS = REGDB_OPTIONS & ~REGDB_OPTION_WOW64_64KEY;//重新打开注册表重定向

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章节。

Visual C++ 2012 Redistributable在Windows XP上安装失败

Visual C++ 2012 Redistributable在Windows XP上安装失败

安装日志:

Burn v3.6.3014.0, Windows v5.1 (Build 2600: Service Pack 3), path: C:\Documents and Settings\xp\桌面\vcredist_x86.exe, cmdline: ''
Setting string variable 'WixBundleLog' to value 'C:\DOCUME~1\xp\LOCALS~1\Temp\dd_vcredist_x86_20131010151346.log'
Setting string variable 'WixBundleOriginalSource' to value 'C:\Documents and Settings\xp\桌面\vcredist_x86.exe'
Setting string variable 'WixBundleName' to value 'Microsoft Visual C++ 2012 Redistributable (x86) - 11.0.50727'
Detect 2 packages
Detected package: vcRuntimeMinimum_x86, state: Absent, cached: None
Detected package: vcRuntimeAdditional_x86, state: Absent, cached: None
Condition 'VersionNT >= v6.1 OR (VersionNT = v6.0 AND ServicePackLevel >= 2)' evaluates to false.
Error 0x81f40001: Bundle condition evaluated to false: VersionNT >= v6.1 OR (VersionNT = v6.0 AND ServicePackLevel >= 2)
Detect complete, result: 0x0

由日志看出VS2012对系统环境的要求是:VersionNT >= v6.1 OR (VersionNT = v6.0 AND ServicePackLevel >= 2) 也就是Vista SP2极其以上的系统。所以XP不满足要求。

在Visual Studio 2012中有可以将Platform ToolSet由默认的Visual Studio 2012(v110)修改为Visual Studio 2010(v100)可以回避此问题。但是很不幸的是我的项目需要用到DismAPI,而DismAPI头文件里面的SAL批注是Microsoft SDK v8.0,也就意味着必须用v110 Platform ToolSet。

DISM 您没有装载或修改此映像的权限。

执行:

C:>dism /mount-wim /wimfile:e:\winpe40.wim /index:1 /mountdir:e:\40

报错:

部署映像服务和管理工具 版本: 6.1.7600.16385

错误: 0xc1510111

您没有装载或修改此映像的权限。 请验证您是否有读/写权限,或者使用 /ReadOnly 选项装载此映像。注意,无法使用只读权限提交>对映像所做的更改。

可以在 C:\Windows\Logs\DISM\dism.log 上找到 DISM 日志文件

DISM错误日志:

WIM open failed with access denied. - CWimImageInfo::Mount(hr:0xc1510111)
d:\w7rtm\base\ntsetup\opktools\dism\providers\wimprovider\dll\wimmanager.cpp:999 - CWimManager::InternalOpMount(hr:0xc1510111)
d:\w7rtm\base\ntsetup\opktools\dism\providers\wimprovider\dll\wimmanager.cpp:2247 - CWimManager::InternalCmdMount(hr:0xc1510111)
Error executing command - CWimManager::InternalExecuteCmd(hr:0xc1510111)
d:\w7rtm\base\ntsetup\opktools\dism\providers\wimprovider\dll\wimmanager.cpp:516 - CWimManager::ExecuteCmdLine(hr:0xc1510111)

解决

去掉WIM镜像文件的只读属性。

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