【问题标题】:Get the user's Desktop folder using Windows API?使用 Windows API 获取用户的桌面文件夹?
【发布时间】:2013-07-29 20:49:07
【问题描述】:

我正在尝试使用SHGetSpecialFolderPath 在 C++ 应用程序(通过 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>

TCHAR path[MAX_PATH];

export LPSTR desktop_directory()
{

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

}

首先我想返回一个 else 案例。我返回“错误”,但编译器警告我试图将CHAR 转换为LPSTR。如果在那里,如果由于某种原因无法获取目录,DLL 可能会崩溃。

同样来自 MSDN 文档,它说“[SHGetSpecialFolderPath is not supported. instead, use ShGetFolderPath.]”,然后我导航到该页面并显示“ShGetFolderPath:已弃用。获取由 CSIDL 标识的文件夹的路径价值。”我应该改用什么?

所以:

  1. 我想添加一个 else 案例,其中我返回一个字符串“错误”
  2. 我想知道我是否使用了正确的、未弃用的 API 函数,该函数适用于早在 Windows XP 的现代 Windows 操作系统。

编辑

这是根据要求更新的代码,

#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";
    }

}

使用 MinGW 编译:g++ "src\dll\main.cpp" -D UNICODE -D _UNICODE -O3 -DNDEBUG -s -shared -o "output\main.dll"

我需要使用 WideCharToMultiByte(CP_UTF8, ...) 将 DLL 中的字符串作为 UTF-8 传递,但我不知道该怎么做。

【问题讨论】:

  • 他们目前推荐SHGetKnownFolderPath,但这首先是在Vista中添加的。我相信所有与 XP 一起工作的工具都已被正式弃用。

标签: c++ windows winapi


【解决方案1】:

SHGetFolderPath() 返回 HRESULT,其中 0 是 S_OK,但您的代码期望它返回 BOOL,就像 SHGetSpecialFolderPath() 一样,其中 0 是 FALSE。所以你需要在你的代码中修复这个错误,因为它当前将成功视为失败。

话虽如此,您正在从您的函数中返回一个LPSTR。那是char*。但是您使用 TCHAR 作为缓冲区。 TCHAR 映射到 charwchar_t 取决于是否定义了 UNICODE。因此,您需要决定是要无条件返回char*,还是要返回TCHAR*,或两者兼而有之。它有很大的不同,例如:

#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";
}

对比:

#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");
}

对比:

#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";
}

更新: 大多数 Win32 API 函数不支持 UTF-8,因此如果您希望函数返回 UTF-8 字符串,则必须调用函数的 Unicode 风格,并且然后使用WideCharToMultiByte() 将输出转换为UTF-8。但是你有一个问题 - 谁分配和释放 UTF-8 缓冲区?有几种不同的处理方法:

  1. 使用线程安全的静态缓冲区(但要注意this gotcha)。如果您不需要担心多个线程访问该函数,则删除 __declspec(thread) 说明符:

    #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. 让 DLL 使用自己的内存管理器动态分配缓冲区并将其返回给调用者,然后要求调用者在使用完缓冲区后将缓冲区传递回 DLL,以便可以使用 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. 让 DLL 使用 Win32 API 内存管理器动态分配缓冲区并将其返回给调用者,然后调用者可以使用相同的 Win32 API 内存管理器释放它,而无需将其传递回 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 = (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. 让调用者传入它自己的缓冲区,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>
    
    // 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;
    }
    

【讨论】:

  • 谢谢,ANSI 代码给出了 CHAR* 转换警告,但我将使用 unicode 方式。我将尝试将它与 SHGetFolderPath() 一起使用
  • 这个,export LPWSTR desktop_directory() { static wchar_t path[MAX_PATH+1]; if (SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, path)) { 返回路径; } else { 返回 L“错误”; } } 抱怨“路径”不是 SHGetFolderPath 函数的 LPTSTR 类型。我可以在函数上使用强制转换 (LPTSTR) 路径,但我确定这样做是错误的方法吗? :c
  • 由于你明确使用LPWSTRwchar_t,你需要明确使用SHGetFolderPathW()来匹配。 SHGetFolderPath() 映射到 SHGetFolderPathA()SHGetFolderPathW() 取决于是否定义了 UNICODE,就像 TCHARcharwchar_t 所做的那样。我的猜测是您的项目实际上没有定义UNICODE
  • 我添加了 #define UNICODE #define _UNICODE 并且不得不返回一个 LPCWSTR(导出 LPCWSTR desktop_directory())。这是正确的,不是吗?
  • UNICODE 应该在项目设置中启用,而不是在代码中。不,您不需要返回LPCWSTR,如果您要依赖UNICODE,则返回LPTSTR,否则LPWSTR 可以。
【解决方案2】:

有比WidecharToMultibyte更好的方法来做到这一点

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

这是更多的伪代码,因为我记不太清了,但 Visual Studio 或 XCode 可能会更正你。

【讨论】:

  • 这将使你的整个 Unicode 字符串出现乱码,除非它只包含 ASCII 字符。
猜你喜欢
  • 1970-01-01
  • 2013-07-21
  • 2020-09-08
  • 2011-08-29
  • 1970-01-01
  • 1970-01-01
  • 2014-09-09
  • 1970-01-01
  • 2020-08-30
相关资源
最近更新 更多