首页 > 技术文章 > cad.net 实现不重复打开文件夹

JJBox 2019-02-15 14:17 原文

原因

实现不重复地打开dwg文件的所在目录的资源管理器,也就是打开文件夹,并设置焦点.

c++

看这里: https://www.cnblogs.com/shankun/p/4088860.html

c#

这里地址的方法也是可用的,但是net3.5不能使用

方法有两个,两者有点差别,可以配合用.

例子一:

​ 枚举桌面资源管理器,关闭相同的.var wins = new ShellWindows();
​ 然后用Process.Start 开启并设置焦点.

例子二:

​ 用 Shell32.ShowFileInExplorer(filename3);
​ 它可以直接设置一个文件夹和焦点,
​ 如果是我自己开的同路径文件夹,它的表现是自己开一个新的.
​ 但是我用它开的,然后换了焦点,它则不开新的,而是重设焦点了.
​ 就好像有个缓存,它不是在全屏幕检索的,是在它第一次开了的文件夹上面检索的.

命令

#if !HC2020
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Acap = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using GrxCAD.DatabaseServices;
using GrxCAD.EditorInput;
using GrxCAD.ApplicationServices;
using GrxCAD.Runtime;
using Acap = GrxCAD.ApplicationServices.Application;
#endif
using System.Diagnostics;
using System.Runtime.InteropServices;
using System;
using System.IO;
using Win32API;

namespace JoinBox
{
    public static class Command_jjOpenFilePath
    {
        /// <summary>
        /// 不重复打开dwg路径的资源管理器
        /// </summary>
        [CommandMethod("JJ_OpenDwgFilePath")]
        public static void JJ_OpenDwgFilePath()
        {
            Database db = HostApplicationServices.WorkingDatabase;//当前的数据库
            Editor ed = Acap.DocumentManager.MdiActiveDocument.Editor;
            ed.WriteMessage(Environment.NewLine + "****惊惊连盒-打开当前Dwg的目录");
            try
            {
                var filename3 = db.GetDwgFullPath();
                if (filename3 == null)
                {
                    return;
                }
                if (filename3 == "")
                {
                    ed.WriteMessage(Environment.NewLine + "你没有保存文件!\n");
                    return;
                }
#if true
                //例子一:
                //关闭已经打开的同路径文件夹们
                var wins = new ShellWindows();//这个名字是为了兼容高版本(没想到吧
                if (wins.Enumerator.Count > 0)
                {
                    string dwgname = CadSystem.Getvar("dwgname");
                    foreach (var item in wins.Enumerator)
                    {
                        if (!string.IsNullOrEmpty(item.LocationURL) &&
                            item.LocationURL + "\\" + dwgname == filename3)
                        {
                            item.Quit();//关闭
                        }
                    }
                }
                //重开一个,防止选择状态被改变,
                Process.Start("explorer", "/select,\"" + filename3 + "\"");//加引号防止空格中断
#else
                //例子二
                JoinBoxCurrency.Shell32.ShowFileInExplorer(filename3);
#endif
            }
            catch (System.Exception e)
            {
                ed.WriteMessage(Environment.NewLine + e.Message);
            }
        }     
    }
}

遍历桌面资源管理器

using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using static Win32API.WinApi;

namespace Win32API
{
    public class InternetExplorer
    {
        /// <summary>
        /// 句柄
        /// </summary>
        public IntPtr HWND { get; set; }

        /// <summary>
        /// 文件夹路径
        /// </summary>
        public string LocationURL { get; set; }

        /// <summary>
        /// 关闭窗口,关闭文件夹
        /// </summary>
        public void Quit()
        {
            SendMessage(HWND, (int)WM.WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
        }
    }

    /// <summary>
    /// 遍历桌面资源管理器
    /// </summary>
    public class ShellWindows
    {
        public List<InternetExplorer> Enumerator = new List<InternetExplorer>();

