#include <stdio.h> #include <locale.h> #include <windows.h> #include <tlhelp32.h> #include <tchar.h> #include <Psapi.h> #pragma comment (lib,"Psapi.lib") BOOL DosPathToNtPath(LPTSTR pszDosPath, LPTSTR pszNtPath) { TCHAR szDriveStr[500]; TCHAR szDrive[3]; TCHAR szDevName[100]; INT iDevName; INT i; //檢查參數 if (!pszDosPath || !pszNtPath) return FALSE; //獲取本地磁盤所有盤符,以'\0'分隔,所以下面+4 if (GetLogicalDriveStrings(sizeof(szDriveStr), szDriveStr)) { for (i = 0; szDriveStr[i]; i += 4) { if (!lstrcmpi(&(szDriveStr[i]), _T("A:\\")) || !lstrcmpi(&(szDriveStr[i]), _T("B:\\"))) continue; //從C盤開始 //盤符 szDrive[0] = szDriveStr[i]; szDrive[1] = szDriveStr[i + 1]; szDrive[2] = '\0'; if (!QueryDosDevice(szDrive, szDevName, 100))//查詢 Dos 設備名(盤符由NT查詢DOS) return FALSE; iDevName = lstrlen(szDevName); if (_tcsnicmp(pszDosPath, szDevName, iDevName) == 0)//是否為此盤 { lstrcpy(pszNtPath, szDrive);//復制驅動器 lstrcat(pszNtPath, pszDosPath + iDevName);//復制路徑 return TRUE; } } } lstrcpy(pszNtPath, pszDosPath); return FALSE; } //獲取進程完整路徑 BOOL GetProcessFullPath(DWORD dwPID) { TCHAR szImagePath[MAX_PATH]; TCHAR pszFullPath[MAX_PATH]; HANDLE hProcess; if (!pszFullPath) return FALSE; pszFullPath[0] = '\0'; hProcess = OpenProcess(PROCESS_QUERY_INFORMATION, 0, dwPID); //由線程ID獲得線程信息 if (!hProcess) return FALSE; if (!GetProcessImageFileName(hProcess, szImagePath, MAX_PATH)) //得到線程完整DOS路徑 { CloseHandle(hProcess); return FALSE; } if (!DosPathToNtPath(szImagePath, pszFullPath)) //DOS路徑轉NT路徑 { CloseHandle(hProcess); return FALSE; } CloseHandle(hProcess); _tprintf(_T("%5d %s \r\n"), dwPID, pszFullPath); return TRUE; } int main(int argc, char* argv[]) { setlocale(LC_ALL, "chs"); //不設置解析中文字符時可能會出問題 HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); //得到系統所有線程快照 if (INVALID_HANDLE_VALUE == hSnapshot) { return NULL; } PROCESSENTRY32 pe = { 0 }; pe.dwSize = sizeof(PROCESSENTRY32); BOOL fOk; for (fOk = Process32First(hSnapshot, &pe); fOk; fOk = Process32Next(hSnapshot, &pe)) //遍歷 { GetProcessFullPath(pe.th32ProcessID); } return 0; }
為什么要使用setlocal呢(非本例)
在 VC2005 中 std::fstream 的打開文件的函數實現里,傳入的 char const* 文件名作為多字節首先被mbstowcs 轉換成寬字節后,再轉發給 Unicode 版本的 API 進行實際的打開文件操作
_MRTIMP2_NCEEPURE FILE *__CLRCALL_PURE_OR_CDECL _Fiopen(const char *filename, ios_base::openmode mode, int prot) { // open wide-named file with byte name wchar_twc_name[FILENAME_MAX]; if (mbstowcs_s(NULL, wc_name, FILENAME_MAX, filename,FILENAME_MAX - 1) != 0) return (0); return _Fiopen(wc_name, mode, prot);
}
問題的關鍵在於,對於 mbstowcs 函數來說,它需要知道多字節的編碼類型才能正確的將其轉換成寬字節的 unicode,很可惜這個編碼類型並沒有體現在函數的參數列表里,而是隱含依賴全局的 locale 。更加不幸的是,全局 locale 默認沒有使用系統當前語言,而是設置為沒什么用處的 "C" locale 。於是 GBK 編碼的文件名在 "C" locale 下轉換錯誤
在本機上vs2017運行本例時出現了無法打印中文字符串的現象