应对32位程序在64位系统上访问注册表和文件自动转向问题

【IT168知识库】
 1 简介

当 前计算机系统已经逐渐地从32位转到64位,XP,2003,VISTA都有64位的版本。从目前而言,32位应用程序还是占了绝大多数,但是也有部分应 用程序既有32位版本,又有64位版本。为了保证32位程序可以顺利运行在64位系统上,微软提供了一套叫WOW64的模拟机制。通常把这套系统称为 WOW64。从总体上来说,WOW64是一套基于用户模式的动态链接库,它可以把32位应用程序的发出的命令翻译成64位系统可以接受的格式。从下图中可 以大概地看出32位应用程序运行在64位系统上的方式。

 


当 32位应用程序运行的时候,首先会去启动本地库加载器(Native Library Loader)。加载器会识别出应用程序是32位的并且用特殊的方式来处理它。加载器会为32位应用程序建立起一个WOW64的模拟环境并把控制权交给 32位的Ntdll.dll。运行在32位应用程序和64位Ntdll.dll之间的WOW64模拟环境会将32位应用程序的指令翻译成64位 Ntdll.dll可以接受的方式,并且它也可以把系统的指令翻译成32位应用程序可以接受的方式。

2 如何判断系统是64位

判 断系统是否是64位的方法有很多。对于C#来说,调用WMI是一种简单易行的方式。我们可以用Win32_Processor类里面的 AddressWidth属性来表示系统的位宽。AddressWidth的值受CPU和操作系统的双重影响。具体的值如下面的表格所示:

 32bit OS64bit OS
32bit CPUAddressWidth = 32N/A
64bit CPUAddressWidth = 32AddressWidth = 64


可以用下面的C#代码得到AddressWidth的值

public   static   string  Detect3264()
{
ConnectionOptions oConn 
=   new  ConnectionOptions();
System.Management.ManagementScope oMs 
=   new  System.Management.ManagementScope( " localhost " , oConn);
System.Management.ObjectQuery oQuery 
=   new  System.Management.ObjectQuery( " select AddressWidth from Win32_Processor " );
ManagementObjectSearcher oSearcher 
=   new  ManagementObjectSearcher(oMs, oQuery);
ManagementObjectCollection oReturnCollection 
=  oSearcher.Get();
string  addressWidth  =   null ;

foreach  (ManagementObject oReturn  in  oReturnCollection)
{
addressWidth 
=  oReturn[ " AddressWidth " ].ToString();
}

return  addressWidth;
}


3 文件系统的转向

32 位进程不能加载64位Dll,64位进程也不可以加载32位Dll。Windows的系统目录包含了所有安装的应用程序和它们的Dll文件,根据我们所述 的规则,它应该被分为给64位应用程序的目录和给32位应用程序的目录。如果不这样,我们就无法区分32位和64位的Dll文件。对于64位应用程序,其 文件通常被放在%windir%/system32和%programfiles%(比如:c:/program files)。对于32位应用程序,其文件通常在%windir%/syswow64和C:/program files (x86)下面。如果我们用32位程序去访问%windir%/system32,不管我们用硬编码还是其它的方式,系统都会自动地给我们转向 到%windir%/syswow64下面。这种转向对于每个32位应用程序默认都是打开的。但是这种转向对于我们来说并不总是需要的。那么我们可以在 C#里面调用相关的API来关闭和打开这种转向。常用的函数有3个,Wow64DisableWow64FsRedirection(关闭系统转 向),Wow64RevertWow64FsRedirection(打开系统转向),Wow64EnableWow64FsRedirection(打 开系统转向)。但是Wow64EnableWow64FsRedirection在嵌套使用的时候不可靠,所以通常用上面的 Wow64RevertWow64FsRedirection来打开文件系统转向功能。在C#中,我们可以利用DllImport直接调用这两个函数。但 是要注意到的是,在32位的Kernel.dll中是没有这两个函数的。那么在C++中应该使用LoadLibrary来动态加载这两个函数。否则会因为 找不到这两个函数而无法通过编译。而且在目前的使用中,发现这两个函数有一个小小的问题。如果我们在调用了 Wow64DisableWow64FsRedirection后去调用Comdlg32.dll的GetOpenFileName函数,是无法调用成功 的。但是也得不到Error的值。在C#中可以用如下的代码关闭和打开文件的转向。

声明调用规则

[DllImport(  " Kernel32.dll " , CharSet = CharSet.Auto, SetLastError  =   true )] 
public   static   extern   bool  Wow64DisableWow64FsRedirection( ref  IntPtr ptr);
[DllImport( 
" Kernel32.dll " , CharSet = CharSet.Auto, SetLastError  =   true )] 
public   static   extern   bool  Wow64RevertWow64FsRedirection(IntPtr ptr);


关闭转向

Wow64DisableWow64FsRedirection( ref  Ptr);


打开转向

Wow64RevertWow64FsRedirection(Ptr);


要注意的是,关闭和打开要成对出现。以免出现混乱的行为。

4 注册表的转向

为 了防止注册表键冲突,注册表在某些键也分成了两个部分。一部分是专门给64位系统访问的,另一部分是专门给32位系统访问的,放在Wow6432Node 下面。当32位程序去访问某些键值的时候,和文件转向类似,系统也会自动地把程序的访问转向到Wow6432Node下面。Wow6432Node这个节 点存在于HKEY_LOCAL_MACHINE和HKEY_CURRENT_USER下面。如果我们希望关闭这个转向的话,可以通过上面的 Wow64DisableWow64FsRedirection和RegOpenKeyEx方法办到。RegOpenEx方法在C#中调用声明方法如下:

