VS2010与Win7共舞:UAC与数据重定向

 【IT168 专稿】回想当年微软高调发布Windows Vista的时候,突出的兼容性问题成为其在推广时遇到的最大阻力,让Windows Vista“出师未捷身先死”,从而成为继Windows Me之后微软最失败的操作系统。有鉴于此,微软在进行Windows 7的开发的时候,将应用程序兼容性放在了前所未有的高度,提前两年就开始为Windows 7进行各种兼容性测试,同时在Windows 7上提供了Windows XP虚拟模式,最大程度地保证应用程序可以平滑地过渡到Windows 7,从这些我们都可以看出微软的良苦用心。

  但是,操作系统的改变,必然会带来一些应用程序兼容性的问题,保持应用程序的良好兼容新,不仅仅是微软自家的事情,作为应用程序的开发者,我们也有不可推卸的责任。你的应用程序是否能够与Windows 7良好兼容?这是摆在每个程序员面前的一个问题。下面我们就以一系列文章,来介绍一下Windows 7有了什么新的变化?这些变化会带来那些应用程序兼容性问题?如何让应用程序与Windows 7保持兼容?

  很多程序员在设计和实现应用程序的时候,为了图方便和省事,都有向应用程序所在的目录“Program Files”,Windows目录或者操作系统根目录(尤其是C:\)写入数据文件的习惯。另外,这些人也习惯于用注册表HKLM/Software下的键值来保存一些数据,比如应用程序的配置参数等等。应用程序一直都工作的很好,直到万恶的UAC的出现。在Windows 7中,这些人会发现他们所要创建的文件没有相应的位置被创建,注册表键值没有被修改。他们问“到底怎么回事?我的应用程序运行正常并没有报错,但是所创建的文件怎么就不见了呢?在其他操作系统上都工作的好好的啊?”

  都是UAC Virtualization惹的祸

  从Windows Vista开始,当然也包括Windows 7,因为UAC机制的引入,操作系统的标准普通用户被限制访问一些核心文件,文件夹和注册表键值。当我们开发的应用程序试图向这些地方写入数据的时候,会被重新定向到其他操作系统认为比较安全的位置。大多数时候,对于普通用户和应用程序开发人员来说这都是透明的,并没有给我们带来什么不便。但是在有些时候,事情并非如此。数据重新定向可能会导致一些奇怪的现象:

  ——在应用程序中,你写入数据到“Program Files”目录,虽然应用程序执行正确,没有错误值返回,但是在这个目录下你却找不到你刚刚写入的文件。

  ——你的应用程序修改了注册表键值,但是你在注册表相应的位置却看不到更新。

  ——当你关闭或者启用UAC后,你的应用程序找不到某些文件了,原来存在的文件凭空消失了。

  在Windows Vista之前,我们通常都是以管理员身份来运行应用程序的。这样,应用程序就可以自由地对操作系统相关的目录或者注册表键值进行读写。当我们以普通用户身份运行这些应用程序时,就会出现这样或者那样无法访问的错误。Windows Vista为了减少这种错误,改善对于普通用户的应用程序的兼容性,同时又不失去其安全性,就利用UAC Virtualization(UAC 虚拟化访问)这种机制,将应用程序的写操作(包括文件和注册表操作)重新定向到了一个预先在用户的配置文件中定义的目录。UAC Virtualization分为文件Virtualization和注册表Virtualization。例如,当一个普通用户运行一个应用程序尝试写入数据到 C:\Program Files\Contoso\Settings.ini时,这个写入操作将被重新定向到C:\Users\Username\AppData\Local\VirtualStore\Program Files\Contoso\settings.ini。这就是为什么我们在写入的时候没有任何错误,但是在相应的目录下找不到我们创建的文件。同样的,对注册表HKLM\Software的写入操作也会被重新定向到HKCU\Software\Classes\VirtualStore。下图1展示了整个UAC Virtualization的流程。

  图1  UAC Virtualization的流程

  这里需要注意的是,UAC Virtualization仅作用于32位的应用程序对系统文件/目录、注册表的读写。64位程序、非交互式程序、模拟程序(Processes that impersonate)、内核调用者、Manifest中含有requestedExecutionLevel属性的可执行文件不包含在Virtualization的作用范围内。

