使用 Windows API 获取用户的桌面文件夹?

时间:2023-04-12
本文介绍了使用 Windows API 获取用户的桌面文件夹?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着跟版网的小编来一起学习吧!

问题描述

我正在尝试使用 SHGetSpecialFolderPath 在 C++ 应用程序(通过 DLL)中获取用户的桌面文件夹:

#define _WIN32_WINNT 0x0500#define _WIN32_IE 0x0500#define CSIDL_MYMUSIC 0x000D#define CSIDL_MYVIDEO 0x000E#include "dll.h"#include #include #include TCHAR 路径[MAX_PATH];导出 LPSTR desktop_directory(){如果(SHGetSpecialFolderPath(HWND_DESKTOP,路径,CSIDL_DESKTOP,FALSE)){返回路径;}}

首先我想返回一个 else 案例.我返回ERROR",但编译器警告我正在尝试将 CHAR 转换为 LPSTR.有了它,如果由于某种原因无法获取目录,则 DLL 看起来可能会崩溃.

同样来自 MSDN 文档,它说[不支持 SHGetSpecialFolderPath.相反,使用 ShGetFolderPath.]",然后我导航到该页面,它说ShGetFolderPath:已弃用.获取由 CSIDL 标识的文件夹的路径价值."我应该用什么代替?

所以:

  1. 我想添加一个 else 情况,我返回一个字符串,上面写着错误"
  2. 我想知道我是否使用了正确的未弃用 API 函数,该函数可以在现代 Windows 操作系统中使用,最早可以追溯到 Windows XP.

编辑

这是按要求更新的代码,

#ifndef UNICODE#define UNICODE#万一#ifndef _UNICODE#define _UNICODE#万一#define _WIN32_WINNT 0x0500#define _WIN32_IE 0x0500#define CSIDL_MYMUSIC 0x000D#define CSIDL_MYVIDEO 0x000E#include "dll.h"#include #include #include 导出 LPCWSTR desktop_directory(){静态 wchar_t 路径[MAX_PATH+1];如果(SHGetFolderPath(NULL,CSIDL_DESKTOP,NULL,0,路径)){MessageBox(NULL, path, L"TEST", MB_OK);//测试返回路径;} 别的 {返回 L"错误";}}

使用 MinGW 编译:g++ "srcdllmain.cpp" -D UNICODE -D _UNICODE -O3 -DNDEBUG -s -shared -o "outputmain.dll"

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

解决方案

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 #include #include 导出 LPSTR desktop_directory(){静态字符路径[MAX_PATH+1];如果(SHGetSpecialFolderPathA(HWND_DESKTOP,路径,CSIDL_DESKTOP,FALSE))返回路径;别的返回错误";}

对比:

#define _WIN32_WINNT 0x0500#define _WIN32_IE 0x0500#define CSIDL_MYMUSIC 0x000D#define CSIDL_MYVIDEO 0x000E#include "dll.h"#include #include #include 导出 LPTSTR desktop_directory(){静态 TCHAR 路径[MAX_PATH+1];如果(SHGetSpecialFolderPath(HWND_DESKTOP,路径,CSIDL_DESKTOP,FALSE))返回路径;别的返回文本(错误");}

对比:

