C#如何使用SHBrowseForFolder导出中文文件夹详解
前言
从业以来,数次踩中编码的坑,这次又马失前蹄,真是事不过三此非彼白.
本来这个小问题不打算拿出来说,但是翻看谷歌发现若干年前也有寥寥数人遇到碰到这个问题,而且都并没有给出一个可行的解决方案,现在问题依然挂在CSDN等地方,似乎不会再有人去回答了,或者其实题主们后面解决了但并没有回头来提供解决方案.现在由我来”终结此贴”
SHBrowseForFolder是一个可以用于获取文件夹路径的WindowsAPI。使用起来可以方便很多,文中将详细介绍关于C#使用SHBrowseForFolder导出中文文件夹的相关内容,下面话不多说了,来一起看看详细的介绍吧
0x00.使用SHBrowseForFolder选择文件夹
(大段代码来袭,不想看可直接拉到底看关键的几行)
底层接口–选择文件夹相关
//------------------------------------------------------------------------- classWin32API { //C#representationoftheIMallocinterface. [InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("00000002-0000-0000-C000-000000000046")] publicinterfaceIMalloc { [PreserveSig] IntPtrAlloc([In]intcb); [PreserveSig] IntPtrRealloc([In]IntPtrpv,[In]intcb); [PreserveSig] voidFree([In]IntPtrpv); [PreserveSig] intGetSize([In]IntPtrpv); [PreserveSig] intDidAlloc(IntPtrpv); [PreserveSig] voidHeapMinimize(); } [StructLayout(LayoutKind.Sequential,Pack=8)] publicstructBROWSEINFO { publicIntPtrhwndOwner; publicIntPtrpidlRoot; publicIntPtrpszDisplayName; [MarshalAs(UnmanagedType.LPTStr)] publicstringlpszTitle; publicintulFlags; [MarshalAs(UnmanagedType.FunctionPtr)] publicShell32.BFFCALLBACKlpfn; publicIntPtrlParam; publicintiImage; } [Flags] publicenumBffStyles { 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)] publicclassOpenFileName { publicintstructSize=0; publicIntPtrdlgOwner=IntPtr.Zero; publicIntPtrinstance=IntPtr.Zero; publicStringfilter=null; publicStringcustomFilter=null; publicintmaxCustFilter=0; publicintfilterIndex=0; publicStringfile=null; publicintmaxFile=0; publicStringfileTitle=null; publicintmaxFileTitle=0; publicStringinitialDir=null; publicStringtitle=null; publicintflags=0; publicshortfileOffset=0; publicshortfileExtension=0; publicStringdefExt=null; publicIntPtrcustData=IntPtr.Zero; publicIntPtrhook=IntPtr.Zero; publicStringtemplateName=null; publicIntPtrreservedPtr=IntPtr.Zero; publicintreservedInt=0; publicintflagsEx=0; } publicclassShell32 { publicdelegateintBFFCALLBACK(IntPtrhwnd,uintuMsg,IntPtrlParam,IntPtrlpData); [DllImport("Shell32.DLL")] publicstaticexternintSHGetMalloc(outIMallocppMalloc); [DllImport("Shell32.DLL")] publicstaticexternintSHGetSpecialFolderLocation( IntPtrhwndOwner,intnFolder,outIntPtrppidl); [DllImport("Shell32.DLL")] publicstaticexternintSHGetPathFromIDList( IntPtrpidl,byte[]pszPath); [DllImport("Shell32.DLL",CharSet=CharSet.Auto)] publicstaticexternIntPtrSHBrowseForFolder(refBROWSEINFObi); } publicclassUser32 { publicdelegatebooldelNativeEnumWindowsProc(IntPtrhWnd,IntPtrlParam); [DllImport("user32.dll",CharSet=CharSet.Auto,SetLastError=true)] publicstaticexternboolEnumWindows(delNativeEnumWindowsProccallback,IntPtrextraData); [DllImport("user32.dll",CharSet=CharSet.Auto,SetLastError=true)] publicstaticexternintGetWindowThreadProcessId(HandleRefhandle,outintprocessId); } } //------------------------------------------------------------------------- classWin32Instance { //------------------------------------------------------------------------- privateHandleRefunityWindowHandle; privateboolbUnityHandleSet; //------------------------------------------------------------------------- publicIntPtrGetHandle(refboolbSuccess) { bUnityHandleSet=false; Win32API.User32.EnumWindows(__EnumWindowsCallBack,IntPtr.Zero); bSuccess=bUnityHandleSet; returnunityWindowHandle.Handle; } //------------------------------------------------------------------------- privatebool__EnumWindowsCallBack(IntPtrhWnd,IntPtrlParam) { intprocid; intreturnVal= Win32API.User32.GetWindowThreadProcessId(newHandleRef(this,hWnd),outprocid); intcurrentPID=System.Diagnostics.Process.GetCurrentProcess().Id; HandleRefhandle= newHandleRef(this, System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle); if(procid==currentPID) { unityWindowHandle=newHandleRef(this,hWnd); bUnityHandleSet=true; returnfalse; } returntrue; } } //-------------------------------------------------------------------------
简单介绍一下Win32API所有接口的结构体都是参照SHBrowseForFolder函数而写,Win32Instance主要是精确的获取当前进程的ID
接下来是获取文件夹路径的简单例子
//------------------------------------------------------------------------- privatevoid__SelectFolder(outstringdirectoryPath) { directoryPath="null"; try { IntPtrpidlRet=IntPtr.Zero; intpublicOptions=(int)Win32API.BffStyles.RestrictToFilesystem| (int)Win32API.BffStyles.RestrictToDomain; intprivateOptions=(int)Win32API.BffStyles.NewDialogStyle; //ConstructaBROWSEINFO. Win32API.BROWSEINFObi=newWin32API.BROWSEINFO(); IntPtrbuffer=Marshal.AllocHGlobal(1024); intmergedOptions=(int)publicOptions|(int)privateOptions; bi.pidlRoot=IntPtr.Zero; bi.pszDisplayName=buffer; bi.lpszTitle="文件夹"; bi.ulFlags=mergedOptions; Win32Instancew=newWin32Instance(); boolbSuccess=false; IntPtrP=w.GetHandle(refbSuccess); if(true==bSuccess) { bi.hwndOwner=P; } pidlRet=Win32API.Shell32.SHBrowseForFolder(refbi); Marshal.FreeHGlobal(buffer); if(pidlRet==IntPtr.Zero) { //UserclickedCancel. return; } byte[]pp=newbyte[2048]; if(0==Win32API.Shell32.SHGetPathFromIDList(pidlRet,pp)) { return; } intnSize=0; for(inti=0;i<2048;i++) { if(0!=pp[i]) { nSize++; } else { break; } } if(0==nSize) { return; } byte[]pReal=newbyte[nSize]; Array.Copy(pp,pReal,nSize); //关键转码部分 Gb2312Encodinggbk=newGb2312Encoding(); Encodingutf8=Encoding.UTF8; byte[]utf8Bytes=Encoding.Convert(gbk,utf8,pReal); stringutf8String=utf8.GetString(utf8Bytes); utf8String=utf8String.Replace("\0",""); directoryPath=utf8String.Replace("\\","/")+"/"; } catch(Exceptione) { Console.WriteLine("获取文件夹目录出错:"+e.Message); } }
以上用到的一个GBK转码库位置查看-github传送门
0x01.GBK转码
以下是关键的一段代码:
Gb2312Encodinggbk=newGb2312Encoding(); Encodingutf8=Encoding.UTF8; byte[]utf8Bytes=Encoding.Convert(gbk,utf8,pReal); stringutf8String=utf8.GetString(utf8Bytes); utf8String=utf8String.Replace("\0","");
谷歌上找到的一个方案是把项目编码全部改为unicode,但是C#项目里貌似没这个设定,所以使用SHGetPathFromIDList拿出的数据直接转码即可支持中文.(全部为英文的路径也不会有影响)
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。