4

What's going on:

I maintain a few small .NET applications for a local business. We've got one pair of apps that work in tandem: a "client manager" (C# / .NET 4.? / winforms) and a "label printer" (VB / .NET 2.0 / winforms).

I recently added a "Print Labels" button to the client manager. This opens the label printer & pre-populates the client's name from the client manager. Woohoo!

Unfortunately, only when the label printer is opened this way, dates print out in "dd/MM/yyyy" format, instead of "MM/dd/yyyy".

What I know:

  • Date is entered via a DateTime winforms input in the label printer that defaults to "Today".
  • We see the formatting bug whether the default date is left or is manually changed.
  • We don't send the date over from the client manager.
  • We use ".ToShortDateString" in the label printer app.
  • This never happened when opening the label printer by double-clicking the shortcut/EXE.
  • This only happens on our Windows 7 Panasonic Toughbook.
  • The date is wrong whether I print to our Dymo label printer or to PDF. (Thanks, @jdweng!)
  • Per the Task Manager, the label printer runs as the only local user no matter how I load it. (Thanks, @Polyfun!)
  • The label printer's CurrentCulture and CurrentUICulture are both en-US, no matter how I load it. (Thanks, @Jimi!)
  • The user profile uses M/d/yyyy via "Control Panel > Region". (Thanks, @Hans Passant!)

Relevant Code

Here's the C# code I'm using in the client manager to open the label printer app (comments added for clarity):

 private void btnLabels_Click(object sender, EventArgs e)
 {
    // Set via a hybrid string/file input in the app's "Options" menu.
    string labelAppLocation = Properties.Settings.Default.LabelAppLocation;

    if (String.IsNullOrEmpty(labelAppLocation))
    {
      MessageBox.Show("We're not sure where your label printer app is located! Set this in \"Options >> Manage Lists >> Files\" and try again.");
    }
    else
    {
      Process p = new Process();
      ProcessStartInfo psi = new ProcessStartInfo();
      psi.FileName = labelAppLocation;

      // This class has some formatting helpers for the 
      // client's name and other demographics we'd like to send later. 
      LabelArgs newLabelArgs = new LabelArgs();
      newLabelArgs.Name = this.clientName.Text;
      psi.Arguments = newLabelArgs.ToString();

      p.StartInfo = psi;
      p.Start();
    }
  }

And here's the VB code in the label printer where the date value gets added to the label:

Private Sub DrawItemLabel(ByVal e As System.Drawing.Printing.PrintPageEventArgs)
     Dim g As Graphics = e.Graphics
     'Custom font handler
     Dim fonts As New ItemLabelFonts 

     '...set fonts, set other line items...

     ' `myDate` comes directly from a DateTime input's `.Value`.
     Dim strMidLine As String = myDate.ToShortDateString & "   " & myClientID & "   " & myCounty
     fonts.MiddleFont = ChooseMaxFontForWidth(strMidLine, fonts.MiddleFont, maxWidth, g)

      '...do some math for spacing before drawing the label...

       g.DrawString(myClientName, fonts.BottomFont, Brushes.Black, 0, bottomTop)
  End Sub

What I think:

  • Maybe Process#Start() is losing the current culture and falling back to a dd/MM/yyyy default somewhere? (Confirmed not from user's "Region" settings)
  • Maybe .NET 4.x & .NET 2.0 aren't playing nicely?
  • Maybe the label printer behaves differently when passed arguments and I need to add a user profile or region through those? There's no logic in the form's Load hook to account for this.

What I've tried:

  • Checking the DateTime input's settings for a region fallback.
  • Changing my user's regional settings to see if I can "reset" the formatting.
  • Duplicating the behavior in Windows 10 & Windows XP (with no luck!)
  • Turning it off & back on again.
  • Poring over MSDN.

Why could opening an app via new Process().Start(); change its date formatting?

