Hello @Gavin Williams ,
Thank you for the clear description and the screenshot. This is expected behavior rather than a name collision: CsWin32 runs independently in each project, so both your shared project and the dependent project each generate their own Windows.Win32.PInvoke class. Once the shared project exposes that class (for example as public, or via InternalsVisibleTo), the dependent project's compilation sees two definitions of the same type, its own generated one plus the imported one, which is exactly what CS0436 reports. As you noted, a fully qualified name cannot help, because it really is the same type generated twice.
The recommended fix is CsWin32's layered composition feature, where one assembly owns the canonical PInvoke class and the other extends it instead of redeclaring it, so callers still reach everything through a single PInvoke.X(). In the shared (lower) project, mark it as the owner in NativeMethods.json:
{
"$schema": "https://aka.ms/CsWin32.schema.json",
"className": "PInvoke",
"public": true
}
And in the dependent project, make it an extender with a unique className:
{
"$schema": "https://aka.ms/CsWin32.schema.json",
"className": "PInvokeApp",
"extensionReceiver": "PInvoke"
}
This relies on C# 14 extension members, so every consuming project needs LangVersion 14 or later (automatic when targeting .NET 10 or later, otherwise set <LangVersion>14</LangVersion>), and CsWin32 will detect and skip duplicate APIs across the layers for you.
If C# 14 is not an option in your setup, you can instead generate each API only once by declaring the P/Invokes in the shared project with "public": true and not listing those same APIs again in the dependent project or give each project a distinct className (such as CorePInvoke and AppPInvoke) so the classes no longer collide, at the cost of losing the single unified PInvoke surface. One thing to watch is that if you have handwritten partial definitions for foundation types like HRESULT or PWSTR, promoting the shared project to public can surface a similar CS0436 on those, which the guide resolves by moving the extra members into an extension(T) block.
The full worked example is here.
To tailor the guidance, it would help to know your CsWin32 package version, the target framework and effective LangVersion, and which project sits at the bottom of your dependency graph. If you found my response helpful or informative, I would greatly appreciate it if you could follow this guide for your confirmation.
Thank you.