Skip to content

UseWindowsForms='true' with OutputType='Exe' produces GUI instead of CUI executable  #13331

@StevenBonePgh

Description

@StevenBonePgh

In the old .csproj format, setting <OutputType>Exe</OutputType> produced an executable that is a console application, even if WinForms was used in the application. Similarly, <OutputType>WinExe</OutputType> always produced a windows GUI application. There are times when a console application is desired, though I'd agree not most times.

This old behavior can be easily confirmed by creating a legacy WinForms project, compiling it with both OutputTypes and running dumpbin /headers MyApp.exe and looking in the output for subsystem. The results are 3 subsystem (Windows CUI) for Exe and 2 subsystem (Windows GUI) for WinExe. Pay attention of the C vs G in that output - it is easy to miss.

After converting to the new SDK format csproj for multitargeting support (as we all want to move to .NET 5.0 but it does not happen overnight), this behavior is broken in the full framework (net48) project - but only if the 'WindowsDesktop' is used and/or 'UseWindowsForms' is true. I'd consider this a regression.

The following project will generate, always, the equal of OutputType == WinExe regardless of what is specified . This is easily confirmed by dumpbin on the output of compilation with both settings.

<Project Sdk="Microsoft.NET.Sdk.WindowsDesktop">
  <PropertyGroup>
   <TargetFrameworks>net48;netcoreapp5.0</TargetFrameworks>
   <UseWindowsForms>true</UseWindowsForms>
   <OutputType>Exe</OutputType>
  </PropertyGroup>
</Project>

The following project, without WindowsDesktop and/or UseWindowsForms the OutputType is respected, and will produce an executable with the expected subsystem. Confirmation achieved with the above process.

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
   <TargetFrameworks>net48;netcoreapp5.0</TargetFrameworks>
   <OutputType>Exe</OutputType>
  </PropertyGroup>
</Project>

If your application does use WinForms, adjusting the project to look like the latter example results in failed compilation., as one would expect.

The only workaround I have been able to discover is not a reasonable one. A post-build step to run editbin /subsystem:console MyApp.exe to twiddle the appropriate bits in the PXE header will make the compiled output behave as desired. To fully/properly integrate this type of workaround in the build pipeline would require something along the lines of Kirill Osenkov's LargeAddressAware package.

Is there a better workaround to restore the previous, and desired, behavior, making the specified OutputType consistent and predictable regardless of what SDK or UseXYZ was also in the project?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions