2013-07-29 28 views
6

Sto cercando di ottenere la cartella desktop dell'utente in un'applicazione C++ (tramite una DLL) utilizzando SHGetSpecialFolderPath:Ottieni la cartella Desktop dell'utente utilizzando l'API di Windows?

#define _WIN32_WINNT 0x0500 
#define _WIN32_IE  0x0500 
#define CSIDL_MYMUSIC 0x000D 
#define CSIDL_MYVIDEO 0x000E 

#include "dll.h" 
#include <windows.h> 
#include <shlobj.h> 
#include <stdio.h> 

TCHAR path[MAX_PATH]; 

export LPSTR desktop_directory() 
{ 

    if (SHGetSpecialFolderPath(HWND_DESKTOP, path, CSIDL_DESKTOP, FALSE)) { 
     return path; 
    } 

} 

Prima di tutto voglio restituire un caso altro. Ritorna "ERROR" ma il compilatore mi avvisa che sta provando a convertire un CHAR in un LPSTR. Con quello se là, sembra che la DLL potrebbe bloccarsi se non può ottenere la directory per qualche motivo.

Anche dalla documentazione MSDN, si dice "[SHGetSpecialFolderPath non è supportato.Utilizzare invece ShGetFolderPath.]", Quindi si accede a tale pagina e viene visualizzato "ShGetFolderPath: Deprecated. Ottiene il percorso di una cartella identificata da un Valore CSIDL. " Cosa dovrei usare invece?

Quindi:

  1. Voglio aggiungere un caso altrove torno una stringa che dice "ERROR"
  2. Voglio sapere se sto usando la funzione API non deprecato corretto che lavorerà per il moderno sistema operativo Windows fino a Windows XP.

EDIT

Ecco il codice aggiornato come richiesto,

#ifndef UNICODE 
#define UNICODE 
#endif 

#ifndef _UNICODE 
#define _UNICODE 
#endif 

#define _WIN32_WINNT 0x0500 
#define _WIN32_IE 0x0500 
#define CSIDL_MYMUSIC 0x000D 
#define CSIDL_MYVIDEO 0x000E 

#include "dll.h" 
#include <windows.h> 
#include <shlobj.h> 
#include <stdio.h> 

export LPCWSTR desktop_directory() 
{ 

    static wchar_t path[MAX_PATH+1]; 

    if (SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, path)) { 
     MessageBox(NULL, path, L"TEST", MB_OK); //test 
     return path; 
    } else { 
     return L"ERROR"; 
    } 

} 

compilazione con MinGW utilizzando: g++ "src\dll\main.cpp" -D UNICODE -D _UNICODE -O3 -DNDEBUG -s -shared -o "output\main.dll"

ho bisogno di passare la stringa dalla DLL come UTF 8 utilizzando WideCharToMultiByte(CP_UTF8, ...), ma non sono sicuro di come farlo.

+0

