microsoft / CsWin32

A source generator to add a user-defined set of Win32 P/Invoke methods and supporting types to a C# project.
MIT License
2.1k stars 90 forks source link

Can not publish project when target runtime is `win-x64` #797

Closed emako closed 1 year ago

emako commented 1 year ago

Actual behavior

[A clear and concise description of what the bug is.]

Can not publish project when target runtime is win-x64, but Portable is OK.

image image

2>'PInvoke' is inaccessible due to its protection level
2>The type or namespace name 'Graphics' does not exist in the namespace 'Windows.Win32' (are you missing an assembly reference?)
2>The type or namespace name 'Foundation' does not exist in the namespace 'Windows.Win32' (are you missing an assembly reference?)
2>The type or namespace name 'UI' does not exist in the namespace 'Windows.Win32' (are you missing an assembly reference?)
2>The type or namespace name 'UI' does not exist in the namespace 'Windows.Win32' (are you missing an assembly reference?)
2>The type or namespace name 'HOT_KEY_MODIFIERS' could not be found (are you missing a using directive or an assembly reference?)
2>The type or namespace name 'HOT_KEY_MODIFIERS' could not be found (are you missing a using directive or an assembly reference?)
2>The type or namespace name 'MOUSE_EVENT_FLAGS' could not be found (are you missing a using directive or an assembly reference?)
2>The type or namespace name 'RECT' could not be found (are you missing a using directive or an assembly reference?)
========== Build: 1 succeeded, 0 failed, 0 up-to-date, 0 skipped ==========
========== Elapsed 00:05.446 ==========
========== Publish: 0 succeeded, 1 failed, 0 skipped ==========
========== Elapsed 00:05.446 ==========

Expected behavior

A clear and concise description of what you expected to happen.

Repro steps

  1. NativeMethods.txt content:

    SetForegroundWindow
    PostMessage
    SendMessage
    IsIconic
    DeleteObject
    StretchBlt
    GetDC
    DeleteDC
    ReleaseDC
    GetDesktopWindow
    GetWindowLong
    SetWindowLong
    BitBlt
    mouse_event
    SetCursorPos
    SetWindowPos
    GetWindowRect
    BringWindowToTop
    SetLayeredWindowAttributes
    RegisterHotKey
    UnregisterHotKey
    GetDeviceCaps
    GetKeyState
    GetAsyncKeyState
    SendInput
    SystemParametersInfo
  2. NativeMethods.json content (if present):

  3. Any of your own code that should be shared?

Context

AArnott commented 1 year ago

What is this publish operation? Is this dotnet publish -r win-x64?

emako commented 1 year ago

I'm not use CLI to publish.

My FolderProfile.pubxml file here, and publish by vs2022

<?xml version="1.0" encoding="utf-8"?>
<!--
https://go.microsoft.com/fwlink/?LinkID=208121.
-->
<Project>
  <PropertyGroup>
    <Configuration>Release</Configuration>
    <Platform>x64</Platform>
    <PublishDir>bin\x64\Release\net6.0-windows10.0.18362.0\publish\win-x64\</PublishDir>
    <PublishProtocol>FileSystem</PublishProtocol>
    <_TargetId>Folder</_TargetId>
    <TargetFramework>net6.0-windows10.0.18362.0</TargetFramework>
    <SelfContained>false</SelfContained>
    <RuntimeIdentifier>win-x64</RuntimeIdentifier>
    <PublishSingleFile>false</PublishSingleFile>
    <PublishReadyToRun>false</PublishReadyToRun>
  </PropertyGroup>
</Project>
emako commented 1 year ago

Under my testing, it will only happened when using Microsoft Visual Studio. This will cause that I cannot generate MSIX packages through Visual Studio. dotnet publish -r win-x64 or dotnet publish -p:PublishProfile=FolderProfile will ok to build the x64 bin.

Microsoft Visual Studio Professional 2022 (64-bit) - Current Version 17.3.2 The newer VS2022 version has too mach bug to use, I using 17.3.2 nowaday.

oold commented 1 year ago

This bug is very annoying and forces me to drop to the command line whenever I'm publishing builds. I can confirm it happens in VS 2019, too.

AArnott commented 1 year ago

I'm not familiar with pubxml files and cannot guess the project type that you're trying to build. But given CLI builds/publish work for you, I daresay the bug isn't in CsWin32, but rather with the Visual Studio project system that you're trying to publish with. The bug should be filed with them. If you don't know how or where to file it, comment again on this issue (even after I close it) with a repro project and I'll find its owners and either forward the bug or tell you where you can file it.

SJC08 commented 10 months ago

WpfApp1.zip This project reproduces the issue. It uses only HWND and cannot be published as win-x64 through FolderProfile.pubxml.

AArnott commented 10 months ago

Thanks. I took a look and found one problem (but there are more): The VS Publish command builds the project twice. The first time works, as it's apparently a standard build. But the second time is the Publish build, and it is doing something funky that breaks CsWin32. In particular, it invokes the compiler without /additionalfile:NativeMethods.txt switch or the /unsafe+ switch. Both of these come from CsWin32's nuget package's msbuild files that are supposed to be imported by the project file, but evidently are not for the publish build.

By adding two elements to the project file, I was able to workaround that deficiency: image

