C#如何使用SHBrowseForFolder導(dǎo)出中文文件夾詳解
前言
從業(yè)以來(lái),數(shù)次踩中編碼的坑, 這次又馬失前蹄 , 真是事不過(guò)三此非彼白.
本來(lái)這個(gè)小問(wèn)題不打算拿出來(lái)說(shuō) , 但是翻看谷歌發(fā)現(xiàn)若干年前也有寥寥數(shù)人遇到碰到這個(gè)問(wèn)題 ,而且都并沒(méi)有給出一個(gè)可行的解決方案 ,現(xiàn)在問(wèn)題依然掛在CSDN等地方 , 似乎不會(huì)再有人去回答了, 或者其實(shí)題主們后面解決了但并沒(méi)有回頭來(lái)提供解決方案. 現(xiàn)在由我來(lái)”終結(jié)此貼”
SHBrowseForFolder是一個(gè)可以用于獲取文件夾路徑的Windows API。使用起來(lái)可以方便很多,文中將詳細(xì)介紹關(guān)于C#使用SHBrowseForFolder導(dǎo)出中文文件夾的相關(guān)內(nèi)容 ,下面話不多說(shuō)了,來(lái)一起看看詳細(xì)的介紹吧
0x00.使用SHBrowseForFolder選擇文件夾
(大段代碼來(lái)襲 , 不想看可直接拉到底看關(guān)鍵的幾行)
底層接口 – 選擇文件夾相關(guān)
//-------------------------------------------------------------------------
class Win32API
{
// C# representation of the IMalloc interface.
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown),
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(LayoutKind.Sequential, Pack = 8)]
public struct BROWSEINFO
{
public IntPtr hwndOwner;
public IntPtr pidlRoot;
public IntPtr pszDisplayName;
[MarshalAs(UnmanagedType.LPTStr)]
public string lpszTitle;
public int ulFlags;
[MarshalAs(UnmanagedType.FunctionPtr)]
public Shell32.BFFCALLBACK 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(LayoutKind.Sequential, CharSet = CharSet.Auto)]
public class OpenFileName
{
public int structSize = 0;
public IntPtr dlgOwner = IntPtr.Zero;
public IntPtr instance = IntPtr.Zero;
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 = IntPtr.Zero;
public IntPtr hook = IntPtr.Zero;
public String templateName = null;
public IntPtr reservedPtr = IntPtr.Zero;
public int reservedInt = 0;
public int flagsEx = 0;
}
public class Shell32
{
public delegate int BFFCALLBACK(IntPtr hwnd, uint uMsg, IntPtr lParam, IntPtr lpData);
[DllImport("Shell32.DLL")]
public static extern int SHGetMalloc(out IMalloc ppMalloc);
[DllImport("Shell32.DLL")]
public static extern int SHGetSpecialFolderLocation(
IntPtr hwndOwner, int nFolder, out IntPtr ppidl);
[DllImport("Shell32.DLL")]
public static extern int SHGetPathFromIDList(
IntPtr pidl, byte[] pszPath);
[DllImport("Shell32.DLL", CharSet = CharSet.Auto)]
public static extern IntPtr SHBrowseForFolder(ref BROWSEINFO bi);
}
public class User32
{
public delegate bool delNativeEnumWindowsProc(IntPtr hWnd, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern bool EnumWindows(delNativeEnumWindowsProc callback, IntPtr extraData);
[DllImport("user32.dll", CharSet = CharSet.Auto, 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.User32.EnumWindows(__EnumWindowsCallBack, IntPtr.Zero);
bSuccess = bUnityHandleSet;
return unityWindowHandle.Handle;
}
//-------------------------------------------------------------------------
private bool __EnumWindowsCallBack(IntPtr hWnd, IntPtr lParam)
{
int procid;
int returnVal =
Win32API.User32.GetWindowThreadProcessId(new HandleRef(this, hWnd), out procid);
int currentPID = System.Diagnostics.Process.GetCurrentProcess().Id;
HandleRef handle =
new HandleRef(this,
System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle);
if (procid == currentPID)
{
unityWindowHandle = new HandleRef(this, hWnd);
bUnityHandleSet = true;
return false;
}
return true;
}
}
//-------------------------------------------------------------------------
簡(jiǎn)單介紹一下 Win32API 所有接口的結(jié)構(gòu)體 都是參照SHBrowseForFolder函數(shù)而寫 , Win32Instance 主要是精確的獲取當(dāng)前進(jìn)程的ID
接下來(lái)是 獲取文件夾路徑的簡(jiǎn)單例子
//-------------------------------------------------------------------------
private void __SelectFolder(out string directoryPath)
{
directoryPath = "null";
try
{
IntPtr pidlRet = IntPtr.Zero;
int publicOptions = (int)Win32API.BffStyles.RestrictToFilesystem |
(int)Win32API.BffStyles.RestrictToDomain;
int privateOptions = (int)Win32API.BffStyles.NewDialogStyle;
// Construct a BROWSEINFO.
Win32API.BROWSEINFO bi = new Win32API.BROWSEINFO();
IntPtr buffer = Marshal.AllocHGlobal(1024);
int mergedOptions = (int)publicOptions | (int)privateOptions;
bi.pidlRoot = IntPtr.Zero;
bi.pszDisplayName = buffer;
bi.lpszTitle = "文件夾";
bi.ulFlags = mergedOptions;
Win32Instance w = new Win32Instance();
bool bSuccess = false;
IntPtr P = w.GetHandle(ref bSuccess);
if (true == bSuccess)
{
bi.hwndOwner = P;
}
pidlRet = Win32API.Shell32.SHBrowseForFolder(ref bi);
Marshal.FreeHGlobal(buffer);
if (pidlRet == IntPtr.Zero)
{
// User clicked Cancel.
return;
}
byte[] pp = new byte[2048];
if (0 == Win32API.Shell32.SHGetPathFromIDList(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];
Array.Copy(pp, pReal, nSize);
// 關(guān)鍵轉(zhuǎn)碼部分
Gb2312Encoding gbk = new Gb2312Encoding();
Encoding utf8 = Encoding.UTF8;
byte[] utf8Bytes = Encoding.Convert(gbk, utf8, pReal);
string utf8String = utf8.GetString(utf8Bytes);
utf8String = utf8String.Replace("\0", "");
directoryPath = utf8String.Replace("\\", "/") + "/";
}
catch (Exception e)
{
Console.WriteLine("獲取文件夾目錄出錯(cuò):" + e.Message);
}
}
以上用到的一個(gè)GBK轉(zhuǎn)碼庫(kù) 位置查看 - github傳送門
0x01.GBK轉(zhuǎn)碼
以下是關(guān)鍵的一段代碼:
Gb2312Encoding gbk = new Gb2312Encoding();
Encoding utf8 = Encoding.UTF8;
byte[] utf8Bytes = Encoding.Convert(gbk, utf8, pReal);
string utf8String = utf8.GetString(utf8Bytes);
utf8String = utf8String.Replace("\0", "");
谷歌上找到的一個(gè)方案是把項(xiàng)目編碼全部改為unicode , 但是C#項(xiàng)目里貌似沒(méi)這個(gè)設(shè)定 , 所以使用SHGetPathFromIDList拿出的數(shù)據(jù)直接轉(zhuǎn)碼即可支持中文.(全部為英文的路徑也不會(huì)有影響)
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
- C#導(dǎo)入導(dǎo)出EXCEL文件的代碼實(shí)例
- C#數(shù)據(jù)導(dǎo)入/導(dǎo)出Excel文件及winForm導(dǎo)出Execl總結(jié)
- C#導(dǎo)出數(shù)據(jù)到Excel文件的方法
- C#導(dǎo)出數(shù)據(jù)到CSV文件的通用類實(shí)例
- C#實(shí)現(xiàn)pdf導(dǎo)出 .Net導(dǎo)出pdf文件
- C#導(dǎo)出生成excel文件的方法小結(jié)(xml,html方式)
- C#導(dǎo)出GridView數(shù)據(jù)到Excel文件類實(shí)例
- C#實(shí)現(xiàn)導(dǎo)出List數(shù)據(jù)到xml文件的方法【附demo源碼下載】
相關(guān)文章
探秘Unity游戲開(kāi)發(fā)中的狀態(tài)設(shè)計(jì)模式
這篇文章主要介紹了探秘Unity游戲開(kāi)發(fā)中的狀態(tài)設(shè)計(jì)模式,狀態(tài)模式是Unity游戲開(kāi)發(fā)中常用的一種設(shè)計(jì)模式,可以幫助開(kāi)發(fā)者更好地管理游戲?qū)ο鬆顟B(tài),提高游戲的可維護(hù)性和可擴(kuò)展性2023-05-05
.NET 某云采購(gòu)平臺(tái)API 掛死問(wèn)題解析
這篇文章主要介紹了.NET 某云采購(gòu)平臺(tái)API 掛死問(wèn)題解析,本文給大家介紹的非常詳細(xì),感興趣的朋友跟隨小編一起看看吧2024-11-11
C# 啟動(dòng) SQL Server 服務(wù)的實(shí)例
下面小編就為大家分享一篇C# 啟動(dòng) SQL Server 服務(wù)的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2017-12-12
C#/VB.NET實(shí)現(xiàn)從PPT中提取圖片的示例代碼
PPT是用于制作幻燈片(演示文稿)的應(yīng)用軟件,每張幻燈片中都可以包含文字、圖形、圖形、表格、聲音和影像等多種信息。本文主要介紹了如何實(shí)現(xiàn)從PPT中提取圖片的功能,需要的可以參考一下2023-03-03
C#將部分Controls數(shù)據(jù)導(dǎo)入對(duì)象并存入ini中的操作方法
在Winform設(shè)計(jì)中,經(jīng)常需要將控件數(shù)據(jù)導(dǎo)出到屬性或字段中,本文詳細(xì)介紹了如何優(yōu)化這一過(guò)程,包括控件和屬性的遍歷,以及使用FieldInfo的getSet函數(shù)和Ini類庫(kù)來(lái)實(shí)現(xiàn)數(shù)據(jù)的有效存儲(chǔ)和轉(zhuǎn)換,感興趣的朋友跟隨小編一起看看吧2024-10-10