Attualmente consiglia [SHGetKnownFolderPath] (http://msdn.microsoft.com/en-us/library/windows/desktop/bb762188.aspx), ma questo è stato aggiunto per la prima volta in Vista. Credo che tutti quelli che lavorano con XP siano ufficialmente deprecati. –

risposta

9

SHGetFolderPath() restituisce un HRESULT, dove 0 è S_OK, ma il codice si aspetta che per restituire un BOOL come SHGetSpecialFolderPath() fa, dove 0 è FALSE. Quindi devi correggere quell'errore nel tuo codice, poiché attualmente sta trattando un successo come se fosse un fallimento.

Detto ciò, si restituisce un LPSTR dalla propria funzione. Questo è un char*. Ma stai usando TCHAR per il tuo buffer. TCHAR mappe a char o wchar_t a seconda che sia definito o meno UNICODE. Quindi è necessario decidere se si desidera restituire un char* incondizionatamente o se si desidera restituire un TCHAR* o entrambi. Si fa una grande differenza, ad esempio:

#define _WIN32_WINNT 0x0500 
#define _WIN32_IE  0x0500 
#define CSIDL_MYMUSIC 0x000D 
#define CSIDL_MYVIDEO 0x000E 

#include "dll.h" 
#include <windows.h> 
#include <shlobj.h> 
#include <stdio.h> 

export LPSTR desktop_directory() 
{ 
    static char path[MAX_PATH+1]; 
    if (SHGetSpecialFolderPathA(HWND_DESKTOP, path, CSIDL_DESKTOP, FALSE)) 
     return path; 
    else 
     return "ERROR"; 
} 

Versus:

#define _WIN32_WINNT 0x0500 
#define _WIN32_IE  0x0500 
#define CSIDL_MYMUSIC 0x000D 
#define CSIDL_MYVIDEO 0x000E 

#include "dll.h" 
#include <windows.h> 
#include <shlobj.h> 
#include <stdio.h> 

export LPTSTR desktop_directory() 
{ 
    static TCHAR path[MAX_PATH+1]; 
    if (SHGetSpecialFolderPath(HWND_DESKTOP, path, CSIDL_DESKTOP, FALSE)) 
     return path; 
    else 
     return TEXT("ERROR"); 
} 

Versus:

#define _WIN32_WINNT 0x0500 
#define _WIN32_IE  0x0500 
#define CSIDL_MYMUSIC 0x000D 
#define CSIDL_MYVIDEO 0x000E 

#include "dll.h" 
#include <windows.h> 
#include <shlobj.h> 
#include <stdio.h> 

export LPSTR desktop_directory_ansi() 
{ 
    static char path[MAX_PATH+1]; 
    if (SHGetSpecialFolderPathA(HWND_DESKTOP, path, CSIDL_DESKTOP, FALSE)) 
     return path; 
    else 
     return "ERROR"; 
} 

export LPWSTR desktop_directory_unicode() 
{ 
    static wchar_t path[MAX_PATH+1]; 
    if (SHGetSpecialFolderPathW(HWND_DESKTOP, path, CSIDL_DESKTOP, FALSE)) 
     return path; 
    else 
     return L"ERROR"; 
} 

Aggiornamento: la maggior parte delle funzioni API Win32 non supportano UTF-8, quindi se si desidera che la funzione restituisca una stringa UTF-8, sarà necessario richiamare l'aroma Unicode delle funzioni e quindi utilizzare WideCharToMultiByte() per convertire l'output in UTF-8. Ma poi hai un problema - chi assegna e libera il buffer UTF-8?Ci sono diversi modi per gestire che:

  1. utilizzare un buffer statico thread-safe (ma attenzione per this gotcha). Se non è necessario preoccuparsi di più thread di accesso a funzioni, quindi rilasciare il __declspec(thread) specificatore:

    #define _WIN32_WINNT 0x0500 
    #define _WIN32_IE 0x0500 
    #define CSIDL_MYMUSIC 0x000D 
    #define CSIDL_MYVIDEO 0x000E 
    
    #include "dll.h" 
    #include <windows.h> 
    #include <shlobj.h> 
    #include <stdio.h> 
    
    __declspec(thread) char desktop_dir_buffer[((MAX_PATH*4)+1]; 
    
    export LPCSTR desktop_directory() 
    { 
        wchar_t path[MAX_PATH+1] = {0}; 
    
        if (SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, path) != S_OK) 
        { 
         MessageBoxW(NULL, L"ERROR in SHGetFolderPathW", L"TEST", MB_OK); 
         return NULL; 
        } 
    
        MessageBoxW(NULL, path, L"TEST", MB_OK); 
    
        int buflen = WideCharToMultiByte(CP_UTF8, 0, path, lstrlenW(path), desktop_dir_buffer, MAX_PATH*4, NULL, NULL); 
        if (buflen <= 0) 
        { 
         MessageBoxW(NULL, L"ERROR in WideCharToMultiByte", L"TEST", MB_OK); 
         return NULL; 
        } 
    
        desktop_dir_buffer[buflen] = 0; 
    
        return desktop_dir_buffer; 
    } 
    
  2. hanno la DLL allocare dinamicamente il buffer utilizzando il proprio gestore di memoria e tornare al chiamante, e poi richiedere al chiamante di passare il buffer torna alla DLL quando fatto utilizzando in modo che possa essere liberata con gestore di memoria della DLL:

    #define _WIN32_WINNT 0x0500 
    #define _WIN32_IE 0x0500 
    #define CSIDL_MYMUSIC 0x000D 
    #define CSIDL_MYVIDEO 0x000E 
    
    #include "dll.h" 
    #include <windows.h> 
    #include <shlobj.h> 
    #include <stdio.h> 
    
    export LPCSTR desktop_directory() 
    { 
        wchar_t path[MAX_PATH+1] = {0}; 
    
        if (SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, 0, path) != S_OK) 
        { 
         MessageBoxW(NULL, L"ERROR in SHGetFolderPathW", L"TEST", MB_OK); 
         return NULL; 
        } 
    
        MessageBoxW(NULL, path, L"TEST", MB_OK); 
    
        int pathlen = lstrlenW(path); 
    
        int buflen = WideCharToMultiByte(CP_UTF8, 0, path, pathlen, NULL, 0, NULL, NULL); 
        if (buflen <= 0) 
        { 
         MessageBoxW(NULL, L"ERROR in WideCharToMultiByte", L"TEST", MB_OK); 
         return NULL; 
        } 
    
        char *buffer = new char[buflen+1]; 
        buflen = WideCharToMultiByte(CP_UTF8, 0, path, pathlen, buffer, buflen, NULL, NULL); 
        if (buflen <= 0) 
        { 
         delete[] buffer; 
         MessageBoxW(NULL, L"ERROR in WideCharToMultiByte", L"TEST", MB_OK); 
         return NULL; 
        } 
        buffer[buflen] = 0; 
    
        return buffer; 
    } 
    
    export void free_buffer(LPVOID buffer) 
    { 
        delete[] (char*) buffer; 
    } 
    
  3. hanno il DLL alloca dinamicamente il buffer utilizzando un gestore di Win32 memoria API e restituirlo al chiamante, e quindi il chiamante può deallocarlo utilizzando la stessa memoria API Win32 gestore senza dover passare nuovamente alla DLL per liberarlo:.

    #define _WIN32_WINNT 0x0500 
    #define _WIN32_IE 0x0500 
    #define CSIDL_MYMUSIC 0x000D 
    #define CSIDL_MYVIDEO 0x000E 
    
    #include "dll.h" 
    #include <windows.h> 
    #include <shlobj.h> 
    #include <stdio.h> 
    
    export LPCSTR desktop_directory() 
    { 
        wchar_t path[MAX_PATH+1] = {0}; 
    
        if (SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, 0, path) != S_OK) 
        { 
         MessageBoxW(NULL, L"ERROR in SHGetFolderPathW", L"TEST", MB_OK); 
         return NULL; 
        } 
    
        MessageBoxW(NULL, path, L"TEST", MB_OK); 
    
        int pathlen = lstrlenW(path); 
    
        int buflen = WideCharToMultiByte(CP_UTF8, 0, path, pathlen, NULL, 0, NULL, NULL); 
        if (buflen <= 0) 
        { 
         MessageBoxW(NULL, L"ERROR in WideCharToMultiByte", L"TEST", MB_OK); 
         return NULL; 
        } 
    
        char *buffer = (char*) LocalAlloc(LMEM_FIXED, buflen+1); 
        if (!buffer) 
        { 
         MessageBoxW(NULL, L"ERROR in LocalAlloc", L"TEST", MB_OK); 
         return NULL; 
        } 
    
        buflen = WideCharToMultiByte(CP_UTF8, 0, path, pathlen, buffer, buflen, NULL, NULL); 
        if (buflen <= 0) 
        { 
         LocalFree(buffer); 
         MessageBoxW(NULL, L"ERROR in WideCharToMultiByte", L"TEST", MB_OK); 
         return NULL; 
        } 
        buffer[buflen] = 0; 
    
        return buffer; // caller can use LocalFree() to free it 
    } 
    
  4. hanno superato il chiamante nel proprio buffer che il DLL riempie semplicemente in questo modo, il chiamante può decidere il modo migliore per allocare e gratuitamente:

    #define _WIN32_WINNT 0x0500 
    #define _WIN32_IE 0x0500 
    #define CSIDL_MYMUSIC 0x000D 
    #define CSIDL_MYVIDEO 0x000E 
    
    #include "dll.h" 
    #include <windows.h> 
    #include <shlobj.h> 
    #include <stdio.h> 
    
    // the caller can set buffer=NULL and buflen=0 to calculate the needed buffer size 
    export int desktop_directory(LPSTR buffer, int buflen) 
    { 
        wchar_t path[MAX_PATH+1] = {0}; 
    
        if (SHGetFolderPathW(NULL, CSIDL_DESKTOP, NULL, 0, path) != S_OK) 
        { 
         MessageBoxW(NULL, L"ERROR in SHGetFolderPathW", L"TEST", MB_OK); 
         return -1; 
        } 
    
        MessageBoxW(NULL, path, L"TEST", MB_OK); 
    
        int pathlen = lstrlenW(path); 
    
        int len = WideCharToMultiByte(CP_UTF8, 0, path, pathlen, buffer, buflen, NULL, NULL); 
        if (len <= 0) 
        { 
         MessageBoxW(NULL, L"ERROR in WideCharToMultiByte", L"TEST", MB_OK); 
         return -1; 
        } 
    
        if (!buffer) 
         ++len; 
        else if (len < buflen) 
         buffer[len] = 0; 
    
        return len; 
    } 
    
