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:
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++ "src\dll\main.cpp" -D UNICODE -D _UNICODE -O3 -DNDEBUG -s -shared -o "output\main.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:
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;
}
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;
}
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
}
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;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With