1

gurus, need your assistance, i'm stumped!

INTRO
I have a line of components, that are developed in C# .NET 4.0, comprising a big product.
The list includes Windows services, IIS web-applications, stand-alone executables, WPF executables launched via ClickOnce..

All components are distributed via MSI deployment packages and installed by a special (explicitly made for this purpose) stand-alone AppSuite executable, also written in C#. At appropriate moment it launches msiexec.exe, passing path to the .msi package and all other necessary arguments (e.g. flags suppressing default MSI UI dialogs, enabling verbose logging, etc.).
Once the files are put in place by MSI (and some other things, e.g. IIS tasks, are done), it calls a CustomAction from the component's main assembly, which launches a configuration dialog where fine-tuning of the install happens (e.g. setting DB connection strings, specifying IP addresses and ports, and so on).

Here's how this process looks regularly: enter image description here

To make things simpler (since a lot of configurational settings are same from one to the next) most components use the same dlgConfig from a shared class library assembly. It provides methods for adjustment: hiding or showing certain controls, which are not used by all components. These methods are called by a CustomAction during its initialization prior to showing the dialog.

There are a few components which have explicitly different configurational settings, so they provide their own dlgConfigs, which are subclassed from the shared one and initialized appropriately.

ISSUE
On occasion we notice a very weird look-n-feel behavior of dlgConfig on Windows 10: it shows up chopped off at the bottom, hiding the [OK] and [Cancel] buttons (and possibly some other controls). Buttons are present and accessible by [Tab]-sequence or [Enter]-key, but not in view: enter image description here

Once i got to see the entire install sequence i noticed that this only happens in the shared dialog! All the subclassed ones show up in proper height. To confirm i added a new "transparent" (i.e. not providing any new functionality) subclassed dialog to a component originally using the shared one; run the install and .. yes it showed up with the buttons. Huh???

Upon closer examination i noticed that in all configuration dialogs (shared or subclassed) text fields have diminished heights. That results in visible difference in height when there's a "look-up" button right next to the textbox.
I made a test WinForms executable that would simply launch the shared dlgConfig and compared it to the one from the installation. Yet one more mystery is here. Every component has a DB connection, therefore to modify conn-string i made a single dlgSqlConn, which is located in the same shared assembly where the root dlgConfig is. And that dialog opens with all its fields and buttons in full view no matter what component is being installed!! Its appearance (fonts and control heights) is also affected in similar fashion, but at least there's no chopping off: enter image description here

Very long time ago i set my personal preference in font selection for UI design to Tahoma. I even enforce that selection in code in every form/dialog's constructor:

this.Font=  new System.Drawing.Font( Utils.scFntTahoma, 8.25F );

Looking at printscreens i cannot say "oh that is not Tahoma!", but a slight difference in font sizes is fairly visible (offensive versions being just a tad bigger)..
Which goes against logic, 'cause the offensive textboxes have smaller height! Huh???

Dialogs are using AutoScaleMode set to Font (it is default, and it is reasonable).
I tried all other choices. Dpi was the worst: it shrinked the dialog with all controls proportionally, but had no impact on its height still being chopped off. Inherit and None have no effect.

I added the following 2 lines from usual WinForms initialization to the start of CustomAction installer class, but it also had no effect:

Application.EnableVisualStyles( );
Application.SetCompatibleTextRenderingDefault( false );

Further, when we set display scale to 150%, AppSuite shows its UI with slightly fuzzy font (expected). Whereas these configuration dialogs look very sharp at that point. After resetting display scale to 100% we see AppSuite sharp, but dlgConfigs are now fuzzy.

Another related issue: for one component a label that in designer takes almost entire width of the dialog (with 8px margins) showed up stretched horizontally hiding a lot of content: enter image description here

This led me to think about user context under which different processes run:

  1. AppSuite is launched by the current Windows user.
  2. It starts initial msiexec.exe with command-line arguments - under current user.
  3. msiexec.exe communicates with Windows Installer service, running under LocalSystem,
  4. Which launches 2 more instances of msiexec.exe, one under current user, another under LocalSystem.
  5. That last one (running as %machine%\SYSTEM) is calling the CustomAction in the deployment assembly and eventually opening the dialog.