+1

Grazie, il codice ANSI fornisce un avviso di conversione CHAR *, ma userò invece la modalità unicode. Cercherò di usare questo con SHGetFolderPath() – user780756

+1

Questo, esportazione LPWSTR desktop_directory() { \t statico percorso wchar_t [MAX_PATH + 1]; \t if (SHGetFolderPath (NULL, CSIDL_DESKTOP, NULL, 0, percorso)) { \t \t percorso di ritorno; } else { return L "ERROR"; \t} \t } lamenta che il 'percorso' non è di tipo LPTSTR per la funzione SHGetFolderPath. Potrei usare un percorso cast (LPTSTR) sulla funzione, ma sono sicuro che sarebbe il modo sbagliato di farlo? : c – user780756

+1

Dato che si utilizza 'LPWSTR' e' wchar_t' in modo esplicito, è necessario usare 'SHGetFolderPathW()' in modo esplicito per farlo corrispondere. 'SHGetFolderPath()' esegue il mapping su 'SHGetFolderPathA()' o 'SHGetFolderPathW()' a seconda che sia definito 'UNICODE', proprio come' TCHAR' fa per 'char' e' wchar_t'. La mia ipotesi è che il tuo progetto non abbia effettivamente definito "UNICODE". –

0

C'è un modo migliore di fare questo che WidecharToMultibyte

wstring wcharthingy = wstring(widechar); 
string convertedthingy = string(wcharthingy.Begin(),wcharthingy.end()) 

Questo è più pseudocodice perché non ricordo esattamente, ma Visual Studio o XCode probabilmente correggeranno anywayay.

Problemi correlati