C#中使用SHBrowseForFolder导出中文文件夹

栏目: ASP.NET · 发布时间: 6年前

内容简介:Unity项目开发笔记(十七)从业以来,数次踩中编码的坑, 这次又马失前蹄 , 真是事不过三此非彼白.本来这个小问题不打算拿出来说 , 但是翻看谷歌发现若干年前也有寥寥数人遇到碰到这个问题 ,而且都并没有给出一个可行的解决方案 ,现在问题依然挂在CSDN等地方 , 似乎不会再有人去回答了, 或者其实题主们后面解决了但并没有回头来提供解决方案. 现在由我来”终结此贴”

Unity项目开发笔记(十七)

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

本来这个小问题不打算拿出来说 , 但是翻看谷歌发现若干年前也有寥寥数人遇到碰到这个问题 ,而且都并没有给出一个可行的解决方案 ,现在问题依然挂在CSDN等地方 , 似乎不会再有人去回答了, 或者其实题主们后面解决了但并没有回头来提供解决方案. 现在由我来”终结此贴”

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

-EOF-


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Building Websites with Joomla!

Building Websites with Joomla!

H Graf / Packt Publishing / 2006-01-20 / USD 44.99

This book is a fast paced tutorial to creating a website using Joomla!. If you've never used Joomla!, or even any web content management system before, then this book will walk you through each step i......一起来看看 《Building Websites with Joomla!》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码