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拿出的数据直接转码即可支持中文.(全部为英文的路径也不会有影响)

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • C#导出GridView数据到Excel文件类实例

    本文实例讲述了C#导出GridView数据到Excel文件类.分享给大家供大家参考.具体如下: 这段C#代码自定义了一个封装类,用于将GridView数据导出到Excel文件 using System; using System.Web; using System.Web.UI; using System.IO; using System.Web.UI.WebControls; namespace DotNet.Utilities { public class ExportExcel { pro

  • C#数据导入/导出Excel文件及winForm导出Execl总结

    一.asp.net中导出Execl的方法: 在asp.net中导出Execl有两种方法,一种是将导出的文件存放在服务器某个文件夹下面,然后将文件地址输出在浏览器上:一种是将文件直接将文件输出流写给浏览器.在Response输出时,\t分隔的数据,导出execl时,等价于分列,\n等价于换行. 1.将整个html全部输出execl 此法将html中所有的内容,如按钮,表格,图片等全部输出到Execl中. 复制代码 代码如下: Response.Clear(); Response.Buffer= t

  • C#实现pdf导出 .Net导出pdf文件

    最近碰见个需求需要实现导出pdf文件,上网查了下代码资料总结了以下代码.可以成功的实现导出pdf文件. 在编码前需要在网上下载个itextsharp.dll,此程序集是必备的.楼主下载的是5.0版本,之前下了个5.4的似乎不好用. 下载之后直接添加引用. <%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Webpdf.aspx.cs" Inherits="Web导出

  • C#实现导出List数据到xml文件的方法【附demo源码下载】

    本文实例讲述了C#实现导出List数据到xml文件的方法.分享给大家供大家参考,具体如下: C#导出List数据到xml文件,这里主要用到的是: XmlSerializer 类 (System.Xml.Serialization) 将对象序列化到 XML 文档中和从 XML 文档中反序列化对象.XmlSerializer 使您得以控制如何将对象编码到 XML 中. 实体类代码: /// <summary> /// 用户实体类 /// /// 注意:类的访问修饰符必须是:public,否则会出现

  • C#导出数据到Excel文件的方法

    本文实例讲述了C#导出数据到Excel文件的方法.分享给大家供大家参考.具体实现方法如下: /// <summary> /// 导出到Excel类,项目需引用Microsodt.Office.Interop.Excel, /// 类文件需using System.Data与System.Windows.Forms命名空间 /// </summary> public class CToExcel { /// <summary> /// 导出到Excel /// </

  • C#导出生成excel文件的方法小结(xml,html方式)

    直接贴上代码,里面都有注释 复制代码 代码如下: /// <summary> /// xml格式生成excel文件并存盘;        /// </summary>        /// <param name="page">生成报表的页面,没有传null</param>        /// <param name="dt">数据表</param>        /// <param

  • C#导入导出EXCEL文件的代码实例

    复制代码 代码如下: using System;using System.Data;using System.Data.OleDb; namespace ZFSoft.Joint{    public class ExcelIO    {        private int _ReturnStatus;        private string _ReturnMessage; /// <summary>        /// 执行返回状态        /// </summary&g

  • C#导出数据到CSV文件的通用类实例

    本文实例讲述了C#导出数据到csv文件的通用类.分享给大家供大家参考.具体如下: 通过这个类可以很简单的定义数据格式,并导出到csv文件 //这里写了一个通用的类 using System; using System.Data; using System.Configuration; using System.Collections.Generic; using System.Web; using System.Web.Security; using System.Web.UI; using S

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

    前言 从业以来,数次踩中编码的坑, 这次又马失前蹄 , 真是事不过三此非彼白. 本来这个小问题不打算拿出来说 , 但是翻看谷歌发现若干年前也有寥寥数人遇到碰到这个问题 ,而且都并没有给出一个可行的解决方案 ,现在问题依然挂在CSDN等地方 , 似乎不会再有人去回答了, 或者其实题主们后面解决了但并没有回头来提供解决方案. 现在由我来"终结此贴" SHBrowseForFolder是一个可以用于获取文件夹路径的Windows API.使用起来可以方便很多,文中将详细介绍关于C#使用SHB

  • jsp按格式导出doc文件实例详解

    jsp按格式导出doc文件实例详解 原理:doc文件其实可以保存为xml文件,该xml文件用字符串表示了doc文件的表现形式,我们只需要用Java将那些要填的内容替换掉然后下载给客户就行了. 1.首先是按照你的文档填写好数据. 2.将文档另存为xml文件,然后编辑该xml文件,将填好的内容用某种格式替换,如:将名字张三替换成${name} 3.读取文件,将文件中的${name}替换成真正的名字. 4.下载. 接下来看代码: 首先是那个转换类 package com.my.util; import

  • SpringBoot使用freemarker导出word文件方法详解

    目录 1.前言 2.需求说明 3.编码 3.1.导入依赖 3.2.接口编写 3.3.工具类 3.4.ftl文件 3.5.测试 4.word转pdf 5.总结 1.前言 在项目中我们有时间需要根据一个word模板文档,批量生成其他的word文档,里面的有些值改变一下而已,那怎么做呢? 2.需求说明 假如说,现在我有个模板文档,内容如下: 现在上面文档里面有如下变量: username:员工姓名 idno:身份证号码 hireDate:入职日期 work:职位 endDate:离职日期 现在我需要针

  • 如何使用Node.js遍历文件夹详解

    目录 前言 获取的文件列表为数组格式 获取的文件列表为对象格式 异步方式 比较同步和异步两种方案 附node遍历文件夹并读取文件内容 参考资料 总结 前言 最近在写一个管理 markdown 文件的工具knowledge-center,需要读取指定文件夹内所有 markdown 文件.因此需要用 Node.js 来实现遍历一个文件夹内所有文件的功能. Node.js 中提供了这些有用的 API: fs.readdir:异步读取文件夹 fs.readdirSync:同步读取文件夹 fs.statS

  • ASP.NET 保留文件夹详解

    1. Bin文件夹 Bin文件夹包含应用程序所需的,用于控件.组件或者需要引用的任何其他代码的可部署程序集.该目录中存在的任何.dll文 件将自动地链接到应用程序.如果在该文件夹中留有不用的或过期的文件,则可能出现"二义性引用(ambiguous reference)"异常的风险.换句话说,如果两个不同的程序集定义相同的类(相同的命名空间和名称),则ASP.NET运行库不能决定应该使用哪一 个程序集,从而抛出一个异常.在开发时,当我们重新命名一个项目或一个程序集的名称时,这是常见的错误

  • Node.js折腾记一:读指定文件夹,输出该文件夹的文件树详解

    前言 用来干什么:想干嘛干嘛 为什么写:写来玩,学习node.js文件系统相关api:树结构这种东西还是挺不错的,会用会造才是真的会 用了什么: fs.readdir(dir), fs.stat(dir).isFile(), path处理路径等 思路: 读取当前文件夹(不是文件夹的另作处理),获得其下所有文件和目录组成的数组: 循环该数组,判断是文件夹还是文件,文件的话直接push到childFiles(对象有两个属性:short文件名,full完整文件路径) 文件夹的话,先把当前文件夹作为ke

  • Python导出并分析聊天记录详解流程

    导出聊天记录生成词云看看你和对象聊了什么(可惜我没女朋友) 1.导出聊天记录打开消息管理器 导出的格式选择txt格式(我这里选择导出的路径是桌面所以在桌面上生成了一个包含聊天记录的.txt文件) 2.编写代码图中框出来的文本是我们不需要的(比如说图片会在这里面显示为[图片]表情显示为[表情]) 所以我们把它替换掉,我这里用到了正则: string = open(r'C:\\Users\\l1768\\Desktop\\消息记录.txt','r',encoding='utf-8').read()

  • Node.js基础入门之缓存区与文件操作详解

    目录 缓存区 1. 什么是缓存区? 2. 创建指定长度的缓存区 3. 通过数组创建缓存区 4. 通过字符串创建缓存区 5. 读写缓存区 6. 复制缓存区 文件操作 1. 异步直接读取 2. 同步直接读取 3. 流式读取 4. 写入文件 5. 流式写入文件 6. 读取文件信息 7. 删除文件 8. 管道 9. 链式流 经过前面三天的学习,Node.js的基础知识已逐渐掌握,今天继续学习缓存区和文件操作,并稍加整理加以分享,如有不足之处,还请指正. 缓存区 1. 什么是缓存区? JavaScript

  • Java使用POI实现导出Excel的方法详解

    目录 一.前景 二.概念 2.1. 简介 2.2.Excel版本和相关对象 2.3.WorkBook 2.4.POI依赖 三.POI - 写 3.1.代码示例 3.2. 性能对比 3.3. 测试rowAccessWindowSize 3.4. 导出Excel样式设置 四.POI - 读 4.1.代码示例 4.2.读取不同的数据类型 4.3.读取公式 五.POI - 遇到的坑 一.前景 在项目开发中往往需要使用到Excel的导入和导出,导入就是从Excel中导入到DB中,而导出就是从DB中查询数据

  • Android 读取资源文件实例详解

    Android 读取资源文件实例详解 本文主要介绍 Android 读取资源文件,直接从 assets 读取,从 Raw 文件中读取,InputStream 转 String. 以下为直接从assets读取: /** * 得到Assets里面相应的文件流 * * @param fileName * @return */ private InputStream getAssetsStream(String fileName) { InputStream is = null; try { is =

随机推荐