And one more finding: MessageBoxes produced from processes, running under current user (Test) and %machine%\SYSTEM (installed component) show distinctly different - one is fuzzy, other is not: enter image description here

Well, my code has absolutely no control over MessageBox's font!!


So, it boils down to: somehow font selection for a new window is depending on user account which the process is run under (even when font is explicitly specified and exists)! Why? How?
Why does that affect only vertical dimension - all controls [except 1 label.. ??] retain their widths?
Can i fix it? If yes - how do i fix it?
I don't believe that behavior has anything to do with C# code (dialogs work and look perfectly on most machines, including many Win-10s, and there are only few flukes)

We could live with textbox heights being squished and fonts being slightly off, but chopping off the height of the dialog .. only when it is a shared one?? WFT's going on?!?!

Maximilian Burszley
  • 18,243
  • 4
  • 34
  • 63
Astrogator
  • 1,041
  • 11
  • 27
  • This question is WAY too broad to be helped. Maybe it's time to leave the legacy behind and move away from winforms and .net 4.0. – Maximilian Burszley Aug 23 '19 at 18:57
  • DpiAwareness status (non-DPIAware, apparently, since `AutoScaleMode = Font` )? Are you mixing WPF assemblies (PresentationCore, PresentationFramework, WindowsBase etc) with WinForm's? – Jimi Aug 23 '19 at 20:05
  • @TheIncorrigible1, IMHO the question is fairly straightforward and specific. It does have a detailed description, which is necessary to explain all the intricacies and illustrate what happens. As for what you called "legacy", I bet observed behavior is independent of versions of used tools (as MessageBox fonts indicate). I even promise to compile an .msi in VS2017 to see if that would make any difference. – Astrogator Aug 23 '19 at 20:37
  • @Jimi, no during the installation all exposed UI elements are of WinForms origin. – Astrogator Aug 23 '19 at 20:38
  • Hmm. In the images you're showing, clearly some of the Windows have been virtualized. Some have not. It doesn't matter if all components you use are meant for WinForm. If a single dependency is from Presentation Framework, you'll have mixed-mode (some Windows with a DpiAware aspect, some not). See the notes here: [DPI Awareness - Unaware in one Release, System Aware in the Other](https://stackoverflow.com/a/50276714/7444103). My suggestion is to turn the whole thing DPIAware. – Jimi Aug 23 '19 at 22:08
  • (Yes, I do understand it'ld take more than a couple of hours :) – Jimi Aug 23 '19 at 22:20
  • @Jimi, i was not even aware of 'DPI Awareness' :). After reading up all morning i feel the answer is indeed somewhere near. Still.. 1) i'd expect DPI-related resizing should affect *both dimensions, not only heights* 2) Dialog chop-off only happens for the original class from shared DLL; every subclassed one (from same DLL as CustomActions code) retains the height 3) So far we use VS2010 to build everything, why would Win10 think the results are DPI-aware? I'd be happy to keep everything DPI-unaware and have bitmap-upscaling with fuzzy font, as seen with AppSuit.exe! (more to follow..) – Astrogator Aug 26 '19 at 17:03
  • See the link in my previous comment. The guys there wanted the same things as you: prevent the System from automatically activating DPI Awareness on some ot the processes. – Jimi Aug 26 '19 at 17:10
  • Yeah, i saw. Stumped, as i can't find anything that could change behavior from 'Unaware' *which should be default*! (( As expected, a build in VS2017 had no effect. Setting `AutoScroll` added a scrollbar, but buttons were still lower than bottom border. Checking `AutoSize`. If i can simply modify dialog dimensions so that buttons do not get hidden, i would not care about fonts. I'd like to retain existing look-n-feel for unaffected Windows, though this may be futile.. – Astrogator Aug 26 '19 at 18:10
  • Unaware is the default in WinForms (but the opposite in WPF). In Windows 10 (also because of some recent-ish updates), the System doesn't necessarily comply. You have to set the DPI Awareness of your app explicitly to `unaware` and explicitly disable the `SystemAware` switch. Follow the directions in that post, to the end of the first part. You should also visit the duplicate marked by Hans Passant. – Jimi Aug 26 '19 at 18:19
  • @Jimi, thank you for guidance! Does that mean i need to add `app.manifest` file - i don't have any yet (there was no need so far)? And if so, where to stick it - into the project being installed? There are some projects that deploy WPF executables (more technically, ClickOnce deployments for these on the server, as they will run on other computers), but observed correlation for the issue is between the `dlgConfig` being from shared DLL, not whether project has WPF. – Astrogator Aug 26 '19 at 19:24
  • The thing is: the problem happens not in any app, but during their installation. So the entry point is the `ModuleInstaller : System.Configuration.Install.Installer` class, that at certain point during `override void Install( IDictionary stateSaver )` opens `dlgConfig` or a subclassed variant of it. And the issue is happening only in the first case. – Astrogator Aug 26 '19 at 19:28
  • Yes, this is also a ClickOnce issue. Or, it is if the framework is < 4.7.2. Search for `ClickOnce` / `DpiAware`. You'll find out. – Jimi Aug 26 '19 at 19:38