Alex
  • 1,434
  • 10
  • 17
  • In Task Manager, Details, make sure the Username column is displayed (right click the column headers and Select columns). Is the label printer running as the expected username? – Polyfun Jan 02 '20 at 17:33
  • 2
    No, the Process class does not have such powers. Hard to come up with another theory, nothing else to look at. The usual cause for unexpected formatting is the user overrides in Control Panel > Region. – Hans Passant Jan 02 '20 at 18:03
  • @jdweng I see the same results when I print the labels to PDF instead of the physical printer too, so I don't think that's the issue. Thanks for the other suggestions! I'll check those out ASAP. – Alex Jan 02 '20 at 18:13
  • Aren't you passing the Arguments already formatted (`newLabelArgs.ToString();`)? So, you're passing an already formatted DateTime (`ToString()` override?). Check/Print/Log the `Thread.CurrentThread.CurrentCulture`/`CurrentUICulture` values in `LabelArgs` when `ToString()` is hit. If it's the other program that formats `DateTime.Today` instead (so, what you do in the first app is not relevant), show that code or perform the check there. – Jimi Jan 02 '20 at 18:36
  • @Jimi We don't pass the date via the arguments - it comes directly from the native Winforms datepicker field in the label printer. I've updated the question to clarify & added the VB code that prints the date out. I was remembering incorrectly - we use an explicit `dd/mm/yyyy` formatter instead of the shorthand `d`. I'll check out those `*Culture` values in just a bit - thank you! – Alex Jan 02 '20 at 18:47
  • I don't understand your question. If you are specifying `"dd'/'MM'/'yyyy"` explicitly, why would you think it would ever print out in `MM/dd/yyyy` format? – Matt Johnson-Pint Jan 02 '20 at 18:50
  • @MattJohnson-Pint Whoops, thanks for catching that! I grabbed code I was testing with by accident. Production code updated! – Alex Jan 02 '20 at 18:56
  • No problem. Ok, I see in your edit that the code path you are interested in is calling `ToShortDateString`, which would indeed pick up the current culture. You could change that, of course. Are you still thinking that the culture is changing via the process start? – Matt Johnson-Pint Jan 02 '20 at 19:00
  • 2
    Have you checked the [control panel user region short date settings](https://support.office.com/en-us/article/change-the-windows-regional-settings-to-modify-the-appearance-of-some-data-types-edf41006-f6e2-4360-bc1b-30e9e8a54989) as per Hans's suggestion? – Matt Johnson-Pint Jan 02 '20 at 19:03
  • Have you considered just sending the date as a timestamp instead? No formatting necessary, just a Unix timestamp or something of that nature. – Charleh Jan 02 '20 at 19:06
  • 1
    My guess is that the code of `LabelArgs.ToString()` holds the answer. Checking the command-line that the label printer app is started with on the "working" PCs versus the "non-working" PC, is also likely to prove illuminating. – Ian Kemp Jan 02 '20 at 20:11
  • I'm back at the machine in question and following up on these suggestions now! I've edited the question to clarify this, but just reiterating: we do not pass the date between the apps. I only included that info because this only happens when the label printer is opened from the case manager, so that has something to do with the issue. – Alex Jan 03 '20 at 04:15
  • When you say "runs as the "Admin" user", do you mean that the apps are run with elevated privileges ("run as Administrator", basically), or that the user profiles are admin users? If it's former, you need to verify the culture settings on the Administrator account (sounds like you have already). – Tieson T. Jan 03 '20 at 05:06
  • @TiesonT. "Admin" is the only user on our system, so that's referring to the currently-active user name, which also happens to be an administrator. :) No specially-elevated privileges or "Run As Administrator" is being used, though. Thanks for clarifying! – Alex Jan 03 '20 at 20:32
  • Does the client manager directly call the label printer's print method (what you show above), or does it open the label printer app with values pre-selected (so you still have to hit a print button of some sort)? – Tieson T. Jan 03 '20 at 23:36
  • The client manager just opens the label printer app & fills in the "Name" field - that "DrawItemLabel" method doesn't get called until we hit the "Print" button in the label printer. – Alex Jan 04 '20 at 03:39
  • You can always force the correct language in the VB code `Thread.CurrentThread.CurrentCulture = New CultureInfo("en-US") Thread.CurrentThread.CurrentUICulture = New CultureInfo("en-US")` – VDWWD Jan 06 '20 at 12:08

2 Answers2

7

Success! I retargeted the label printer app from .NET 2.0 to .NET 4.0 (matching the client manager app), cleared out new warnings, and rebuilt the app. Running the v4.0 version of the label printer fixes the issue with no obvious side effects.

I should have done this sooner - I expected going from 2.0 to 4.0 to be a big task, but there were no build errors and the only warnings were implicit conversions. None of these affected Date variables, so I'm still unclear why the 2.0 version acted so strangely, but I'm glad to put this bug to bed. I don't intend to write any new .NET 2.0 projects anytime soon!

Alex
  • 1,434
  • 10
  • 17
  • It's possible that Process.Start runs the executable in the host's .NET Framework version, but if so, it doesn't seem like that is documented anywhere (it's one of the things I tried looking up). Could try something like https://stackoverflow.com/questions/3704449/net-what-version-of-the-framework-am-i-currently-running-in-from-c if you really wanted to know, I suppose. – Tieson T. Jan 09 '20 at 20:54
  • @TiesonT. that's a fun thought! I'll see if I can explore that this weekend - I'd still love to understand exactly why such a weird subtle thing was happening. Thank you! – Alex Jan 10 '20 at 03:11
  • Yeah - that was the intent behind adding a bounty to your question; I wasn't finding anything that would indicate why a .NET executable would behave differently when run directly versus PInvoke or similar, and I was hoping one of the higher-rep C# users would chime in. – Tieson T. Jan 10 '20 at 03:48
