一聚教程网:一个值得你收藏的教程网站

最新下载

C#如何使用SHBrowseForFolder导出中文文件夹详解

时间:2018-11-13 16:10:26 编辑:猪哥 来源:转载

前言

从业以来,数次踩中编码的坑, 这次又马失前蹄 , 真是事不过三此非彼白.

本来这个小问题不打算拿出来说 , 但是翻看谷歌发现若干年前也有寥寥数人遇到碰到这个问题 ,而且都并没有给出一个可行的解决方案 ,现在问题依然挂在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拿出的数据直接转码即可支持中文.(全部为英文的路径也不会有影响)

文章评论

热门栏目

马会一码爆料 许昌市| 商河县| 墨竹工卡县| 香河县| 大化| 盐源县| 闽清县| 正镶白旗| 太康县| 胶南市| 阿拉善左旗| 贡山| 大悟县| 潢川县| 安塞县| 正阳县| 个旧市| 吴忠市| 大厂| 东丽区| 若羌县| 宜章县| 武山县| 广昌县| 汪清县| 尼木县| http://yiwmcd.cn 乌拉特后旗| 沧州市| 化德县| 磴口县| 西乡县| 陕西省| 余庆县| 金乡县| 镶黄旗| 疏勒县| 莱阳市| 泰宁县| 天全县| 浮梁县| 侯马市| 许昌市| 通许县| 棋牌| 茌平县| 大新县| 安庆市| 梁平县| 和林格尔县| 大埔县| 侯马市| 六安市| 北安市| 德阳市| 淄博市| http://iiiwny.cn 桃园县| 南康市| 惠东县| 福安市| 思南县| 西丰县| 潮州市| 灵寿县| 黄浦区| 宾川县| 临海市| 翁源县| 杂多县| 平谷区| 荆州市| 汤原县| 楚雄市| 青冈县| 天柱县| 来安县| 龙岩市| 静乐县| 德州市| 社旗县| 怀化市| 宁津县| 黔西| 松原市| 伊宁县| 新干县| 沁源县| 永春县| 惠州市| 佛坪县| http://www.pyicja.cn 澳门| 临夏市| 富平县| 文山县| 潼南县| 天镇县| 鹤山市| 文化| 沙雅县| 阿克| 宝坻区| 新源县| 隆回县| 花莲市| 石首市| 五寨县| 旅游| 栾川县| 五常市| 平乐县| 嘉祥县| 正镶白旗| 桐城市| 隆德县| 西乌珠穆沁旗| 咸阳市| 清水县| 开阳县| 蕲春县| http://www.aloqzv.cn 平潭县| 成安县| 通城县| 陕西省| 平南县| 申扎县| 奎屯市| 通海县| 涞水县| 尚志市| 麟游县| 尤溪县| 弥勒县| 高邑县| 景东| 奉化市| 油尖旺区| 洪湖市| 金昌市| 濉溪县| 汝南县| 长汀县| 烟台市| 怀安县| 佳木斯市| 新建县| 富裕县| 蒲城县| 永新县| http://www.dkqkjy.cn 通州市| 志丹县| 望都县| 庆云县| 龙州县| 宁南县| 攀枝花市| 和田县| 广汉市| 静乐县| 晋江市| 弥勒县| 延安市| 包头市| 庆安县| 铁力市| 开平市| 巴彦县| 奈曼旗| 尖扎县| 巴彦县| 吴旗县| 连云港市| 德阳市| 溧阳市| 屏山县| 玛纳斯县| 长泰县| http://www.oeidgs.cn 若尔盖县| 钦州市| 桂阳县| 伊川县| 买车| 鄱阳县| 九江县| 民丰县| 封丘县| 湘乡市| 乐至县| 贵南县| 乌海市| 凤山市| 南昌县| 庄浪县| 德清县|