如何获取正确的文件路径

  UAC Virtualization (UAC虚拟化访问) 只是为了帮助现有的应用程序与Windows Vista或者Windows 7保持兼容,减少应用程序错误而设计的。为Windows 7而全新设计的应用程序,不应该再向一些敏感的系统目录或者注册表位置写入数据。同时也不应该借助虚拟化技术为一些不正确的应用程序行为提供补救方案,这无异于饮鸩止渴。当更多的应用程序移植到Windows 7之后,微软可能在未来版本的Windows取消对UAC虚拟存储的支持。例如,64位应用程序是禁用虚拟存储的。

  在为Windows 7新开发应用程序时,我们应该始终开发与标准用户权限相适应的应用程序,而不要指望总是在管理员权限下运行你所设计的应用程序。同时,更多地在普通用户权限下测试你的应用程序,而不是在管理员权限下测试你的应用程序。

  当我们那些在Windows 7之前设计的应用程序遇到UAC Virtualization问题的时候,我们需要从新设计我们的代码,将文件写入到合适的位置。在改善既有代码,使之可以与Windows 7兼容的时候,我们应该确保以下几点:

  ——在运行的时候,应用程序只会将数据保存到每个用户预先定义的位置或者是%alluserprofile% 中定义的普通用户拥有访问权限的位置。

  ——确定你要写入数据的“已知文件夹”(Knownfolders)。通常,所有用户共用的公共数据文件应该写入到一个全局的公共的位置,这样所有用户都可以访问到。而其它数据则应该写入每个用户自己的文件夹。

  1 公共数据文件包括日志文件,配置文件(通常是INI或者XML文件),应用程序状态文件,比如保存的游戏进程等等。

  2 而属于每个用户的文档,则应该保持在文档目录下,或者是用户自己指定的目录。

  ——当你确定合适的文件保存位置后,不要在代码中明文写出(Hard-code)你选择的路径。为了更好地保持兼容性,我们应该采用下面这些API来获得操作系统“已知文件夹(Knownfolders)”的正确路径。

  1 C/C++非托管代码: 使用SHGetKnownFolderPath函数,通过指定“已知文件夹”的KNOWNFOLDERID作为参数来获得正确的文件夹路径。

  •   FOLDERID_ProgramData –所有用户都可以访问的应用程序数据适合放置在这个目录下。
  •   FOLDERID_LocalAppData – 每个用户单独访问的应用程序数据适合放置在这个目录下。
  •   FOLDERID_RoamingAppData – 每个用户单独访问的应用程序数据适合放置在这个目录下。 与上面一个目录不同的是,放置在这个目录下的文件会随着用户迁移,当一个用户在同一个域中的其他计算机登录的时候,这些文件会被复制到当前登录的机器上,就像用户随身携带的公文包一样。

  下面这段代码演示了在非托管代码中如何调用SHGetKnownFolderPath函数获得正确的文件保存路径:

#include  " shlobj.h "
#include 
" shlwapi.h "
//

#define  AppFolderName _T("YourApp")
#define  DataFileName _T("SomeFile.txt")

