如何通过代码设置另存为对话框的目录?

2023-12-24

基本上,我编写了一些代码,用于监听应用程序内弹出的“另存为”对话框,当它弹出时,它会按下“保存”,所有这些都是通过代码实现的。这很好用,但是我需要能够在保存之前将文件路径设置为我想要的路径。

到目前为止,这是我的代码:

using System;
using System.Diagnostics;
using System.ComponentModel;
using System.Collections.Generic;



using System.Runtime.InteropServices;
using HWND = System.IntPtr;
using System.Text;

/// <summary>Contains functionality to get all the open windows.</summary>
public static class OpenWindowGetter
{
    private const int BN_CLICKED = 245;
    /// <summary>Returns a dictionary that contains the handle and title of all the open windows.</summary>
    /// <returns>A dictionary that contains the handle and title of all the open windows.</returns>
    public static IDictionary<HWND, string> GetOpenWindows()
    {

        HWND shellWindow = GetShellWindow();
        Dictionary<HWND, string> windows = new Dictionary<HWND, string>();

        EnumWindows(delegate (HWND hWnd, int lParam)
        {

            if (hWnd == shellWindow) return true;
            if (!IsWindowVisible(hWnd)) return true;

            int length = GetWindowTextLength(hWnd);
            if (length == 0) return true;

            StringBuilder builder = new StringBuilder(length);
            GetWindowText(hWnd, builder, length + 1);


            if (builder.ToString() == "Export Selection") //Check for the export selection window
            {
                //Press the "save" button through code here
                IntPtr hwndChild = FindWindowEx((IntPtr)hWnd, IntPtr.Zero, "Button", "&Save");
                SendMessage((HWND)(int)hwndChild, BN_CLICKED, (HWND)0, IntPtr.Zero);
            }


            windows[hWnd] = builder.ToString();
            return true;

        }, 0);

        return windows;
    }

    private delegate bool EnumWindowsProc(HWND hWnd, int lParam);


    [DllImport("USER32.DLL")]
    private static extern int SetWindowText(HWND hWnd, String lpString);

    [DllImport("USER32.DLL")]
    private static extern bool EnumWindows(EnumWindowsProc enumFunc, int lParam);

    [DllImport("USER32.DLL")]
    private static extern int GetWindowText(HWND hWnd, StringBuilder lpString, int nMaxCount);

    [DllImport("USER32.DLL")]
    private static extern int GetWindowTextLength(HWND hWnd);

    [DllImport("USER32.DLL")]
    private static extern bool IsWindowVisible(HWND hWnd);

    [DllImport("USER32.DLL")]
    private static extern IntPtr GetShellWindow();

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
    public static extern IntPtr FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lclassName, string windowTitle);

    [DllImport("User32.dll")]
    public static extern Int32 SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);
}



namespace ConsoleApp1
{
    class Program
    {
       
        static void Main(string[] args)
        {
            while (true)
            {


                foreach (KeyValuePair<IntPtr, string> window in OpenWindowGetter.GetOpenWindows())
                {
                    IntPtr handle = window.Key;
                    string title = window.Value;

                    //Console.WriteLine("{0}: {1}", handle, title);
                }

            }
        }
    }
}


我一直在使用 FindWindowEx 和 SendMessage 来控制窗口句柄。这对于“保存”按钮非常有效,但现在我尝试访问窗口的工具栏部分并将其文本设置到所需的目录。我什至不确定这种方法是否有效,但这似乎是最简单的。我附上了一张屏幕截图以供参考(红色圆圈区域是我在“按”保存之前尝试访问并更改为给定文件路径的句柄)

如图所示,我一直在使用 Spy++ 来获取有关窗口及其句柄的信息。例如,如果我尝试这样做来获取指向该句柄的指针:

IntPtr hwndChildToolbar= FindWindowEx((IntPtr)hWnd, IntPtr.Zero, "ToolbarWindow32", "Address: "+"C:\\Windows\\System32");

这不起作用。 Spy++ 应用程序中看到的“Caption”值会更改为当前目录名称,因此尝试访问它似乎没有意义。

下一行代码确实给了我一个返回的指针,但它不是正确的地址。值得注意的是,该窗口上有多个属于 ToolbarWindow32 类的句柄:

IntPtr hwndChildToolbar= FindWindowEx((IntPtr)hWnd, IntPtr.Zero, "ToolbarWindow32", null);

如果我能找到一种方法来获取正确的句柄,那么从这里我只想使用 SetWindowText(如果可能)并将其值设置为我想要的文件路径的字符串。

总而言之,我需要某种方法来轻松设置目录,并且我不确定这种方法是否可行。我的 C# 知识有限,所以一切都有帮助!


下面是一个示例 .NET Framework 控制台应用程序,它使用 UI 自动化,打开记事本,键入内容并将其作为文件保存在临时文件夹中,使用通用对话框顶部的地址栏。

看这里.NET UI 自动化概述 https://learn.microsoft.com/en-us/dotnet/framework/ui-automation/ui-automation-overview或这里本机 UI 自动化 https://learn.microsoft.com/en-us/windows/win32/winauto/entry-uiauto-win32有关 UI 自动化的介绍和参考。使用 UI 自动化通常比破解 Windows 句柄更好。如果您无法使用 UI 自动化来完成此操作,那么您很可能也无法使用句柄来完成此操作。要发现您可以使用和编码的元素,您可以使用Windows SDK 中的检查工具 https://learn.microsoft.com/en-us/windows/win32/winauto/inspect-objects.

注意我在这里使用的是interop version https://www.nuget.org/packages/Interop.UIAutomationClient/原生 UI 自动化的问题,因为 Windows 提供的 .NET 原始包装器出于某种原因多年来一直没有被 Microsoft 更新。

// this code needs the "Interop.UIAutomationClient" Nuget package and "using Interop.UIAutomationClient"
class Program
{
    private static readonly CUIAutomation8 _automation = new CUIAutomation8();

    static void Main()
    {
        // track window open event
        var processId = 0;
        _automation.AddAutomationEventHandler(UIA_EventIds.UIA_Window_WindowOpenedEventId, _automation.GetRootElement(), TreeScope.TreeScope_Subtree, null,
            new AutomationEventHandler((window, id) =>
        {
            // check the process id we opened
            if (window.CurrentProcessId != processId)
                return;

            // get editor control
            var editor = window.FindFirst(TreeScope.TreeScope_Children, _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_EditControlTypeId));
            if (editor == null) // not the window we're looking for
                return;

            // get editor's value pattern & set some text value
            var value = (IUIAutomationValuePattern)editor.GetCurrentPattern(UIA_PatternIds.UIA_ValuePatternId);
            value.SetValue("hello world");

            // get menu bar
            var menuBar = window.FindFirst(TreeScope.TreeScope_Children, _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_MenuBarControlTypeId));
            if (menuBar == null)
            {
                Console.WriteLine("Can't find menu bar.");
                return;
            }

            // get "File" menu item (beware of localization) & invoke (open)
            var file = menuBar.FindFirst(TreeScope.TreeScope_Children, _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_NamePropertyId, "File"));
            if (file == null)
            {
                Console.WriteLine("Can't find 'File' menu item.");
                return;
            }

            // expand "File" menu
            var expand = (IUIAutomationExpandCollapsePattern)file.GetCurrentPattern(UIA_PatternIds.UIA_ExpandCollapsePatternId);
            expand.Expand();

            do
            {
                // get the "Save" item by name from the window subtree (as the menu that opens is a child of the window)
                // do some retry to handle menu opening time
                var save = window.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreatePropertyConditionEx(UIA_PropertyIds.UIA_NamePropertyId, "Save", PropertyConditionFlags.PropertyConditionFlags_MatchSubstring));
                if (save != null)
                {
                    ((IUIAutomationInvokePattern)save.GetCurrentPattern(UIA_PatternIds.UIA_InvokePatternId)).Invoke();
                    break;
                }

            }
            while (true);

