2016-11-27 67 views
1

我試圖創建一個從OPENFILENAME對話框獲取文件路徑的函數。我的代碼看起來像這樣。OPENFILENAME對話框返回亞洲字母代替文件路徑

wstring src; 

bool open() 
{ 
    const string title = "Select a File"; 

    wchar_t filename[MAX_PATH]; 

    OPENFILENAMEA ofn; 
    ZeroMemory(&filename, sizeof(filename)); 
    ZeroMemory(&ofn, sizeof(ofn)); 

    ofn.lStructSize  = sizeof(ofn); 
    ofn.hwndOwner  = NULL; 
    ofn.lpstrFilter  = "Music (.mp3)\0*.mp3\0All\0*.*\0"; 
    ofn.lpstrFile  = LPSTR(filename); 
    ofn.nMaxFile  = MAX_PATH; 
    ofn.lpstrTitle  = title.c_str(); 
    ofn.Flags   = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST; 

    if (GetOpenFileNameA(&ofn)) 
    { 
     src = filename; //<----------Save filepath in global variable 
     return true; 
    } 
    return false; 
} 

一旦在評論排放置一個斷點,我可以檢查「SRC」和「文件名」,它在這一點上是對我來說,亞裔無法辨認字母的價值。爲什麼會發生?這是一個轉換問題嗎?

編輯:

多虧了快速回復和少數意見,代碼是現在功能齊全。感謝Hans Passant提供了一個非常直接的解決方案,同時也非常感謝Cody Gray重寫函數,解釋錯誤,並且告訴我應該如何處理它。由於我仍然在學習winapi的第一步,這些信息將在未來的項目中爲我提供很好的幫助。

+0

嘗試'char filename [MAX_PATH];'而不是wchar_t –

+0

LPSTR轉換非常邪惡,這阻止了編譯器告訴你,你做錯了。然而,沒有阻止你做錯了。改爲使用OPENFILENAMEW和GetOpenFileNameW,使用L「blabla」生成寬字符串文字。 –

+0

'GetOpenFileNameA'是'GetOpenFileName'的ANSI子函數,所以你應該檢查你的代碼不是以Unicode編譯的。你應該使用'GetOpenFileName'而不是'GetOpenFileNameA',好的子功能的選擇將自動完成。 – Gwen

回答

3

這是由混合ANSI和Unicode字符類型引起的經典錯誤。它的標誌是字符串中出現隨機的亞洲原始字符,完全如您所描述的那樣。

快速瀏覽一下您的代碼會立即發現問題。您正在調用Win32函數的ANSI版本 - GetOpenFileNameA - 並使用ANSI版本的數據結構 - OPENFILENAMEA - 您的filename數組由寬字符(wchar_t)組成。在Win32中,A-後綴類型始終爲ANSI,並且需要使用1個字節的char類型。 W-後綴類型始終爲Unicode,並且需要使用2字節wchar_t類型。您不能在沒有明確轉換的情況下混合兩者,例如使用MultiByteToWideChar和/或WideCharToMultiByte

注意,編譯器會陷入這種類型不匹配錯誤,如果你沒有使用顯式強制關閉它。

在當今時代,你應該總是使用W -suffixed的API,因爲你總是要支持Unicode。世界不是ASCII,當每個人切換到Windows NT和Windows 98/ME死亡並被埋沒時,Windows ANSI編碼就變得過時了。

因此重寫代碼如下(我也冒昧改變一些其他的成語和清理其他方式的代碼,還有,喜歡用C++語言構造零內存):

std::wstring src; 

bool open() 
{ 
    const std::wstring title = L"Select a File"; 
    std::wstring filename(MAX_PATH, L'\0'); 

    OPENFILENAMEW ofn = { }; 
    ofn.lStructSize  = sizeof(ofn); 
    ofn.hwndOwner  = NULL; 
    ofn.lpstrFilter  = L"Music (.mp3)\0*.mp3\0All\0*.*\0"; 
    ofn.lpstrFile  = &filename[0]; // use the std::wstring buffer directly 
    ofn.nMaxFile  = MAX_PATH; 
    ofn.lpstrTitle  = title.c_str(); 
    ofn.Flags   = OFN_DONTADDTORECENT | OFN_FILEMUSTEXIST; 

    if (GetOpenFileNameW(&ofn)) 
    { 
     src = filename; //<----------Save filepath in global variable 
     return true; 
    } 
    return false; 
} 

爲了避免需要明確地鍵入W每一次,確保雙方UNICODE_UNICODE爲您的項目全局定義。最好的方法是使用項目屬性,您可以在其中預定義這些符號。否則,您可以在預編譯頭的頂部定義它們。這可確保功能和類型W -suffixed變種總是用,即使你忽略的後綴。所以你可以簡單地說GetOpenFileNameOPENFILENAME。 Windows頭文件中的宏處理分辨率。

這樣做的好處是,如果忘記在前綴L前面加上一個寬字符串文字,會導致編譯器生成類型不匹配錯誤,如果您還沒有習慣於這樣做,這是一個常見錯誤所以。 (當然,這是假定你還得到了使用顯式強制沉默編譯器警告,因爲你可以很容易地通過這樣打敗這個安全網的壞習慣。)

+0

非常翔實的解釋錯誤是如何發生的,如何解決這個問題,並指出爲什麼這個解決方案將被建議超過其他人。非常感謝。 – qwarten