多 DPI 系统上的 VSTO 自定义任务窗格显示内容两次

2024-01-07

我正在使用 VSTO 构建一个 Office 插件。在具有不同 DPI 设置的多个显示器的系统上,我的自定义任务窗格的内容在具有较高 DPI 设置的显示器上绘制两次:

只有较小的版本真正响应用户输入。较大的版本似乎只是放大的图像。

我尝试过使用各种 DPI 相关设置,例如:

  • AutoScaleMode在我的用户控件上。我尝试了所有选项,没有改变。
  • 使用以下命令将进程设置为 DPI 感知(或不感知)SetProcessDpiAwareness。我尝试了所有选项,没有改变。
  • 使用 app.manifest 和设置dpiAware to true and false。不用找了。

新的 Web Addins 没有这个问题。此外,内部任务窗格不存在此问题。

这是一个已知问题吗?我怎样才能解决这个问题?


这似乎是 Office 产品处理数据的方式中的一个错误。WM_DPICHANGED https://msdn.microsoft.com/en-us/library/windows/desktop/dn312083%28v=vs.85%29.aspx信息。应用程序应该枚举其所有子窗口并重新缩放它们以响应消息,但不知何故无法正确处理加载项窗格。

您可以通过禁用 DPI 缩放来解决该错误。你说你尝试调用SetProcessDpiAwareness,但一旦为应用程序设置了 DPI 感知,该功能就会失败,并且您正在使用的应用程序显然已经设置了它,因为它适用于父窗口。然后你应该做的是调用SetThreadDpiAwarenessContext https://msdn.microsoft.com/en-us/library/windows/desktop/mt748629%28v=vs.85%29.aspx, 像这个 C# 包装器 https://github.com/shawnmcdowell/Office-Extensibility-Samples/blob/e52661e0e627b97945ab16a3ecd1292e7af055d2/VSTOSharedAddin/VSTOSharedAddin/UserControlWinForm.cs#L87。不幸的是,我没有 Win10 multimon 设置来自己测试这一点,但这应该在应用程序运行时起作用。尝试这个插件 https://github.com/shawnmcdowell/Office-Extensibility-Samples/tree/e52661e0e627b97945ab16a3ecd1292e7af055d2/VSTOSharedAddin/VSTOSharedAddin,它有一个按钮可以设置线程 DPI 感知上下文,看看这是否适合您。


应用程序挂钩方法

Since SetThreadDpiAwarenessContext可能在您的系统上不可用,处理该问题的一种方法是使主窗口忽略WM_DPICHANGED信息。这可以通过安装应用程序挂钩来更改消息或通过子类化窗口来完成。应用程序挂钩是一种稍微简单一些的方法,陷阱也较少。基本上这个想法是拦截主应用程序的GetMessage and change WM_DPICHANGED to WM_NULL https://stackoverflow.com/a/42773037/4454124,这将使应用程序丢弃该消息。缺点是这种方法仅适用于已发布的消息,但是WM_DPICHANGED应该是其中之一。

因此,要安装应用程序挂钩,您的加载项代码将类似于:

public partial class ThisAddIn
{
    public enum HookType : int
    {
        WH_JOURNALRECORD = 0,
        WH_JOURNALPLAYBACK = 1,
        WH_KEYBOARD = 2,
        WH_GETMESSAGE = 3,
        WH_CALLWNDPROC = 4,
        WH_CBT = 5,
        WH_SYSMSGFILTER = 6,
        WH_MOUSE = 7,
        WH_HARDWARE = 8,
        WH_DEBUG = 9,
        WH_SHELL = 10,
        WH_FOREGROUNDIDLE = 11,
        WH_CALLWNDPROCRET = 12,
        WH_KEYBOARD_LL = 13,
        WH_MOUSE_LL = 14
    }

    delegate IntPtr HookProc(int code, IntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll", SetLastError = true)]
    static extern IntPtr SetWindowsHookEx(HookType hookType, HookProc lpfn, IntPtr hMod, uint dwThreadId);
    [DllImport("user32.dll")]
    static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);


    [StructLayout(LayoutKind.Sequential)]
    public struct POINT
    {
        public int X;
        public int Y;
    }
    public struct MSG
    {
        public IntPtr hwnd;
        public uint message;
        public IntPtr wParam;
        public IntPtr lParam;
        public uint time;
        public POINT pt;
    }

    HookProc cbGetMessage = null;

    private UserControl1 myUserControl1;
    private Microsoft.Office.Tools.CustomTaskPane myCustomTaskPane;
    private void ThisAddIn_Startup(object sender, System.EventArgs e)
    {
        this.cbGetMessage = new HookProc(this.MyGetMessageCb);
        SetWindowsHookEx(HookType.WH_GETMESSAGE, this.cbGetMessage, IntPtr.Zero, (uint)AppDomain.GetCurrentThreadId());

        myUserControl1 = new UserControl1();
        myCustomTaskPane = this.CustomTaskPanes.Add(myUserControl1, "My Task Pane");
        myCustomTaskPane.Visible = true;


    }

    private IntPtr MyGetMessageCb(int code, IntPtr wParam, IntPtr lParam)
    {
        unsafe
        {
            MSG* msg = (MSG*)lParam;
            if (msg->message == 0x02E0)
                msg->message = 0;
        }

        return CallNextHookEx(IntPtr.Zero, code, wParam, lParam);
    }

    private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
    {
    }

    #region VSTO generated code

    private void InternalStartup()
    {
        this.Startup += new System.EventHandler(ThisAddIn_Startup);
        this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
    }

    #endregion
}

请注意,这很大程度上是未经测试的代码,如果它可以阻止WM_DPICHANGED您可能必须确保在应用程序退出之前删除挂钩来清理消息。


子类化方法

如果您想要阻止的消息没有发布到窗口,而是发送,则应用程序挂钩方法将不起作用,并且主窗口必须被相反,子类化 https://msdn.microsoft.com/en-us/library/windows/desktop/bb773183%28v=vs.85%29.aspx。这次我们将把代码放在用户控件中,因为主窗口需要在调用之前完全初始化SetWindowLong.

因此,为了子类化 Power Point 窗口,我们的用户控件(位于插件内)看起来像这样(请注意,我为此使用 OnPaint,但您可以使用任何内容,只要保证窗口在调用SetWindowLong):

public partial class UserControl1 : UserControl
{
    const int GWLP_WNDPROC = -4;
    [DllImport("user32", SetLastError = true)]
    extern static IntPtr CallWindowProc(IntPtr lpPrevWndFunc, IntPtr hWnd, uint uMsg, IntPtr wParam, IntPtr lParam);
    [DllImport("user32", SetLastError = true)]
    extern static IntPtr SetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr lpNewLong);
    [DllImport("user32", SetLastError = true)]
    extern static IntPtr SetWindowLong(IntPtr hWnd, int nIndex, IntPtr lpNewLong);
    delegate IntPtr WindowProc(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam);
    private IntPtr origProc = IntPtr.Zero;
    private WindowProc wpDelegate = null;
    public UserControl1()
    {
        InitializeComponent();
        this.Paint += UserControl1_Paint;

    }

    void UserControl1_Paint(object sender, PaintEventArgs e)
    {
        if (origProc == IntPtr.Zero)
        {
            //Subclassing
            this.wpDelegate = new WindowProc(MyWndProc);
            Process process = Process.GetCurrentProcess();
            IntPtr wpDelegatePtr = Marshal.GetFunctionPointerForDelegate(wpDelegate);
            if (IntPtr.Size == 8)
            {
                origProc = SetWindowLongPtr(process.MainWindowHandle, GWLP_WNDPROC, wpDelegatePtr);
            }
            else
            {
                origProc = SetWindowLong(process.MainWindowHandle, GWLP_WNDPROC, wpDelegatePtr);
            }
        }
    }


    //Subclassing
    private IntPtr MyWndProc(IntPtr hwnd, uint uMsg, IntPtr wParam, IntPtr lParam)
    {
        if (uMsg == 0x02E0) //WM_DPICHANGED
            return IntPtr.Zero;
        IntPtr retVal = CallWindowProc(origProc, hwnd, uMsg, wParam, lParam);
        return retVal;
    }
}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