[DllImport( " Advapi32.dll " , CharSet  =  CharSet.Auto, SetLastError  =   true )]
public
static   extern   uint  RegOpenKeyEx( UIntPtr hKey, string  lpSubKey, uint  ulOptions, int  samDesired, out  IntPtr phkResult);


其中需要注意的是samDesired这个参数。这个参数可以取 KEY_ALL_ACCESS, KEY_QUERY_VALUE,  KEY_WOW64_64KEY等值(详情可以查阅MSDN)。当我们已经关闭了文件系统的转向,那么就可以利用: (KEY_QUERY_VALUE | KEY_WOW64_64KEY)来得到注册表的完全访问权限。这个地方需要注意的是,在Vista下面,有一些注册表项是只读的,如果用了KEY_ALL_ACCESS这个参数,就会出现“Access is denied” 这个错误(ErrorCode = 5)。因此,如果不是要写入注册表的话,最好不要使用KEY_ALL_ACCESS。我们可以用如下代码来完全访问注册表。

        // use this function transfer the string key name to HKEY handle
         private   static  UIntPtr TransferKeyName( string  keyName) 
{
UIntPtr HKEY_CLASSES_ROOT        
=  (UIntPtr) 0x80000000 ;
UIntPtr HKEY_CURRENT_USER        
=  (UIntPtr) 0x80000001 ;
UIntPtr HKEY_LOCAL_MACHINE    
=  (UIntPtr) 0x80000002 ;
UIntPtr HKEY_USERS                    
=  (UIntPtr) 0x80000003 ;
UIntPtr HKEY_CURRENT_CONFIG 
=  (UIntPtr) 0x80000005 ;

switch (keyName)
{
case   " HKEY_CLASSES_ROOT " :
return  HKEY_CLASSES_ROOT;
case   " HKEY_CURRENT_USER " :
return  HKEY_CURRENT_USER;
case   " HKEY_LOCAL_MACHINE " :
return  HKEY_LOCAL_MACHINE;
case   " HKEY_USERS " :
return  HKEY_USERS;
case   " HKEY_CURRENT_CONFIG " :
return  HKEY_CURRENT_CONFIG;
}

return  HKEY_CLASSES_ROOT;
}


public   static   bool  OpenRegKey( string  keyName,  string  subKeyName)
{
UIntPtr hKey 
=  TransferKeyName(keyName);
UIntPtr phkResult 
=  UIntPtr.Zero;
int  KEY_QUERY_VALUE     =  ( 0x0001 ); 
int  KEY_WOW64_64KEY     =  ( 0x0100 );
int  KEY_ALL_WOW64         =  (KEY_QUERY_VALUE  |  KEY_WOW64_64KEY);

if (Is64bitOS  &&   ! IsWow64RedirectionEnabled)  // The os is 64 bit and the FileRedirection is closed
            {                
samDesired 
=  KEY_ALL_WOW64;
}

if (RegOpenKeyEx(hKey, subKeyName, 0 ,samDesired, out  phkResult)  ==   0 // ERROR_SUCCESS=0
            {
return   true ;
}

int  errCode  =  System.Runtime.InteropServices.Marshal.GetLastWin32Error();

return   false ;
}


5 进一步阅读

需要更多的64位编程的资料,可以查阅下面的链接

http://www.microsoft.com/whdc/system/platform/64bit/WoW64_bestprac.mspx

http://msdn2.microsoft.com/en-us/library/aa384187.aspx

http://msdn2.microsoft.com/en-us/library/aa384235.aspx

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Win10 x64位系统上,使用Visual Basic (VB)读写64位注册表需要遵循以下步骤: 1. 使用VB编写程序,确保你已安装了适用于x64位系统的VB版本,并确保你的代码是64位的。 2. 在代码中添加对Microsoft.Win32命名空间的引用,以便使用Registry类的相关功能。例如,使用语句`Imports Microsoft.Win32`。 3. 打开64位注册表编辑器,通过按下Win键+R来运行命令,然后输入`regedit`,并按下Enter键。使用注册表编辑器导航到你希望读写的注册表项。 4. 在VB代码中,使用Registry类的静态方法打开指定的注册表项。例如,使用`Registry.LocalMachine.OpenSubKey`方法来打开HKEY_LOCAL_MACHINE注册表项。 5. 通过使用OpenSubKey方法返回的RegistryKey对象可以进行读取和写入注册表的操作。例如,使用RegistryKey对象的GetValue方法来读取注册表项的值,使用SetValue方法来写入或修改注册表项的值。 6. 完成读写操作后,关闭打开的注册表项。使用RegistryKey对象的Close方法来关闭注册表项,确保释放资源。 值得注意的是,读写的注册表项必须具有适当的权限。如果你的程序未以管理员身份运行,则可能会受到限制或无法访问部分注册表项。为了确保无法访问注册表项也能正常工作,你可以在程序的运行时使用管理员权限运行。可以通过右键点击程序的可执行文件,选择“以管理员身份运行”来实现。 总而言之,通过VB编程语言,在Win10 x64位系统上读写64位注册表,你需要使用Registry类的相关方法来打开、读取和写入注册表项的值,并确保具有管理员权限以便访问所有的注册表项。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值