SoFunction
Updated on 2025-03-07

Detailed explanation of how to export Chinese folders using SHBrowseForFolder in C#

Preface

Since I started working, I have been hitting the code pit several times, but this time I missed it. It’s really a matter of three things.

Originally, I didn't plan to show this small problem, but after looking through Google, I found that a few years ago, there were only a few people who encountered this problem, and they did not provide a feasible solution. Now the problem is still in CSDN and other places. It seems that no one will answer it anymore, or the questioners solved it later but did not look back to provide a solution. Now I will "end this post"

SHBrowseForFolder is a Windows API that can be used to get folder paths. It is much more convenient to use. The article will introduce the relevant content about using SHBrowseForFolder to export Chinese folders in C#. I won’t say much below. Let’s take a look at the detailed introduction together.

0x00. Select folder using SHBrowseForFolder

(Large piece of code is coming, if you don’t want to see it, you can just pull it to see the key lines)

Underlying interface – Select folder related

//-------------------------------------------------------------------------
class Win32API
{
 // C# representation of the IMalloc interface.
 [InterfaceType(),
 Guid("00000002-0000-0000-C000-000000000046")]
 public interface IMalloc
 {
 [PreserveSig]
 IntPtr Alloc([In] int cb);
 [PreserveSig]
 IntPtr Realloc([In] IntPtr pv, [In] int cb);
 [PreserveSig]
 void Free([In] IntPtr pv);
 [PreserveSig]
 int GetSize([In] IntPtr pv);
 [PreserveSig]
 int DidAlloc(IntPtr pv);
 [PreserveSig]
 void HeapMinimize();
 }

 [StructLayout(, Pack = 8)]
 public struct BROWSEINFO
 {
 public IntPtr hwndOwner;
 public IntPtr pidlRoot;
 public IntPtr pszDisplayName;
 [MarshalAs()]
 public string lpszTitle;
 public int ulFlags;
 [MarshalAs()]
 public  lpfn;
 public IntPtr lParam;
 public int iImage;
 }

 [Flags]
 public enum BffStyles
 {
 RestrictToFilesystem = 0x0001, // BIF_RETURNONLYFSDIRS
 RestrictToDomain = 0x0002, // BIF_DONTGOBELOWDOMAIN
 RestrictToSubfolders = 0x0008, // BIF_RETURNFSANCESTORS
 ShowTextBox = 0x0010, // BIF_EDITBOX
 ValidateSelection = 0x0020, // BIF_VALIDATE
 NewDialogStyle = 0x0040, // BIF_NEWDIALOGSTYLE
 BrowseForComputer = 0x1000, // BIF_BROWSEFORCOMPUTER
 BrowseForPrinter = 0x2000, // BIF_BROWSEFORPRINTER
 BrowseForEverything = 0x4000, // BIF_BROWSEINCLUDEFILES
 }

 [StructLayout(, CharSet = )]
 public class OpenFileName
 {
 public int structSize = 0;
 public IntPtr dlgOwner = ;
 public IntPtr instance = ;
 public String filter = null;
 public String customFilter = null;
 public int maxCustFilter = 0;
 public int filterIndex = 0;
 public String file = null;
 public int maxFile = 0;
 public String fileTitle = null;
 public int maxFileTitle = 0;
 public String initialDir = null;
 public String title = null;
 public int flags = 0;
 public short fileOffset = 0;
 public short fileExtension = 0;
 public String defExt = null;
 public IntPtr custData = ;
 public IntPtr hook = ;
 public String templateName = null;
 public IntPtr reservedPtr = ;
 public int reservedInt = 0;
 public int flagsEx = 0;
 }

 public class Shell32
 {
 public delegate int BFFCALLBACK(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData);

 [DllImport("")]
 public static extern int SHGetMalloc(out IMalloc ppMalloc);

 [DllImport("")]
 public static extern int SHGetSpecialFolderLocation(
   IntPtr hwndOwner, int nFolder, out IntPtr ppidl);

 [DllImport("")]
 public static extern int SHGetPathFromIDList(
   IntPtr pidl, byte[] pszPath);

 [DllImport("", CharSet = )]
 public static extern IntPtr SHBrowseForFolder(ref BROWSEINFO bi);
 }