But it turned out there was more than that. The win32metadata package has its own msbuild import which was also being skipped. In the end, I was able to get a successful publish step by copying the whole content of both .props files into the project file itself, and then switch to an absolute path because the $(NuGetPackageRoot) property wasn't set.

    <PropertyGroup>
        <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
    </PropertyGroup>

    <ItemGroup>
        <None Remove="NativeMethods.json" />
        <None Remove="NativeMethods.txt" />
        <AdditionalFiles Include="NativeMethods.json" Condition="Exists('NativeMethods.json')" />
        <AdditionalFiles Include="NativeMethods.txt" Condition="Exists('NativeMethods.txt')" />
    </ItemGroup>

    <ItemGroup>
        <!-- Provide the path to the winmds used as input into the analyzer. -->
        <CompilerVisibleProperty Include="CsWin32InputMetadataPaths" />
        <CompilerVisibleProperty Include="CsWin32InputDocPaths" />
    </ItemGroup>

    <Target Name="AssembleCsWin32InputPaths" BeforeTargets="GenerateMSBuildEditorConfigFileCore">
        <!-- Roslyn only allows source generators to see msbuild properties, to lift msbuild items into semicolon-delimited properties. -->
        <PropertyGroup>
            <CsWin32InputMetadataPaths>@(ProjectionMetadataWinmd->'%(FullPath)','|')</CsWin32InputMetadataPaths>
            <CsWin32InputDocPaths>@(ProjectionDocs->'%(FullPath)','|')</CsWin32InputDocPaths>
        </PropertyGroup>
    </Target>

    <Target Name="FixMds" BeforeTargets="CoreCompile" Condition="'@(ProjectionMetadataWinmd)'==''">
        <Message Importance="high" Text="Adding missing winmd" />
        <ItemGroup>
            <ProjectionMetadataWinmd Include="C:\.tools\.nuget\packages\microsoft.windows.sdk.win32metadata\55.0.45-preview\Windows.Win32.winmd" />
        </ItemGroup>
    </Target>

I think ultimately, the problem is just that: $(NuGetPackageRoot) isn't set in a VS Publish build. That would explain the lack of imports. That needs to be fixed. But that's a VS product problem. Can you use Report a Problem in VS and share the link for what you create here? Feel free to link to that issue from the feedback ticket you create so they can see the research here.

SJC08 commented 10 months ago

Thanks. I took a look and found one problem (but there are more): The VS Publish command builds the project twice. The first time works, as it's apparently a standard build. But the second time is the Publish build, and it is doing something funky that breaks CsWin32. In particular, it invokes the compiler without /additionalfile:NativeMethods.txt switch or the /unsafe+ switch. Both of these come from CsWin32's nuget package's msbuild files that are supposed to be imported by the project file, but evidently are not for the publish build.

By adding two elements to the project file, I was able to workaround that deficiency: image

But it turned out there was more than that. The win32metadata package has its own msbuild import which was also being skipped. In the end, I was able to get a successful publish step by copying the whole content of both .props files into the project file itself, and then switch to an absolute path because the $(NuGetPackageRoot) property wasn't set.

  <PropertyGroup>
      <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>

  <ItemGroup>
      <None Remove="NativeMethods.json" />
      <None Remove="NativeMethods.txt" />
      <AdditionalFiles Include="NativeMethods.json" Condition="Exists('NativeMethods.json')" />
      <AdditionalFiles Include="NativeMethods.txt" Condition="Exists('NativeMethods.txt')" />
  </ItemGroup>

  <ItemGroup>
      <!-- Provide the path to the winmds used as input into the analyzer. -->
      <CompilerVisibleProperty Include="CsWin32InputMetadataPaths" />
      <CompilerVisibleProperty Include="CsWin32InputDocPaths" />
  </ItemGroup>

  <Target Name="AssembleCsWin32InputPaths" BeforeTargets="GenerateMSBuildEditorConfigFileCore">
      <!-- Roslyn only allows source generators to see msbuild properties, to lift msbuild items into semicolon-delimited properties. -->
      <PropertyGroup>
          <CsWin32InputMetadataPaths>@(ProjectionMetadataWinmd->'%(FullPath)','|')</CsWin32InputMetadataPaths>
          <CsWin32InputDocPaths>@(ProjectionDocs->'%(FullPath)','|')</CsWin32InputDocPaths>
      </PropertyGroup>
  </Target>

  <Target Name="FixMds" BeforeTargets="CoreCompile" Condition="'@(ProjectionMetadataWinmd)'==''">
      <Message Importance="high" Text="Adding missing winmd" />
      <ItemGroup>
          <ProjectionMetadataWinmd Include="C:\.tools\.nuget\packages\microsoft.windows.sdk.win32metadata\55.0.45-preview\Windows.Win32.winmd" />
      </ItemGroup>
  </Target>

I think ultimately, the problem is just that: $(NuGetPackageRoot) isn't set in a VS Publish build. That would explain the lack of imports. That needs to be fixed. But that's a VS product problem. Can you use Report a Problem in VS and share the link for what you create here? Feel free to link to that issue from the feedback ticket you create so they can see the research here.

Thank you so much! The solution worked, and I'll also report it to VS soon.