多 DPI 系统上的 VSTO 自定义任务窗格显示内容两次 的相关文章

  • BASIC 中的 C 语言中的 PeekInt、PokeInt、Peek、Poke 等效项

    我想知道该命令的等效项是什么Peek and Poke 基本和其他变体 用 C 语言 类似PeekInt PokeInt 整数 涉及内存条的东西 我知道在 C 语言中有很多方法可以做到这一点 我正在尝试将基本程序移植到 C 语言 这只是使用
  • 在 xaml 中编写嵌套类型时出现设计时错误

    我创建了一个用户控件 它接受枚举类型并将该枚举的值分配给该用户控件中的 ComboBox 控件 很简单 我在数据模板中使用此用户控件 当出现嵌套类型时 问题就来了 我使用这个符号来指定 EnumType x Type myNamespace
  • 没有特殊字符的密码验证器

    我是 RegEx 的新手 已经进行了大量搜索 但没有找到任何具体内容 我正在编写一个验证密码字符串的正则表达式 可接受的字符串必须至少具有 4 种字符类型中的 3 种 数字 小写字母 大写字母 特殊字符 我对包含有一个想法 也就是说 如果这
  • 在一个数据访问层中处理多个连接字符串

    我有一个有趣的困境 我目前有一个数据访问层 它必须与多个域一起使用 并且每个域都有多个数据库存储库 具体取决于所调用的存储过程 目前 我只需使用 SWITCH 语句来确定应用程序正在运行的计算机 并从 Web config 返回适当的连接字
  • 如何从 Visual Studio 将视图导航到其控制器?

    问题是解决方案资源管理器上有 29 个项目 而且项目同时具有 ASP NET MVC 和 ASP NET Web 表单结构 在MVC部分中 Controller文件夹中有大约100个子文件夹 每个文件夹至少有3 4个控制器 视图完全位于不同
  • 为什么 GCC 不允许我创建“内联静态 std::stringstream”?

    我将直接前往 MCVE include
  • 传递给函数时多维数组的指针类型是什么? [复制]

    这个问题在这里已经有答案了 我在大学课堂上学习了 C 语言和指针 除了多维数组和指针之间的相似性之外 我认为我已经很好地掌握了这个概念 我认为由于所有数组 甚至多维 都存储在连续内存中 因此您可以安全地将其转换为int 假设给定的数组是in
  • 从经典 ASP 调用 .Net C# DLL 方法

    我正在开发一个经典的 asp 项目 该项目需要将字符串发送到 DLL DLL 会将其序列化并发送到 Zebra 热敏打印机 我已经构建了我的 DLL 并使用它注册了regasm其次是 代码库这使得 IIS 能够识别它 虽然我可以设置我的对象
  • -webkit-box-shadow 与 QtWebKit 模糊?

    当时有什么方法可以实现 webkit box shadow 的工作模糊吗 看完这篇评论错误报告 https bugs webkit org show bug cgi id 23291 我认识到这仍然是一个问题 尽管错误报告被标记为RESOL
  • 人脸 API DetectAsync 错误

    我想创建一个简单的程序来使用 Microsoft Azure Face API 和 Visual Studio 2015 检测人脸 遵循 https social technet microsoft com wiki contents ar
  • WcfSvcHost 的跨域异常

    对于另一个跨域问题 我深表歉意 我一整天都在与这个问题作斗争 现在已经到了沸腾的地步 我有一个 Silverlight 应用程序项目 SLApp1 一个用于托管 Silverlight SLApp1 Web 的 Web 项目和 WCF 项目
  • 如何定义一个可结构化绑定的对象的概念?

    我想定义一个concept可以检测类型是否T can be 结构化绑定 or not template
  • C# xml序列化必填字段

    我需要将一些字段标记为需要写入 XML 文件 但没有成功 我有一个包含约 30 个属性的配置类 这就是为什么我不能像这样封装所有属性 public string SomeProp get return someProp set if som
  • 如何在当前 Visual Studio 主机内的 Visual Studio 扩展中调试使用 Roslyn 编译的代码?

    我有一个 Visual Studio 扩展 它使用 Roslyn 获取当前打开的解决方案中的项目 编译它并从中运行方法 程序员可以修改该项目 我已从当前 VisualStudioWorkspace 成功编译了 Visual Studio 扩
  • C# 动态/expando 对象的深度/嵌套/递归合并

    我需要在 C 中 合并 2 个动态对象 我在 stackexchange 上找到的所有内容仅涵盖非递归合并 但我正在寻找能够进行递归或深度合并的东西 非常类似于jQuery 的 extend obj1 obj2 http api jquer
  • 如何在 Linq to SQL 中使用distinct 和 group by

    我正在尝试将以下 sql 转换为 Linq 2 SQL select groupId count distinct userId from processroundissueinstance group by groupId 这是我的代码
  • 使用特定参数从 SQL 数据库填充组合框

    我在使用参数从 sql server 获取特定值时遇到问题 任何人都可以解释一下为什么它在 winfom 上工作但在 wpf 上不起作用以及我如何修复它 我的代码 private void UpdateItems COMBOBOX1 Ite
  • 对于某些 PDF 文件,LoadIFilter() 返回 -2147467259

    我正在尝试使用 Adob e IFilter 搜索 PDF 文件 我的代码是用 C 编写的 我使用 p invoke 来获取 IFilter 的实例 DllImport query dll SetLastError true CharSet
  • 当文件流没有新数据时如何防止fgets阻塞

    我有一个popen 执行的函数tail f sometextfile 只要文件流中有数据显然我就可以通过fgets 现在 如果没有新数据来自尾部 fgets 挂起 我试过ferror and feof 无济于事 我怎样才能确定fgets 当
  • 使用 WGL 创建现代 OpenGL 上下文?

    我正在尝试使用 Windows 函数创建 OpenGL 上下文 现代版本 基本上代码就是 创建窗口类 注册班级 创建一个窗口 choose PIXELFORMATDESCRIPTOR并设置它 创建旧版 OpenGL 上下文 使上下文成为当前

随机推荐