0

I think we're missing something for the Region/Date/Timezone format - somewhere hidden.

I've got a Powershell script to set the date to Australian. Here is the code and XML: https://github.com/averkinderen/Azure/blob/master/101-ServerBuild/AURegion.xml

#Set Windows regional format (date/time etc.) to Australia - this applies to all users

# Set Locale, language etc.
& $env:SystemRoot\System32\control.exe "intl.cpl,,/f:`"C:\temp\AURegion.xml`""

# Set Timezone
& tzutil /s "AUS Eastern Standard Time"

# Set languages/culture
Set-Culture en-AU 

$currentlist = Get-WinUserLanguageList
Write-Host "new lang" $currentlist.LanguageTag

Then I searched for an USA version and I found it here: https://poorerleno.blogspot.com/2018/07/remote-patching-1.html?_sm_au_=i7VM8snvHjkLnPvjBJ3vvK7RJCBJt

#Set Windows regional format (date/time etc.) to American - this applies to all users

if (!(test-path c:\temp)) {
New-Item c:\temp -ItemType Directory
} 

[xml]$XmlDocument = invoke-webrequest -Uri https://raw.githubusercontent.com/poorleno1/systemlocale/master/USRegion.xml -UseBasicParsing | Select-Object -ExpandProperty content

$XmlDocument.Save("c:\temp\USRegion.xml")

# Set Locale, language etc.
& $env:SystemRoot\System32\control.exe "intl.cpl,,/f:`"C:\temp\USRegion.xml`""

# Set Timezone
& tzutil /s "Central European Standard Time"

# Set languages/culture
Set-Culture en-US

$currentlist = Get-WinUserLanguageList
Write-Host "new lang" $currentlist.LanguageTag
Jeremy Thompson
  • 61,933
  • 36
  • 195
  • 321
  • Thanks @Jeremy Thompson! This looks like it will need some adjustment to match what we'd like (ex: "Central European Standard Time" => "Eastern Standard Time") but I'll test it out as soon as I'm back at the laptop in question. – Alex Jan 06 '20 at 22:30
  • Wow, I should probably correct the author, its point 8 in the ref link. – Jeremy Thompson Jan 07 '20 at 01:22
  • I was able to test this out just a bit ago and it unfortunately did not solve the problem. Thanks for trying though! – Alex Jan 07 '20 at 04:07