#define _WIN32_WINNT 0x0500#define _WIN32_IE 0x0500#define CSIDL_MYMUSIC 0x000D#define CSIDL_MYVIDEO 0x000E#include "dll.h"#include #include #include 导出 LPSTR desktop_directory_ansi(){静态字符路径[MAX_PATH+1];如果(SHGetSpecialFolderPathA(HWND_DESKTOP,路径,CSIDL_DESKTOP,FALSE))返回路径;别的返回错误";}导出 LPWSTR desktop_directory_unicode(){静态 wchar_t 路径[MAX_PATH+1];如果(SHGetSpecialFolderPathW(HWND_DESKTOP,路径,CSIDL_DESKTOP,FALSE))返回路径;别的返回 L"错误";}

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

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

    #define _WIN32_WINNT 0x0500#define _WIN32_IE 0x0500#define CSIDL_MYMUSIC 0x000D#define CSIDL_MYVIDEO 0x000E#include "dll.h"#include #include #include __declspec(thread) char desktop_dir_buffer[((MAX_PATH*4)+1];导出 LPCSTR desktop_directory(){wchar_t 路径[MAX_PATH+1] = {0};if (SHGetFolderPathW(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, path) != S_OK){MessageBoxW(NULL, L"ERROR in SHGetFolderPathW", L"TEST", MB_OK);返回空;}MessageBoxW(NULL, path, L"TEST", MB_OK);int buflen = WideCharToMultiByte(CP_UTF8, 0, path, lstrlenW(path), desktop_dir_buffer, MAX_PATH*4, NULL, NULL);如果(buflen <= 0){MessageBoxW(NULL, L"WideCharToMultiByte 中的错误", L"TEST", MB_OK);返回空;}desktop_dir_buffer[buflen] = 0;返回桌面目录缓冲区;}

  2. 让 DLL 使用自己的内存管理器动态分配缓冲区并将其返回给调用者,然后要求调用者在使用完缓冲区后将缓冲区传递回 DLL,以便可以通过 DLL 的释放它内存管理器:

    #define _WIN32_WINNT 0x0500#define _WIN32_IE 0x0500#define CSIDL_MYMUSIC 0x000D#define CSIDL_MYVIDEO 0x000E#include "dll.h"#include #include #include 导出 LPCSTR desktop_directory(){wchar_t 路径[MAX_PATH+1] = {0};如果(SHGetFolderPathW(NULL,CSIDL_DESKTOP,NULL,0,路径)!= S_OK){MessageBoxW(NULL, L"ERROR in SHGetFolderPathW", L"TEST", MB_OK);返回空;}MessageBoxW(NULL, path, L"TEST", MB_OK);int pathlen = lstrlenW(path);int buflen = WideCharToMultiByte(CP_UTF8, 0, path, pathlen, NULL, 0, NULL, NULL);如果(buflen <= 0){MessageBoxW(NULL, L"WideCharToMultiByte 中的错误", L"TEST", MB_OK);返回空;}char *buffer = new char[buflen+1];buflen = WideCharToMultiByte(CP_UTF8, 0, path, pathlen, buffer, buflen, NULL, NULL);如果(buflen <= 0){删除[]缓冲区;MessageBoxW(NULL, L"WideCharToMultiByte 中的错误", L"TEST", MB_OK);返回空;}缓冲区[buflen] = 0;返回缓冲区;}导出 void free_buffer(LPVOID 缓冲区){删除[](字符*)缓冲区;}

  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 #include #include 导出 LPCSTR desktop_directory(){wchar_t 路径[MAX_PATH+1] = {0};如果(SHGetFolderPathW(NULL,CSIDL_DESKTOP,NULL,0,路径)!= S_OK){MessageBoxW(NULL, L"ERROR in SHGetFolderPathW", L"TEST", MB_OK);返回空;}MessageBoxW(NULL, path, L"TEST", MB_OK);int pathlen = lstrlenW(path);int buflen = WideCharToMultiByte(CP_UTF8, 0, path, pathlen, NULL, 0, NULL, NULL);如果(buflen <= 0){MessageBoxW(NULL, L"WideCharToMultiByte 中的错误", L"TEST", MB_OK);返回空;}char *buffer = (char*) LocalAlloc(LMEM_FIXED, buflen+1);如果(!缓冲){MessageBoxW(NULL, L"ERROR in LocalAlloc", L"TEST", MB_OK);返回空;}buflen = WideCharToMultiByte(CP_UTF8, 0, path, pathlen, buffer, buflen, NULL, NULL);如果(buflen <= 0){本地自由(缓冲区);MessageBoxW(NULL, L"WideCharToMultiByte 中的错误", L"TEST", MB_OK);返回空;}缓冲区[buflen] = 0;返回缓冲区;//调用者可以使用 LocalFree() 来释放它}

  4. 让调用者在自己的缓冲区中传递 DLL 简单地填充.这样,调用者可以决定分配和释放它的最佳方式:

    #define _WIN32_WINNT 0x0500#define _WIN32_IE 0x0500#define CSIDL_MYMUSIC 0x000D#define CSIDL_MYVIDEO 0x000E#include "dll.h"#include #include #include //调用者可以设置buffer=NULL和buflen=0来计算需要的缓冲区大小导出 int desktop_directory(LPSTR 缓冲区,int buflen){wchar_t 路径[MAX_PATH+1] = {0};如果(SHGetFolderPathW(NULL,CSIDL_DESKTOP,NULL,0,路径)!= S_OK){MessageBoxW(NULL, L"ERROR in SHGetFolderPathW", L"TEST", MB_OK);返回-1;}MessageBoxW(NULL, path, L"TEST", MB_OK);int pathlen = lstrlenW(path);int len = WideCharToMultiByte(CP_UTF8, 0, path, pathlen, buffer, buflen, NULL, NULL);如果 (len <= 0){MessageBoxW(NULL, L"WideCharToMultiByte 中的错误", L"TEST", MB_OK);返回-1;}如果(!缓冲)++连;否则如果 (len < buflen)缓冲区[len] = 0;返回 len;}

