cad.net 同一个命令开关参照面板

功能演示

alt 图片

我要判断是否已经打开了参照面板,从而进行重复命令都开关面板.
然而cad自己没有相关的系统变量.
这时我就需要利用到Win32API来判断程序是否打开了参照面板了

Win32API

首先学习的是从.NET平台调用 Win32API

SPY++

其次,我们要用到spy++这个工具来看windowns窗口的句柄,
注意这个工具若没有的话,
要在控制面板上面选择vs,然后安装c++的相关内容,不然只安装net是没有的...
工具--导入和导出设置--重置所有设置--Visual C++,选择C++工程的环境!!
然后就可以利用spy++查找的拖拽来拖到cad窗口上...
alt 图片

alt 图片

代码:

Win32API

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

namespace JingJingBoxDD;

public partial class Win32api
{
    // https://blog.csdn.net/bcbobo21cn/article/details/50930221

    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 List<WindowInfo> GetAllDesktopWindows()
    {
        //用来保存窗口对象 列表
        var wndList = new List<WindowInfo>();
        //枚举所有桌面窗口
        EnumWindows(delegate (IntPtr hWnd, int lParam)
        {
            var wnd = new WindowInfo();
            var sb = new StringBuilder(256);
            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;
    }
}

public struct InternetExplorer
{
    public IntPtr HWND;//句柄
    public string LocationURL;//文件夹路径
    public void Quit() =>  //关闭文件夹
        Win32api.QuitToolbar(HWND);
}

/// <summary>
/// 遍历桌面资源管理器
/// </summary>
public class ShellWindows : IEnumerable
{
    private static string[] wins = [ "WorkerW",
        "ReBarWindow32",
        "Address Band Root",
        "msctls_progress32",
        "Breadcrumb Parent",
        "ToolbarWindow32"/*资源管理器*/
    ];
    private List<InternetExplorer> list;
    public IEnumerator GetEnumerator()
    {
        return (list as IEnumerable).GetEnumerator();
    }

    /// <summary>
    /// 获取桌面所有文件夹的路径
    /// </summary>
    /// <returns></returns>
    public ShellWindows()
    {
        list = new();
        var allDesktop = Win32api.GetAllDesktopWindows();
        foreach (var item in allDesktop)
        {
            if (item.className != "CabinetWClass")
                continue;
            // 顺序查找
            var fi = item.hwnd;
            for (int i = 0; i < wins.Length; i++)
            {
                fi = Win32api.FindWindowEx(fi, 0, wins[i], null);
                if (fi == IntPtr.Zero) break;
            }
            // toolbar上的按钮没有handler,要用发送通知信息
            if (fi != IntPtr.Zero) continue;
            var sb = new StringBuilder(256);
            // 获取窗口名称-路径地址
            _ = Win32api.GetWindowText(fi, sb, sb.Capacity);
            string path = sb.Remove(0, 4).ToString();//4表示"地址: "长度
            list.Add(new() { HWND = item.hwnd, LocationURL = path });
        }
    }
}

public class LocalPrinter
{
    /// <summary>
    /// 设置默认打印机    
    /// </summary>
    /// <param name="Name"></param>
    /// <returns></returns>
    [DllImport("winspool.drv")]
    public static extern bool SetDefaultPrinter(string Name);

    /// <summary>
    /// 系统所有打印机名称(默认将在第一)
    /// </summary> 
    public static List<string> GetLocalPrinters()
    {
        // 这里要是用一个插入序Map该多好.
        var ps = new List<string>();
        var sets = new HashSet<string>();
        try
        {
            // 遍历全部打印机
            foreach (string item in PrinterSettings.InstalledPrinters)
                sets.Add(item);

            // 默认打印机始终出现在列表的第一项
            var fs = new System.Drawing.Printing
            .PrintDocument()
            .PrinterSettings.PrinterName;
            if (fs != null)
            {
                sets.Remove(fs);
                ps.Add(fs);
            }
            ps.AddRange(sets);
        }
        catch { }
        return ps;
    }
}

命令

using JingJingBoxDD;

public class 开关参照面板
{
    private static bool Chongfu = false;

    // 重复执行命令可以关闭和打开参照面板
    [CommandMethod(nameof(JJ_er), CommandFlags.Session)]//发送同步命令
    public static void JJ_er()
    {
        if (Chongfu)
        {
            Chongfu = !Chongfu;
            return;
        }
        const string canzhao = "外部参照";
        var wndList = Win32api.GetAllDesktopWindows();
        IntPtr open = IntPtr.Zero;
#if HC2019
        var regexGcad = new Regex("浩辰");
        foreach (var item in wndList)
        {
            if (!regexGcad.IsMatch(item.windowName) continue;         
            var fi = Win32api.FindWindowEx(item.hwnd, 0, null, canzhao);//"外部参照"子窗口
            if (fi == IntPtr.Zero) continue;
            if (!Win32api.IsWindowVisible(fi)) continue;
            open = fi;
            break;
       }
#else
        var regexAcad = new Regex("AutoCAD");
        foreach (var item in wndList)
        {
            if (regexAcad.IsMatch(item.windowName))
            {
                var fi = Win32api.FindWindowEx(item.hwnd, 0, null, "特性");//用过ctrl+1这里会变为"特性"子窗口 
                if (fi == IntPtr.Zero)
                    fi = Win32api.FindWindowEx(item.hwnd, 0, null, canzhao);//"外部参照"子窗口
                if (fi == IntPtr.Zero)
                    fi = Win32api.FindWindowEx(item.hwnd, 0, null, "块编写选项板");
                if (fi == IntPtr.Zero) continue;
                fi = Win32api.FindWindowEx(fi, 0, null, canzhao);//找到"特性"或"外部参照"下的"外部参照"子子窗口
                if (fi != IntPtr.Zero)
                {
                    open = fi;
                    break;
                }
            }
            else if (item.windowName == canzhao)
            {
                open = item.hwnd;
                break;
            }
        }
#endif

        if (open == IntPtr.Zero)
            foreach (var item in wndList)
                if (item.windowName.Contains(canzhao))
                {
                    open = item.hwnd;
                    break;
                }
        bool sendClose = false;
        // 浩辰的参照面板拖拉出来,然后放回索引箭头靠边,在这里判断
        if (open != IntPtr.Zero)
            sendClose = Win32api.IsWindowVisible(open);//可见

        // 发送开关参照面板命令
        SendExter(sendClose);
        // 实现空格重复本命令
        Chongfu = true;
        SendToCad.SendCommand(nameof(JJ_er));
    }

    /// <summary>
    /// 开关参照面板
    /// </summary>
    private static void SendExter(bool closeFlag)
    {
        string c = closeFlag ? "Externalreferencesclose" : "Externalreferences";
        // 如果已经是退出编辑参照状态,然后发送打开参照面板,
        // 但仍然程序会提示并阻止 "** 编辑参照时不允许使用 EXTERNALREFERENCES 命令 **"  
        // 可能是CAD长事务bug,解决方案如下

        SendToCad.SendCommand(c);//先发送一次,尝试触发异常
        var blockName = CadSystem.Getvar("refeditname");//是否有在位编辑
        if (blockName != "") return;//无编辑状态
        string last = CadSystem.Getvar("lastprompt"); //再获取最后一行命令
        Regex re = new("编辑参照时不允许使用");
        if (!re.IsMatch(last)) return;//无异常
        SendToCad.SendCommand("_qsave");
        SendToCad.SendCommand("_u");
        SendToCad.SendCommand(c);//最后再次发送打开
    }
}

(完)

posted @ 2019-02-12 21:08  惊惊  阅读(959)  评论(0编辑  收藏  举报