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拿出的数据直接转码即可支持中文.(全部为英文的路径也不会有影响)
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对毛票票的支持。