I am trying to get the user's Desktop folder in a C++ application (via a DLL) by using SHGetSpecialFolderPath:

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

}

First I want to return an else case. I return "ERROR" but the compiler warns me that is trying to convert a CHAR to a LPSTR. With that if there, it looks that the DLL might crash if it cannot get the directory for some reason.

Also from the MSDN documentation, it says "[SHGetSpecialFolderPath is not supported. Instead, use ShGetFolderPath.]", then I navigate to that page and it says "ShGetFolderPath: Deprecated. Gets the path of a folder identified by a CSIDL value." What am I supposed to use instead?

So:

  1. I want to add an else case where I return a string saying "ERROR"
  2. I want to know if I am using the correct non-deprecated API function that will work to the modern Windows OS as far back as Windows XP.

EDIT

Here is the updated code as requested,

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

}

Compiling with MinGW using: g++ "srcdllmain.cpp" -D UNICODE -D _UNICODE -O3 -DNDEBUG -s -shared -o "outputmain.dll"

I need to pass the string from the DLL as UTF-8 using WideCharToMultiByte(CP_UTF8, ...), but I am not sure how to do that.

解决方案

SHGetFolderPath() returns an HRESULT, where 0 is S_OK, but your code is expecting it to return a BOOL like SHGetSpecialFolderPath() does, where 0 is FALSE. So you need to fix that mistake in your code, as it is currently treating a success as if it were a failure instead.

With that said, you are returning a LPSTR from your function. That is a char*. But you are using TCHAR for your buffer. TCHAR maps to either char or wchar_t depending on whether UNICODE is defined or not. So you need to decide if you want to return a char* unconditionally, or if you want to return a TCHAR*, or both. It makes a big difference, eg:

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

Update: most Win32 API functions do not support UTF-8, so if you want the function to return a UTF-8 string, you will have to call the Unicode flavor of the functions, and then use WideCharToMultiByte() to convert the output to UTF-8. But then you have a problem - who allocates and frees the UTF-8 buffer? There are several different ways to handle that:

  1. use a thread-safe static buffer (but watch out for this gotcha). If you don't need to worry about multiple threads accessing the function, then drop the __declspec(thread) specifier:

    #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. have the DLL dynamically allocate the buffer using its own memory manager and return it to the caller, and then require the caller to pass the buffer back to the DLL when done using it so it can be freed with the DLL's memory manager:

    #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. have the DLL dynamically allocate the buffer using a Win32 API memory manager and return it to the caller, and then the caller can deallocate it using the same Win32 API memory manager without having to pass it back to the DLL to free it:

    #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. have the caller pass in its own buffer that the DLL simply fills in. That way, the caller can decide the best way to allocate and free it:

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

这篇关于使用 Windows API 获取用户的桌面文件夹?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持跟版网!

上一篇:运行时检查失败 #0 从 kernel32.dll 加载 QueryFullProcessImageName 下一篇:加载 DLL 不初始化静态 C++ 类

相关文章