1 Answers1

0

@Jimi, thank you very much for your comments which at least pointed me in the right direction!

Well, in my case there are no WPF dependencies during the time CustomActions run while installing. Our ClickOnce deployments are simply creating web-apps/ virtual-dirs on IIS server (not on eventual end-client computers), and just put files there (same as a regular WebDeploymentProjects do), so in that regard there's no WPF involvement either.

I did not want to simply make my code 'DPI Aware', primarily because it is not (and actually implementing that would be too much effort). Plus, i don't know what would i mark as 'aware' (or 'unaware') in a CustomAction.. But the hunch i had about user context led me to start logging all kinds of info about the environment while installing. Surprisingly it is quite different from what a regular process sees, which brings up a lot of questions..

Main process AppSuite.exe (under MACHINE\CURRENT_USER, who is an Administrator):

Environment.OSVersion.VersionString:    Windows 10 Professional 10.0.17763.0
System.Drawing.SystemFonts
            {Name}, {SizeInPoints} [{Height}] '{SystemFontName}' [{OriginalFontName}]
Default:    Tahoma, 8.25   [14] 'DefaultFont' []
Dialog:     Tahoma, 8      [13] 'DialogFont' [MS Shell Dlg 2]
MsgBox:     Segoe UI, 10.5 [19] 'MessageBoxFont' []
IconTtl:    Segoe UI, 10.5 [19] 'IconTitleFont' []
Status:     Segoe UI, 10.5 [19] 'StatusFont' []

Indirectly launched msiexec.exe, running CustomAction (under NT_AUTHORITY\SYSTEM):

Environment.OSVersion.VersionString:    Microsoft Windows NT 6.3.9600.0
System.Drawing.SystemFonts
            {Name}, {SizeInPoints} [{Height}] '{SystemFontName}' [{OriginalFontName}]
Default:    Tahoma, 8      [20] 'DefaultFont' []
Dialog:     Tahoma, 8      [20] 'DialogFont' [MS Shell Dlg 2]
MsgBox:     Segoe UI, 10.5 [28] 'MessageBoxFont' []
IconTtl:    Segoe UI, 10.5 [28] 'IconTitleFont' []
Status:     Segoe UI, 10.5 [28] 'StatusFont' []

As you can see, fonts are [except 8.25 vs. 8] same, but the Height property jumps in value quite a bit!

The culprit to the shared dialog showing up chopped off turned out to be .. ControlBox set to false (it was hidden specifically to enforce clicking [OK] or [Cancel] buttons for proceeding with install or aborting). As soon as i reenabled ControlBox, dlgConfig showed up with buttons visible (i had to add new code now preventing dialog closure if it was not a result of either of these buttons): enter image description here Weird: subclassed dialogs did not override that property, but still showed up properly.. Can't explain.

Textbox fonts are still appearing smaller in height than they should be, but now that all fields are visible, at least configuration UI becomes usable.

As a guy from a post related to my previous problem says, "Somebody at Microsoft has to die for this". Seriously, Microsoft, stop messing things up!!

Astrogator
  • 1,041
  • 11
  • 27