I am building an office addin using VSTO. On systems with multiple monitors with different DPI settings, the contents of my custom task pane is drawn twice on the monitor with the higher DPI settings:
Only the smaller version is actually responding to user input. The larger version seems to be simply an upscaled image.
I have tried playing around with diverse DPI related settings like:
AutoScaleMode
on my user control. I tried all options, no change.- Setting the process to DPI aware - or not - using
SetProcessDpiAwareness
. I tried all options, no change. - Using an app.manifest and setting
dpiAware
totrue
andfalse
. No change.
The new Web Addins don't have this problem. Also, the internal task panes don't have this problem.
Is this a known problem? How can I fix this?
This seems to be a bug in Office products in the way they handle the processing of the
WM_DPICHANGED
message. The application is supposed to enumerate all of its child windows and rescale them in response to the message but it's somehow failing to process add-in panes properly.What you can do to work around the bug is disable DPI scaling. You say you tried invoking
SetProcessDpiAwareness
, but that function is documented to fail once DPI awareness has been set for an app, and the app you're using clearly has it set because it works for the parent window. What you are supposed to do then is invokeSetThreadDpiAwarenessContext
, like in this C# wrapper. Unfortunately I don't have a Win10 multimon setup to test this myself, but that's supposed to work as the application is running. Try this add-in, it has a button to set thread DPI awareness context, and see if that works for you.The application hook approach
Since
SetThreadDpiAwarenessContext
may not be available on your system, one way to deal with the problem is to make the main window ignore theWM_DPICHANGED
message. This can be done either by installing an application hook to change the message or by subclassing the window. An application hook is a slightly easier approach with fewer pitfalls. Basically the idea is to intercept the main application'sGetMessage
and changeWM_DPICHANGED
toWM_NULL
, which will make the application discard the message. The drawback is that this approach only works for posted messages, butWM_DPICHANGED
should be one of those.So to install an application hook, your add-in code would look something like:
Please note that this is largely untested code, and if it works in blocking the
WM_DPICHANGED
message you will probably have to make sure to clean up by removing the hook before application exit.The subclassing approach
If the message you want to block is not posted to the window, but sent instead, the application hook method is not going to work and the main window will have to be subclassed instead. This time we will place our code within the user control because the main windows needs to be fully initialized before invoking
SetWindowLong
.So to subclass the Power Point window, our user control (which is within the addin) would look something like (note that I am using OnPaint for this but you can use whatever as long as it's guaranteed that the window is initialized at the time of invoking
SetWindowLong
):Try to add the following code to the ctor of your form:
Also you may find the Creating a DPI-Aware Application thread helpful.
This is a hypothesis and hopefully points you to the root cause; the problem is Message Pumps being filtered in VSTO Office apps.
Could be a red herring as I've never seen
WndProc messages
cause double rendering but I've never seen double rendering before!However, setting the focus problems and/or un-clickable controls made me remember this behaviour.
Originally I came across this wierd issue with one of my Excel Add-Ins: BUG: Cant choose dates on a DatePicker that fall outside a floating VSTO Add-In
Hans Passant identified the root cause:
I've answered a few questions with this information. This QA shows one way to correct the message pump dispatching, eg Excel CustomTaskPane with WebBrowser control - keyboard/focus issues
If this isn't the root cause at least you can cross it off the troubleshooting steps, its an "off the beaten track" diagnostic technique.
If at all possible I'd love an [mcve] to help you fix it.
Edit:
I cannot reproduce it! Its PC SPECIFIC. Try upgrading your Video Driver or try a machine with a different video card. Here are my vid card specs:
Since your addin is running in a hosted environment, there's no help in making changes affecting anything on process level. However, there are Win32 APIs in place to dealing with child windows. A process may have different DPI-awareness contexts amongst it's top-level windows. Available since The Anniversary Update (Windows 10, version 1703).
I haven't tested it myself, so I can only point you in the most relevant direction. "When you want to opt a dialog or an HWND in a dialog out of automatic DPI scaling you can use SetDialogDpiChangeBehavior/SetDialogControlDpiChangeBehavior"
More info here: https://blogs.windows.com/buildingapps/2017/04/04/high-dpi-scaling-improvements-desktop-applications-windows-10-creators-update/#bEKiRLjiB4dZ7ft9.97
It's been quite many years, since I've dwelved in low level win32 dialogs - but I'm quite sure you can use those API's on any window handle without creating an actual dialog. A dialog and a normal window, just differs in the default message loop handler and a few different default window styles, if I remember correctly.
By the looks of it, it seems you use WPF in the addin. DPI awareness and WPF has it's moments for sure. But hosting the WPF inside a elementhost, might give you additional control over the DPI issue. Especially when applying Win32 APIs, and being able to use the window handle of the elementhost and override WIN32 messages it receives.
I hope this is of any help.