2013年10月

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镜像文件的只读属性。