技术开发 频道

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

  如何获取正确的文件路径

  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;
}
0
相关文章