//  构造一个数据文件路径
//  dataFilePath指向一个长度为MAX_PATH,类型为TCHAR的字符串数值
//  hwndDlg是消息对话框的父窗口句柄
//  当有错误发生的时候用于显示错误提示
//  includeFileName用于表示是否在路径后面扩展文件名
BOOL MakeDataFilePath(TCHAR  * dataFilePath, 
                      HWND hwndDlg, BOOL includeFileName)
{
    
//  初始化工作
    memset(dataFilePath,  0 , MAX_PATH  *   sizeof (TCHAR));
    PWSTR pszPath 
=  NULL;

    
//  SHGetKnownFolderPath函数可以返回一个已知文件见的路径,
    
//  例如我的文档(My Documents),桌面(Desktop),
       
//  应用程序文件夹(Program Files)等等。 
    
//  对于数据文件来说,FOLDERID_ProgramFiles并不是一个合适的位置
    
//  使用FOLDERID_ProgramFiles保存所有用户共享的数据文件
    
//  使用FOLDERID_LocalAppData保存属于每个用户自己的文件(non-roaming).
    
//  使用FOLDERID_RoamingAppData保存属于每个用户自己的文件(roaming).
//  对于“随身文件”(Roaming files),
//  当一个用户在一个域中的其他计算机登陆的时候,
    
//  这些文件会被复制到当前登录的机器上,就像用户随身携带的公文包一样    

    
//  获取文件夹路径
     if  (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData,
               
0 , NULL,  & pszPath)))
    
//  错误的做法: if (FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFiles,
       
//  0, NULL, &pszPath)))
    {
        
//  提示错误
        MessageBox(hwndDlg, _T( " SHGetKnownFolderPath无法获取文件路径 " ),
            _T(
" Error " ), MB_OK  |  MB_ICONERROR);
        
return  FALSE;
    }

    
//  复制路径到目标变量
    _tcscpy_s(dataFilePath, MAX_PATH, pszPath);
    ::CoTaskMemFree(pszPath);

    
// 错误的做法: _tcscpy_s(dataFilePath, MAX_PATH, _T("C:\\"));

    
//  在路径后面扩展应用程序所在文件夹
     if  ( ! ::PathAppend(dataFilePath, AppFolderName))
    {
        
//  提示错误
        MessageBox(hwndDlg, _T( " PathAppend无法扩展路径 " ),
            _T(
" Error " ), MB_OK  |  MB_ICONERROR);
        
return  FALSE;
    }

    
//  是否添加文件名
     if  (includeFileName)
    {
        
//  在路径后扩展文件名
         if  ( ! ::PathAppend(dataFilePath, DataFileName))
        {
            
//  提示错误
            MessageBox(hwndDlg, _T( " PathAppend无法扩展文件名 " ),
                _T(
" Error " ), MB_OK  |  MB_ICONERROR);
            
return  FALSE;
        }
    }

    
return  TRUE;
}

 2 托管代码: 使用System.Environment.GetFolderPath函数,通过指定我们想要获取的“已知文件夹”为参数,从而获取相应的文件夹的正确路径。

  •   Environment.SpecialFolder.CommonApplicationData – 所有用户都可以访问的应用程序数据适合放置在这个目录下。
  •   Environment.SpecialFolder.LocalApplicationData – 每个用户单独访问的应用程序数据适合放置在这个目录下。
  •   Environment.SpecialFolder.ApplicationData – 每个用户单独访问的应用程序数据适合放置在这个目录下。这是“随身文件夹”。

  下面这段代码展示了如何在托管代码中获取正确的文件路径:

internal   class  FileIO
    {
        
private   const   string  AppFolderName  =   " YourApp " ;
        
private   const   string  DataFileName  =   " SomeFile.txt " ;
        
private   static   string  _dataFilePath;

        
///   <summary>
        
///  构建路径
        
///   </summary>
         static  FileIO()
        {
            
//  Environment.GetFolderPath返回一个“已知文件夹”的路径
            
//  Path.Combine可以合并两个路径成一个合法的路径

            
//  …
            
            _dataFilePath 
=  Path.Combine(Environment.GetFolderPath(
                  Environment.SpecialFolder.ProgramFiles), AppFolderName);
            
// 错误的做法:
            
// _dataFilePath = Path.Combine(Environment.GetFolderPath(
             Environment.SpecialFolder.CommonApplicationData), AppFolderName);
            
            
//  扩展文件名
            _dataFilePath  =  Path.Combine(_dataFilePath, DataFileName);
        }

         