            // get the "Save As" dialog
            // do some retry to handle dialog opening time
            IUIAutomationElement dialog;
            do
            {
                dialog = window.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_LocalizedControlTypePropertyId, "dialog"));
                if (dialog != null)
                    break;
            }
            while (true);

            // get the "Previous locations" to enable the Address edit box
            var previous = dialog.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreateAndCondition(
                _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_ButtonControlTypeId),
                _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_NamePropertyId, "Previous Locations")));
            if (previous == null)
            {
                Console.WriteLine("Can't find 'Previous Locations' button.");
                return;
            }

            // push "Previous Locations" button
            var previousButton = (IUIAutomationInvokePattern)previous.GetCurrentPattern(UIA_PatternIds.UIA_InvokePatternId);
            previousButton.Invoke();

            // enter the directory path
            var address = dialog.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreateAndCondition(
                _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_EditControlTypeId),
                _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_NamePropertyId, "Address")));
            if (address == null)
            {
                Console.WriteLine("Can't find 'Address' edit.");
                return;
            }

            // sets the directory (here we use the temp directory)
            var edit = (IUIAutomationValuePattern)address.GetCurrentPattern(UIA_PatternIds.UIA_ValuePatternId);
            edit.SetValue(System.IO.Path.GetTempPath());

            // push "Previous Locations" button again to "commit"
            previousButton.Invoke();

            // get the "File name:" edit
            // do some retry to handle folder refresh
            do
            {
                var fileName = dialog.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreateAndCondition(
                    _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_EditControlTypeId),
                    _automation.CreatePropertyConditionEx(UIA_PropertyIds.UIA_NamePropertyId, "File name", PropertyConditionFlags.PropertyConditionFlags_MatchSubstring)));
                if (fileName != null)
                {
                    // sets the file name (some "random" name)
                    ((IUIAutomationValuePattern)fileName.GetCurrentPattern(UIA_PatternIds.UIA_ValuePatternId)).SetValue(@"hello" + Environment.TickCount + ".txt");
                    break;
                }
            }
            while (true);

            // get the "Save" button
            var dialogSave = dialog.FindFirst(TreeScope.TreeScope_Subtree, _automation.CreateAndCondition(
                _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_ControlTypePropertyId, UIA_ControlTypeIds.UIA_ButtonControlTypeId),
                _automation.CreatePropertyCondition(UIA_PropertyIds.UIA_NamePropertyId, "Save")));
            if (dialogSave == null)
            {
                Console.WriteLine("Can't find 'Save' button.");
                return;
            }

            // press the 'Save' button
            ((IUIAutomationInvokePattern)dialogSave.GetCurrentPattern(UIA_PatternIds.UIA_InvokePatternId)).Invoke();
        }));

        // start notepad
        var process = Process.Start("notepad");
        processId = process.Id;

        Console.WriteLine("Press any key to quit...");
        Console.ReadKey(false);
        try
        {
            process.CloseMainWindow();
        }
        catch
        {
            // maybe closed by something else, do nothing
        }
    }

    // helper class
    class AutomationEventHandler : IUIAutomationEventHandler
    {
        public AutomationEventHandler(Action<IUIAutomationElement, int> action)
        {
            if (action == null)
                throw new ArgumentNullException(nameof(action));

            Action = action;
        }

        public Action<IUIAutomationElement, int> Action { get; }
        public void HandleAutomationEvent(IUIAutomationElement sender, int eventId) => Action(sender, eventId);
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何通过代码设置另存为对话框的目录? 的相关文章

  • 通过 SocketCAN 进行 boost::asio

    我正在考虑利用升压阿西奥 http www boost org doc libs 1 49 0 doc html boost asio html从a读取数据套接字CAN http en wikipedia org wiki SocketCA
  • 为什么在创建矩阵类时使用向量不好?

    对于我的矩阵类 我做了 template
  • 如何在 C# / .NET 中创建内存泄漏[重复]

    这个问题在这里已经有答案了 可能的重复 托管代码中是否可能存在内存泄漏 特别是 C 3 0 https stackoverflow com questions 6436620 is it possible to have a memory
  • 防止 boost::asio::io_context 在空轮询调用时停止

    此代码调用发布的句柄 boost asio io context ioc boost asio post ioc std cout lt lt lol lt lt std endl ioc poll 而这并没有 boost asio io
  • 信号处理程序有单独的堆栈吗?

    信号处理程序是否有单独的堆栈 就像每个线程都有单独的堆栈一样 这是在 Linux C 环境中 来自 Linux 手册页signal 7 http kernel org doc man pages online pages man7 sign
  • 在 JSQMessagesViewController 中显示 LocationMediaItem

    我刚刚尝试实施LocationMediaItem in my Xamarin iOS应用程序使用JSQMessagesViewController 一切都很顺利 唯一的问题是UICollectionView应该显示位置的单元格永远停留在加载
  • C++ php 和静态库

    我创建了一个library a 其中包含 cpp 和 h 文件 其中包含很多类 嵌套类和方法 我想在 php 示例中包含这个静态库并尝试使用它 我想提一下 我是 php 新手 我已经在 test cpp 文件中测试了我的 libray a
  • 将二进制数据从 C# 上传到 PHP

    我想将文件从 Windows C 应用程序上传到运行 PHP 的 Web 服务器 我知道 WebClient UploadFile 方法 但我希望能够分块上传文件 以便我可以监控进度并能够暂停 恢复 因此 我正在读取文件的一部分并使用 We
  • AES 输出是否小于输入?

    我想加密一个字符串并将其嵌入到 URL 中 因此我想确保加密的输出不大于输入 AES 是可行的方法吗 不可能创建任何始终会创建比输入更小的输出的算法 但可以将任何输出反转回输入 如果您允许 不大于输入 那么基本上您只是在谈论同构算法alwa
  • 如何分析组合的 python 和 c 代码

    我有一个由多个 python 脚本组成的应用程序 其中一些脚本正在调用 C 代码 该应用程序现在的运行速度比以前慢得多 因此我想对其进行分析以查看问题所在 是否有工具 软件包或只是一种分析此类应用程序的方法 有一个工具可以将 python
  • ASP.NET MailMessage.BodyEncoding 和 MailMessage.SubjectEncoding 默认值

    很简单的问题 但我在 MSDN 上找不到答案 查找 ASP NET 将用于的默认值 MailMessage BodyEncoding and MailMessage SubjectEncoding 如果你不在代码中设置它们 Thanks F
  • C# 中的 strstr() 等效项

    我有两个byte 我想找到第二个的第一次出现byte 在第一个byte 或其中的一个范围 我不想使用字符串来提高效率 翻译第一个byte to a string会效率低下 基本上我相信就是这样strstr 在 C 中做 最好的方法是什么 这
  • 新任务中使用的依赖注入服务

    我在需要时使用依赖项注入来访问我的服务 但我现在想要创建一个并发任务 但这会由于依赖项注入对象及其生命周期而导致问题 我读过这篇文章 标题 防止多线程 Link http mehdi me ambient dbcontext in ef6
  • 了解使用 Windows 本机 WPF 客户端进行 ADFS 登录

    我已经阅读了大量有关 ADFS 与 NodeJS Angular 或其他前端 Web 框架集成以及一般流程如何工作的文献 并通过 Auth0 Angular 起始代码构建了概念证明 但我不明白如何这可以与本机 WPF Windows 应用程
  • 更改 Windows Phone 系统托盘颜色

    有没有办法将 Windows Phone 上的系统托盘颜色从黑色更改为白色 我的应用程序有白色背景 所以我希望系统托盘也是白色的 您可以在页面 XAML 中执行此操作
  • 如何在 DropDownList 中保留空格 - ASP.net MVC Razor 视图

    我在视图中通过以下方式绑定我的模型 问题是我的项目文本是格式化文本 单词之间有空格 如下所示 123 First 234 00 123 AnotherItem 234 00 123 Second 234 00 我想保留此项目文本中的空格 即
  • 矩阵到数组 C#

    这将是转换方阵的最有效方法 例如 1 2 3 4 5 6 7 8 9 into 1 2 3 4 5 6 7 8 9 in c 我在做 int array2D new int 1 2 3 4 5 6 7 8 9 int array1D new
  • 在简单注入器中解析具有自定义参数的类

    我正在使用以下命令创建 WPF MVVM 应用程序简易注射器作为 DI 容器 现在 当我尝试从简单注入器解析视图时遇到一些问题 因为我需要在构造时将参数传递到构造函数中 而不是在将视图注册到容器时 因此这不是适用的 简单注入器将值传递到构造
  • 使我的 COM 程序集调用异步

    我刚刚 赢得 了在当前工作中维护用 C 编码的遗留库的特权 这个dll 公开使用 Uniface 构建的大型遗留系统的方法 除了调用 COM 对象之外别无选择 充当此遗留系统与另一个系统的 API 之间的链接 在某些情况下 使用 WinFo
  • Java 和/C++ 在多线程方面的差异

    我读过一些提示 多线程实现很大程度上取决于您正在使用的目标操作系统 操作系统最终提供了多线程能力 比如Linux有POSIX标准实现 而windows32有另一种方式 但我想知道编程语言水平的主要不同 C似乎为同步提供了更多选择 例如互斥锁

随机推荐

  • pytesseract 不适用于一位数字图像

    我有使用 pytesseract 的代码并且工作完美 只有当我尝试识别的图像是 0 到 9 时才不起作用 如果图像只有一位数字 则不会给出任何结果 这是我正在工作的图像样本 这是我正在使用的代码 import pytesseract var
  • Scala:值 :: 不是 Int 的成员

    我最近开始使用 scala 但我无法获取任何错误消息 对于以下代码 我得到指定的消息 使用 eclipse def helper Int gt List Int x gt x match case 2 gt 2 1 我可以使用 List 2
  • getSignedUrl() 和 getDownloadUrl() 之间的区别

    Node js 上的 get getSignedUrl 方法与 SDK 上的 getDownloadURL 方法有什么区别 我用的是颤动 与我在云函数中使用的 getSignedUrl 一样 当图像更改时返回的url不会更改 具有相同的文件
  • 作为一个原子操作插入到 azure cosmos db 中的多个容器

    我对 Azure Cosmos DB 有点陌生 我想知道它是否可以选择将多个容器上的多个操作作为一个原子操作 例如 所有成功或失败都来自 NET 后端 对于单个容器中的单个操作来说 操作是原子的 如果您使用存储过程 则可以在单个容器内的单个
  • Microsoft Excel 保存文件时使用什么字符集?

    我有一个 Java 应用程序 可以读取在 Excel 中创建的 CSV 文件 例如 2007 有谁知道 MS Excel 使用什么字符集来保存这些文件 我会猜测 windows 1255 Cp1255 ISO 8859 1 UTF8 但我无
  • 保护 ajax 请求的安全

    我有一个使用会话 cookie 来确保安全的网站 它工作得很好 但是现在任何 ajax 请求都不安全 例如 假设用户在某个页面上 他们只有通过会话登录才能访问此页面 到目前为止一切顺利 但现在他们要求的ajax请求是 ajaxpages s
  • Gradle 工件插件无法解决对配置阶段的依赖

    我正在尝试使用artifactory gradle 插件解决配置阶段的依赖关系 apply plugin java apply plugin com jfrog artifactory artifactory contextUrl arti
  • 我们可以使用贪心算法而不是动态规划来解决“整齐打印”问题吗?

    算法导论 书中的 打印整齐 问题是通过动态规划来解决的 是问题5 3 已找到解决方案here https segue middlebury edu repository viewfile polyphony repository repos
  • 如何通过 OpenCV 和 Python 通过索引从视频中获取帧?

    我需要通过帧索引访问视频中的帧 到目前为止我使用了这样的代码 video cv2 VideoCapture video path status frame video read 该代码读取第一帧 如果我重复使用该代码 我将获得下一帧 但是我
  • CPU 中的相关负载重新排序

    我一直在阅读内存屏障 软件黑客的硬件视图 http www puppetmastertrading com images hwViewForSwHackers pdf 保罗 E 麦肯尼 Paul E McKenney 撰写的一篇非常受欢迎的
  • 电子邮件上的 CSS

    有没有人找到一种在以编程方式生成的电子邮件中嵌入 CSS 的好方法 我发现的最好方法是将样式代码放入资源文件中并在代码中调用它 一个例子是 Dim objBuilder objBuilder New StringBuilder objBui
  • 如何关闭本地 firebase 模拟器?

    目前我使用以下命令初始化 firebase 模拟器 firebase emulators start 经过一段时间的研究后 我想停止它 那么我怎样才能停止模拟器呢 查看端口被哪个进程占用sudo lsof i tcp
  • 从 htaccess 中的 URL 中删除不需要的字符

    我们当前的 htaccess 设置正确地将 URL 转换为这样 site com page php sid Friend 到 site com Friend 然而 由于不相关的疏忽 我们几乎所有的 URL 都被双索引为 site com F
  • phpmyadmin:创建一个函数

    我正在尝试在我的 phpmyadmin 中创建一个函数 不起作用 这是我的语法 DELIMITER CREATE FUNCTION fixString input varchar RETURNS varchar BEGIN declare
  • 如何在 TypeORM queryBuilder 中显示生成的 SQL/原始 SQL

    我开发了typeorm querybuilder 为了调试的目的 我想显示生成的 SQL 查询 我测试过printSql 方法 但它没有显示任何 SQL 查询 const Result await this attendanceReposi
  • R Keras:将tensorflow张量转换为R数组

    我正在使用 R Keras 我可以通过执行以下命令来获取中间层的输出 layer output lt get layer mymodel index 1 output 其中 mymodel 是 Keras 模型 问题是layer outpu
  • 更新拉丝面积图的 y 轴

    我正在使用 d3 js 并且我正在通过修改来处理拉丝面积图这个例子 http bl ocks org 1667367 除了根据画笔改变 x 轴之外 我还希望根据画笔内数据的 y 值重新绘制图表的 y 轴 类似于 我已经使该功能正常工作 但只
  • Spring boot @DataJpaTest排除过滤器不起作用

    我有这个测试 RunWith SpringRunner class DataJpaTest excludeFilters Filter type FilterType REGEX pattern io rainrobot adwisdom
  • @PostConstruct 拦截器与 @Named @ViewScoped 未调用

    我仔细阅读了有关的文章拦截器 http docs jboss org weld reference 1 0 0 en US html interceptors html在接缝 焊接文档中并实施了InterceptorBinding Inte
  • 如何通过代码设置另存为对话框的目录?

    基本上 我编写了一些代码 用于监听应用程序内弹出的 另存为 对话框 当它弹出时 它会按下 保存 所有这些都是通过代码实现的 这很好用 但是我需要能够在保存之前将文件路径设置为我想要的路径 到目前为止 这是我的代码 using System