        /// <summary>
        /// 获取桌面所有文件夹的路径
        /// </summary>
        /// <returns></returns>
        public ShellWindows()
        {
            var wndList = new List<WindowInfo>();
            GetAllDesktopWindows(wndList);

            var classArrar = new string[] {
             "WorkerW",
             "ReBarWindow32",
             "Address Band Root",
             "msctls_progress32",
             "Breadcrumb Parent",
             "ToolbarWindow32"
            };

            var displays = new Shell32.GetDisplays();
            foreach (var item in wndList)
            {
                if (item.ClassName != "CabinetWClass")//资源管理器
                {
                    continue;
                }
                var fi = Finds(item.Hwnd, classArrar);
                if (fi == IntPtr.Zero)
                {
                    continue;
                }

                //知识:toolbar上的按钮没有handler,要用发送通知信息
                var sb = new StringBuilder(256);
                //获取窗口名称-路径地址
                GetWindowText(fi, sb, sb.Capacity);
                string path = sb.ToString();
                path = path.Substring(4, path.Length - 4);//4表示"地址: "长度...怎么对应每个语言呢?
                //窗口名称如果是特殊文件夹则不是路径,如"桌面"/"文档"/"音乐"
                if (string.IsNullOrEmpty(Path.GetDirectoryName(path)))
                {
                    path = displays.GetPath(path);
                }
                Enumerator.Add(new InternetExplorer
                {
                    HWND = item.Hwnd,
                    LocationURL = path
                });
                Enumerator.Add(ie);
            }
        }

        /// <summary>
        /// 递归查找子窗体句柄
        /// </summary>
        IntPtr Finds(IntPtr hwnd, string[] classArrar, int arrNum = 0)
        {
            IntPtr fi = IntPtr.Zero;
            if (classArrar.Length > arrNum)
            {
                fi = FindWindowEx(hwnd, 0, classArrar[arrNum], null);
                if (fi != IntPtr.Zero && classArrar.Length > arrNum + 1)//最后一个就不找了
                {
                    fi = Finds(fi, classArrar, ++arrNum);
                }
            }
            return fi;
        }
    }
}

Win32API使用

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Text;

namespace Win32API
{ 
    public class WinApi
    {
        public delegate bool WNDENUMPROC(IntPtr hwnd, int lParam);

        /// <summary>
        /// 置前窗口
        /// </summary>
        /// <param name="hwnd"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool SetForegroundWindow(IntPtr hwnd);

        /// <summary>
        /// 枚举窗口 
        /// </summary>
        /// <param name="lpEnumFunc"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool EnumWindows(WNDENUMPROC lpEnumFunc, int lParam);

        /// <summary>
        /// 获取窗口Text 
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="lpString"></param>
        /// <param name="nMaxCount"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int GetWindowText(IntPtr hwnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);

        /// <summary>
        /// 获取窗口类名
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="lpString"></param>
        /// <param name="nMaxCount"></param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int GetClassName(IntPtr hwnd, [MarshalAs(UnmanagedType.LPWStr)]StringBuilder lpString, int nMaxCount);

        /// <summary>
        /// 窗口隐藏
        /// </summary>
        /// <param name="hwnd">窗口句柄</param>
        /// <returns></returns>
        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern bool IsWindowVisible(IntPtr hwnd);

        /// <summary>
        /// 查找子窗口
        /// </summary>
        /// <param name="hwnd"></param>
        /// <param name="hwndChildAfter"></param>
        /// <param name="lpszClass"></param>
        /// <param name="lpszWindow"></param>
        /// <returns></returns>
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern IntPtr FindWindowEx(IntPtr hwnd, uint hwndChildAfter, string lpszClass, string lpszWindow);

        ///https://jingyan.baidu.com/article/c45ad29cd5fb58051653e278.html
        /// <summary>
        /// 发送消息
        /// </summary> 
        /// <returns></returns>  
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

        /// <summary>
        /// 发送消息
        /// </summary> 
        /// <returns></returns>  
        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        public static extern int SendMessage(IntPtr hwnd, int wMsg, IntPtr wParam, ref Rectangle lParam);