public   static   void  Save( string  text)
        {
            
//  检查要保存的字符串是否为空
             if  (String.IsNullOrEmpty(text))
            {
                MessageBox.Show(
" 字符串为空,无法保持. " " 空字符串 " ,
                     MessageBoxButtons.OK, MessageBoxIcon.Error);
                
return ;
            }

            
try
            {
                
//  获取文件保存的路径
                 string  dirPath  =  Path.GetDirectoryName(_dataFilePath);
                
//  检查文件夹是否存在
                 if  ( ! Directory.Exists(dirPath))
                    Directory.CreateDirectory(dirPath); 
//  创建文件夹
            }
            
catch  (Exception ex)
            {
                MessageBox.Show(ex.Message, 
" 文件夹创建失败 " ,
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                
return ;
            }

            
try
            {
                
//  保存字符串到文件
                StreamWriter sw  =   new  StreamWriter(_dataFilePath);
                
try
                {
                    sw.Write(text);
                }
                
finally
                {
                    
//  关闭文件
                    sw.Close();
                }
            }
            
catch  (Exception ex)
            {
                MessageBox.Show(ex.Message, 
" 文件写入失败 " ,
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
        }
        
//  …
   }
}

        如果上面的方法都不适合你,你还可以使用环境变量获取相应的文件夹路径:

  •   %ALLUSERSPROFILE% – 所有用户都可以访问的应用程序数据适合放置在这个目录下。
  •   %LOCALAPPDATA% – 每个用户单独访问的应用程序数据适合放置在这个目录下。 - (Windows Vista 或者Windows 7)
  •   %APPDATA% – 每个用户单独访问的应用程序数据适合放置在这个目录下。这是“随身文件夹”。- (Windows Vista 或者Windows 7)

智慧旅游解决方案利用云计算、物联网和移动互联网技术,通过便携终端设备,实现对旅游资源、经济、活动和旅游者信息的智能感知和发布。这种技术的应用旨在提升游客在旅游各个环节的体验,使他们能够轻松获取信息、规划行程、预订票务和安排食宿。智慧旅游平台为旅游管理部门、企业和游客提供服务,包括政策发布、行政管理、景区安全、游客流量统计分析、投诉反馈等。此外,平台还提供广告促销、库存信息、景点介绍、电子门票、社交互动等功能。 智慧旅游的建设规划得到了国家政策的支持,如《国家中长期科技发展规划纲要》和国务院的《关于加快发展旅游业的意见》,这些政策强调了旅游信息服务平台的建设和信息化服务的重要性。随着技术的成熟和政策环境的优化,智慧旅游的时机已经到来。 智慧旅游平台采用SaaS、PaaS和IaaS等云服务模式,提供简化的软件开发、测试和部署环境,实现资源的按需配置和快速部署。这些服务模式支持旅游企业、消费者和管理部门开发高性能、高可扩展的应用服务。平台还整合了旅游信息资源,提供了丰富的旅游产品创意平台和统一的旅游综合信息库。 智慧旅游融合应用面向游客和景区景点主管机构,提供无线城市门户、智能导游、智能门票及优惠券、景区综合安防、车辆及停车场管理等服务。这些应用通过物联网和云计算技术,实现了旅游服务的智能化、个性化和协同化,提高了旅游服务的自由度和信息共享的动态性。 智慧旅游的发展标志着旅游信息化建设的智能化和应用多样化趋势,多种技术和应用交叉渗透至旅游行业的各个方面,预示着全面的智慧旅游时代已经到来。智慧旅游不仅提升了游客的旅游体验,也为旅游管理和服务提供了高效的技术支持。
智慧旅游解决方案利用云计算、物联网和移动互联网技术,通过便携终端设备,实现对旅游资源、经济、活动和旅游者信息的智能感知和发布。这种技术的应用旨在提升游客在旅游各个环节的体验,使他们能够轻松获取信息、规划行程、预订票务和安排食宿。智慧旅游平台为旅游管理部门、企业和游客提供服务,包括政策发布、行政管理、景区安全、游客流量统计分析、投诉反馈等。此外,平台还提供广告促销、库存信息、景点介绍、电子门票、社交互动等功能。 智慧旅游的建设规划得到了国家政策的支持,如《国家中长期科技发展规划纲要》和国务院的《关于加快发展旅游业的意见》,这些政策强调了旅游信息服务平台的建设和信息化服务的重要性。随着技术的成熟和政策环境的优化,智慧旅游的时机已经到来。 智慧旅游平台采用SaaS、PaaS和IaaS等云服务模式,提供简化的软件开发、测试和部署环境,实现资源的按需配置和快速部署。这些服务模式支持旅游企业、消费者和管理部门开发高性能、高可扩展的应用服务。平台还整合了旅游信息资源,提供了丰富的旅游产品创意平台和统一的旅游综合信息库。 智慧旅游融合应用面向游客和景区景点主管机构,提供无线城市门户、智能导游、智能门票及优惠券、景区综合安防、车辆及停车场管理等服务。这些应用通过物联网和云计算技术,实现了旅游服务的智能化、个性化和协同化,提高了旅游服务的自由度和信息共享的动态性。 智慧旅游的发展标志着旅游信息化建设的智能化和应用多样化趋势,多种技术和应用交叉渗透至旅游行业的各个方面,预示着全面的智慧旅游时代已经到来。智慧旅游不仅提升了游客的旅游体验,也为旅游管理和服务提供了高效的技术支持。
深度学习是机器学习的一个子领域,它基于人工神经网络的研究,特别是利用多层次的神经网络来进行学习和模式识别。深度学习模型能够学习数据的高层次特征,这些特征对于图像和语音识别、自然语言处理、医学图像分析等应用至关重要。以下是深度学习的一些关键概念和组成部分: 1. **神经网络(Neural Networks)**:深度学习的基础是人工神经网络,它是由多个层组成的网络结构,包括输入层、隐藏层和输出层。每个层由多个神经元组成,神经元之间通过权重连接。 2. **前馈神经网络(Feedforward Neural Networks)**:这是最常见的神经网络类型,信息从输入层流向隐藏层,最终到达输出层。 3. **卷积神经网络(Convolutional Neural Networks, CNNs)**:这种网络特别适合处理具有网格结构的数据,如图像。它们使用卷积层来提取图像的特征。 4. **循环神经网络(Recurrent Neural Networks, RNNs)**:这种网络能够处理序列数据,如时间序列或自然语言,因为它们具有记忆功能,能够捕捉数据中的时间依赖性。 5. **长短期记忆网络(Long Short-Term Memory, LSTM)**:LSTM 是一种特殊的 RNN,它能够学习长期依赖关系,非常适合复杂的序列预测任务。 6. **生成对抗网络(Generative Adversarial Networks, GANs)**:由两个网络组成,一个生成器和一个判别器,它们相互竞争,生成器生成数据,判别器评估数据的真实性。 7. **深度学习框架**:如 TensorFlow、Keras、PyTorch 等,这些框架提供了构建、训练和部署深度学习模型的工具和库。 8. **激活函数(Activation Functions)**:如 ReLU、Sigmoid、Tanh 等,它们在神经网络中用于添加非线性,使得网络能够学习复杂的函数。 9. **损失函数(Loss Functions)**:用于评估模型的预测与真实值之间的差异,常见的损失函数包括均方误差(MSE)、交叉熵(Cross-Entropy)等。 10. **优化算法(Optimization Algorithms)**:如梯度下降(Gradient Descent)、随机梯度下降(SGD)、Adam 等,用于更新网络权重,以最小化损失函数。 11. **正则化(Regularization)**:技术如 Dropout、L1/L2 正则化等,用于防止模型过拟合。 12. **迁移学习(Transfer Learning)**:利用在一个任务上训练好的模型来提高另一个相关任务的性能。 深度学习在许多领域都取得了显著的成就,但它也面临着一些挑战,如对大量数据的依赖、模型的解释性差、计算资源消耗大等。研究人员正在不断探索新的方法来解决这些问题。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值