当前位置: 移动技术网 > IT编程>开发语言>c# > C#如何使用SHBrowseForFolder导出中文文件夹详解

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

2019年07月18日  | 移动技术网IT编程  | 我要评论

前言

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

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

总结

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

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网