 public class User32
 {
 public delegate bool delNativeEnumWindowsProc(IntPtr hWnd, IntPtr lParam);

 [DllImport("", CharSet = , SetLastError = true)]
 public static extern bool EnumWindows(delNativeEnumWindowsProc callback, IntPtr extraData);

 [DllImport("", CharSet = , SetLastError = true)]
 public static extern int GetWindowThreadProcessId(HandleRef handle, out int processId);
 }
}
//-------------------------------------------------------------------------
class Win32Instance
{
 //-------------------------------------------------------------------------
 private HandleRef unityWindowHandle;
 private bool bUnityHandleSet;
 //-------------------------------------------------------------------------
 public IntPtr GetHandle(ref bool bSuccess)
 {
 bUnityHandleSet = false;
 Win32API.(__EnumWindowsCallBack, );
 bSuccess = bUnityHandleSet;
 return ;
 }
 //-------------------------------------------------------------------------
 private bool __EnumWindowsCallBack(IntPtr hWnd, IntPtr lParam)
 {
 int procid;

 int returnVal =
  Win32API.(new HandleRef(this, hWnd), out procid);

 int currentPID = ().Id;

 HandleRef handle =
  new HandleRef(this, 
  ().MainWindowHandle);

 if (procid == currentPID)
 {
  unityWindowHandle = new HandleRef(this, hWnd);
  bUnityHandleSet = true;
  return false;
 }

 return true;
 }
}
//-------------------------------------------------------------------------

A brief introduction to the structure of all interfaces of Win32API are written in reference to the SHBrowseForFolder function. Win32Instance mainly obtains the ID of the current process accurately

Next is a simple example of getting the folder path

//-------------------------------------------------------------------------
private void __SelectFolder(out string directoryPath)
{
 directoryPath = "null";
 try
 {
 IntPtr pidlRet = ;
 int publicOptions = (int) |
 (int);
 int privateOptions = (int);

 // Construct a BROWSEINFO.
  bi = new ();
 IntPtr buffer = (1024);
 int mergedOptions = (int)publicOptions | (int)privateOptions;
  = ;
  = buffer;
  = "Folder";
  = mergedOptions;

 Win32Instance w = new Win32Instance();
 bool bSuccess = false;
 IntPtr P = (ref bSuccess);
 if (true == bSuccess)
 {
   = P;
 }

 pidlRet = Win32API.(ref bi);
 (buffer);

 if (pidlRet == )
 {
  // User clicked Cancel.
  return;
 }
 
 byte[] pp = new byte[2048];
 if (0 == Win32API.(pidlRet, pp))
 {
  return;
 }

 int nSize = 0;
 for (int i = 0; i < 2048; i++)
 {
  if (0 != pp[i])
  {
  nSize++;
  }
  else
  {
  break;
  }

 }

 if (0 == nSize)
 {
  return;
 }

 byte[] pReal = new byte[nSize];
 (pp, pReal, nSize);
 // Key transcoding part Gb2312Encoding gbk = new Gb2312Encoding();
 Encoding utf8 = Encoding.UTF8;
 byte[] utf8Bytes = (gbk, utf8, pReal);
 string utf8String = (utf8Bytes);
 utf8String = ("\0", "");
 directoryPath = ("\\", "/") + "/";

 }
 catch (Exception e)
 {
 ("Error getting folder directory:" + );
 }
}

A GBK transcoding library used above is viewed at the location -github portal

Transcoding

Here is a key piece of code:

Gb2312Encoding gbk = new Gb2312Encoding();
Encoding utf8 = Encoding.UTF8;
byte[] utf8Bytes = (gbk, utf8, pReal);
string utf8String = (utf8Bytes);
utf8String = ("\0", "");

A solution found on Google is to change the project code to unicode, but this setting seems to be not available in the C# project, so the data taken out by SHGetPathFromIDList can be directly transcoded to support Chinese. (The paths that are all in English will not have any impact)

Summarize

The above is the entire content of this article. I hope that the content of this article has certain reference value for everyone's study or work. If you have any questions, you can leave a message to communicate. Thank you for your support.