編譯程序的時候設置一下
在項目屬性--鏈接器--清單文件--UAC執行級別改成requireAdministratorc++
void GainAdminPrivileges(CString strApp, UINT idd) { CString strCmd; strCmd.Format (_T("/adminoption %d"), idd); SHELLEXECUTEINFO execinfo; memset(&execinfo, 0, sizeof(execinfo)); execinfo.lpFile = strApp; execinfo.cbSize = sizeof(execinfo); execinfo.lpVerb = _T("runas"); execinfo.fMask = SEE_MASK_NO_CONSOLE; execinfo.nShow = SW_SHOWDEFAULT; execinfo.lpParameters = strCmd; ShellExecuteEx(&execinfo); }
strApp是應用程序的路徑idd我傳的是1,但好像傳幾都沒問題shell
BOOL ElevateCurrentProcess(CString sCmdLine) { TCHAR szPath[MAX_PATH] = {0}; if (::GetModuleFileName(NULL, szPath, MAX_PATH)) { // Launch itself as administrator. SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO) }; sei.lpVerb = _T("runas"); sei.lpFile = szPath; sei.lpParameters = (LPCTSTR)sCmdLine; // sei.hwnd = hWnd; sei.nShow = SW_SHOWNORMAL; if (!ShellExecuteEx(&sei)) { DWORD dwStatus = GetLastError(); if (dwStatus == ERROR_CANCELLED) { // The user refused to allow privileges elevation. return FALSE; } else if (dwStatus == ERROR_FILE_NOT_FOUND) { // The file defined by lpFile was not found and // an error message popped up. return FALSE; } return FALSE; } return TRUE; } return FALSE; }
當咱們那些在Windows 7以前設計的應用程序遇到UAC Virtualization問題的時候,咱們須要重新設計咱們的代碼,將文件寫入到合適的位置。在改善既有代碼,使之能夠與Windows 7兼容的時候,咱們應該確保如下幾點:編程
——在運行的時候,應用程序只會將數據保存到每一個用戶預先定義的位置或者是%alluserprofile% 中定義的普通用戶擁有訪問權限的位置。windows
——肯定你要寫入數據的「已知文件夾」(Knownfolders)。一般,全部用戶共用的公共數據文件應該寫入到一個全局的公共的位置,這樣全部用戶均可以訪問到。而其它數據則應該寫入每一個用戶本身的文件夾。api
1 公共數據文件包括日誌文件,配置文件(一般是INI或者XML文件),應用程序狀態文件,好比保存的遊戲進程等等。函數
2 而屬於每一個用戶的文檔,則應該保持在文檔目錄下,或者是用戶本身指定的目錄。ui
——當你肯定合適的文件保存位置後,不要在代碼中明文寫出(Hard-code)你選擇的路徑。爲了更好地保持兼容性,咱們應該採用下面這些API來得到操做系統「已知文件夾(Knownfolders)」的正確路徑。操作系統
1 C/C++非託管代碼: 使用SHGetKnownFolderPath函數,經過指定「已知文件夾」的KNOWNFOLDERID做爲參數來得到正確的文件夾路徑。.net
FOLDERID_ProgramData –全部用戶均可以訪問的應用程序數據適合放置在這個目錄下。 FOLDERID_LocalAppData – 每一個用戶單獨訪問的應用程序數據適合放置在這個目錄下。 FOLDERID_RoamingAppData – 每一個用戶單獨訪問的應用程序數據適合放置在這個目錄下。 與上面一個目錄不一樣的是,放置在這個目錄下的文件會隨着用戶遷移,當一個用戶在同一個域中的其餘計算機登陸的時候,這些文件會被複制到當前登陸的機器上,就像用戶隨身攜帶的公文包同樣。
下面這段代碼演示了在非託管代碼中如何調用shell函數,SHGetKnownFolderPath函數得到正確的文件保存路徑(SHGetFolderLocation, SHGetFolderPath, SHGetSpecialFolderLocation, SHGetSpecialFolderPath):設計
#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); } } // … } }
若是上面的方法都不適合你,你還可使用環境變量,使用getenv()或GetEnvironmentVariable()獲取相應的文件夾路徑:
%ALLUSERSPROFILE% – 全部用戶均可以訪問的應用程序數據適合放置在這個目錄下。 %LOCALAPPDATA% – 每一個用戶單獨訪問的應用程序數據適合放置在這個目錄下。 - (Windows Vista 或者Windows 7) %APPDATA% – 每一個用戶單獨訪問的應用程序數據適合放置在這個目錄下。這是「隨身文件夾」。- (Windows Vista 或者Windows 7)