C#如何使用SHBrowseForFolder导出中文文件夹详解
前言
从业以来,数次踩中编码的坑, 这次又马失前蹄 , 真是事不过三此非彼白.
本来这个小问题不打算拿出来说 , 但是翻看谷歌发现若干年前也有寥寥数人遇到碰到这个问题 ,而且都并没有给出一个可行的解决方案 ,现在问题依然挂在csdn等地方 , 似乎不会再有人去回答了, 或者其实题主们后面解决了但并没有回头来提供解决方案. 现在由我来”终结此贴”
shbrowseforfolder是一个可以用于获取文件夹路径的windows api。使用起来可以方便很多,文中将详细介绍关于c#使用shbrowseforfolder导出中文文件夹的相关内容 ,下面话不多说了,来一起看看详细的介绍吧
0x00.使用shbrowseforfolder选择文件夹
(大段代码来袭 , 不想看可直接拉到底看关键的几行)
底层接口 – 选择文件夹相关
//------------------------------------------------------------------------- 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; } } //-------------------------------------------------------------------------
简单介绍一下 win32api 所有接口的结构体 都是参照shbrowseforfolder函数而写 , win32instance 主要是精确的获取当前进程的id
接下来是 获取文件夹路径的简单例子
//------------------------------------------------------------------------- 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); // 关键转码部分 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("获取文件夹目录出错:" + e.message); } }
以上用到的一个gbk转码库 位置查看 - github传送门
0x01.gbk转码
以下是关键的一段代码:
gb2312encoding gbk = new gb2312encoding(); encoding utf8 = encoding.utf8; byte[] utf8bytes = encoding.convert(gbk, utf8, preal); string utf8string = utf8.getstring(utf8bytes); utf8string = utf8string.replace("\0", "");
谷歌上找到的一个方案是把项目编码全部改为unicode , 但是c#项目里貌似没这个设定 , 所以使用shgetpathfromidlist拿出的数据直接转码即可支持中文.(全部为英文的路径也不会有影响)
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。
上一篇: Go语言系列:(1)在VsCode中配置Go的开发环境
下一篇: .NET实现简易的文件增量备份程序