        /// <summary>
        /// 关闭文件夹
        /// </summary>
        /// <param name="hwnd"></param>
        public static void QuitToolbar(IntPtr hwnd)
        {
            // https://docs.microsoft.com/zh-cn/windows/desktop/winmsg/wm-close
            const int WM_CLOSE = 0x0010;
            SendMessage(hwnd, WM_CLOSE, IntPtr.Zero, IntPtr.Zero);
        }

        //窗口样式 
        public struct WindowInfo
        {
            public IntPtr hwnd;
            public string windowName;
            public string className;
        }

        /// <summary>
        /// 枚举所有桌面窗口
        /// </summary>
        /// <returns></returns>
        public static WindowInfo[] GetAllDesktopWindows()
        {
            //用来保存窗口对象 列表
            var wndList = new List<WindowInfo>();

            //枚举所有桌面窗口
            EnumWindows(delegate (IntPtr hWnd, int lParam)
            {
                WindowInfo wnd = new WindowInfo();
                StringBuilder sb = new StringBuilder(256);

                //获得HWND
                wnd.hwnd = hWnd;

                //获取窗口名称
                GetWindowText(hWnd, sb, sb.Capacity);
                wnd.windowName = sb.ToString();

                //获取窗口类
                GetClassName(hWnd, sb, sb.Capacity);
                wnd.className = sb.ToString();

                //添加到列表中
                wndList.Add(wnd);
                return true;
            }, 0);

            return wndList.ToArray();
        }
    }
}

Sell32API使用

using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;

namespace JoinBoxCurrency
{
    public partial class Shell32
    {
        /// <summary>
        /// 打开文件夹并选择项目
        /// </summary>
        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        public static extern int SHOpenFolderAndSelectItems(IntPtr pidlFolder, uint cidl, [MarshalAs(UnmanagedType.LPArray)] IntPtr[] apidl, uint dwFlags);

        /// <summary>
        /// 解析显示名称
        /// </summary>
        [DllImport("shell32.dll", CharSet = CharSet.Auto)]
        public static extern void SHParseDisplayName([MarshalAs(UnmanagedType.LPWStr)] string name, IntPtr bindingContext, out IntPtr pidl, uint sfgaoIn, out uint psfgaoOut);

        // https://stackoverflow.com/questions/8182494/how-can-i-set-an-existing-explorer-exe-instance-to-select-a-file
        /// <summary>
        /// 置前文件夹并设置文件焦点
        /// </summary>
        /// <param name="file">文件路径</param>
        public static void ShowFileInExplorer(string file)
        {
            // 如果是我自己开的同路径文件夹,它的表现是自己开一个新的.
            // 但是我用它开的,然后换了焦点,它则不开新的,而是重设焦点了.
            // 就好像有个缓存,它不是在全屏幕检索的,是在它第一次开了的文件夹上面检索的.

            // 获取当前打开的文件夹(资源管理器)指针
            var directory = Path.GetDirectoryName(file);
            SHParseDisplayName(directory, IntPtr.Zero, out IntPtr folderPtr, 0, out _);
            if (folderPtr == IntPtr.Zero)
            {
                return;
            }
            if (File.Exists(file))
            {
                file = Path.GetFileName(file);
                SHParseDisplayName(file, IntPtr.Zero, out IntPtr filePtr, 0, out _);
                if (filePtr != IntPtr.Zero)
                {
                    IntPtr[] files = { filePtr };
                    SHOpenFolderAndSelectItems(folderPtr, (uint)files.Length, files, 0);
                    Marshal.FreeCoTaskMem(filePtr);
                }
            }
            Marshal.FreeCoTaskMem(folderPtr);
        }
    }


    public partial class Shell32
    {
        /// <summary>
        /// 获取文件信息
        /// </summary>
        [DllImport("shell32.dll")]
        public static extern int SHGetFileInfoW(IntPtr pidl, uint dwFileAttributes, out SHFILEINFO psfi, uint cbFileInfo, uint flags);

        /// <summary>
        /// 获取文件夹路径
        /// </summary>
        [DllImport("shell32.dll")]
        public static extern HResult SHGetFolderLocation(IntPtr hwnd, int nFolder, IntPtr token, int dwReserved, out IntPtr pidl);

        /// <summary>
        /// 释放指针
        /// </summary>
        /// <param name="pidl"></param>
        [DllImport("shell32.dll")]
        public static extern void ILFree(IntPtr pidl);

        /// <summary>
        /// 获取特殊文件夹枚举值(如"桌面","文档")的中文名称
        /// </summary>
        public static string GetDisplayInvoke(Environment.SpecialFolder specialFolder)
        {
            IntPtr pidl = IntPtr.Zero;
            try
            {
                HResult hr = SHGetFolderLocation(IntPtr.Zero, (int)specialFolder, IntPtr.Zero, 0, out pidl);
                if (hr.IsFailure)
                    return null;

                SHFILEINFO shfi;
                if (0 != SHGetFileInfoW(pidl,
                                        FILE_ATTRIBUTE_NORMAL,
                                        out shfi,
                                        (uint)Marshal.SizeOf(typeof(SHFILEINFO)),
                                        SHGFI_PIDL | SHGFI_DISPLAYNAME))
                {
                    return shfi.szDisplayName;
                }
                return null;
            }
            finally
            {
                if (pidl != IntPtr.Zero)
                    ILFree(pidl);
            }
        }

        /// <summary>
        /// 获取所有的特殊文件夹
        /// </summary>
        /// <returns></returns>
        public static Dictionary<Environment.SpecialFolder, string> GetDisplayDictionary()
        {
            var dic = new Dictionary<Environment.SpecialFolder, string>();
            foreach (var value in Enum.GetValues(typeof(Environment.SpecialFolder)))
            {
                var spe = (Environment.SpecialFolder)value;  //然后强转成枚举类型,可作为输出
                var name = GetDisplayInvoke(spe);
                if (!dic.ContainsKey(spe))
                {
                    dic.Add(spe, name);
                }
            }
            return dic;
        }

        /// <summary>
        /// 通过特殊文件夹的名称获取对应的枚举
        /// </summary>
        /// <returns></returns>
        public static Environment.SpecialFolder? GetDisplayInvoke(string specialFolder)
        {
            var dic = GetDisplayDictionary();
            foreach (var pa in dic)
            {
                if (pa.Value == specialFolder)
                {
                    return pa.Key;
                }
            }
            return null;
        }

        public class GetDisplays
        {
            Dictionary<Environment.SpecialFolder, string> dic;
            public GetDisplays()
            {
                dic = GetDisplayDictionary();
            }
            //返回名称
            public string GetName(Environment.SpecialFolder specialFolder)
            {
                // 透过缓存速度更快
                return dic[specialFolder];
            }
            //返回枚举
            public Environment.SpecialFolder? GetName(string specialFolder)
            {
                foreach (var pa in dic)
                {
                    if (pa.Value == specialFolder)
                        return pa.Key;
                }
                return null;
            }
            //窗口名称如果是特殊文件夹则不是路径,如"桌面"/"文档"/"音乐"
            //返回路径
            public string GetPath(string specialFolder)
            {
                string path = null;
                var sf = GetName(specialFolder);
                if (sf != null)
                {
                    path = Environment.GetFolderPath(sf.Value);
                }
                return path;
            }
        }

        private const uint FILE_ATTRIBUTE_NORMAL = 0x00000080;
        private const uint SHGFI_DISPLAYNAME = 0x000000200; // get display name
        private const uint SHGFI_PIDL = 0x000000008;        // pszPath is a pidl

        [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
        public struct SHFILEINFO
        {
            public IntPtr hIcon;
            public int iIcon;
            public uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string szTypeName;
        }

        [StructLayout(LayoutKind.Sequential)]
        public struct HResult
        {
            private int _value;

            public int Value
            {
                get { return _value; }
            }

            public Exception Exception
            {
                get { return Marshal.GetExceptionForHR(_value); }
            }

            public bool IsSuccess
            {
                get { return _value >= 0; }
            }

            public bool IsFailure
            {
                get { return _value < 0; }
            }
        }

    }
}

(完)

推荐阅读