CsWin32: IShellWindows.FindWindowSW definition throws NotSupportedException unless `int*` is changed to `out int`
I tracked this down to the int* phwnd
parameter. It works just fine if you edit the generated declaration to declare the parameter as out int
. For example, https://github.com/microsoft/nodejstools/blob/main/Nodejs/Product/Nodejs/SharedProject/SystemUtilities.cs#L26 uses an out int
definition.
Documentation: https://learn.microsoft.com/en-us/windows/win32/api/exdisp/nf-exdisp-ishellwindows-findwindowsw#parameters
Repro steps
NativeMethods.txt
content:
ShellWindows
IShellWindows
CSIDL_DESKTOP
IServiceProvider
ShellWindowTypeConstants
ShellWindowFindWindowOptions
NativeMethods.json
content (if present):
- Any of your own code that should be shared?
var shellWindows = (IShellWindows)new ShellWindows();
int hwnd;
var serviceProvider = (IServiceProvider)shellWindows.FindWindowSW(
PInvoke.CSIDL_DESKTOP,
pvarLocRoot: null,
(int)ShellWindowTypeConstants.SWC_DESKTOP,
&hwnd,
(int)ShellWindowFindWindowOptions.SWFO_NEEDDISPATCH);
Context
- CsWin32 version: [e.g.
0.2.188-beta
] - Win32Metadata version (if explicitly set by project):
- Target Framework: net6.0-windows8
LangVersion
(if explicitly set by project):latest
About this issue
- Original URL
- State: closed
- Created a year ago
- Comments: 15 (15 by maintainers)
Commits related to this issue
- Fix for non-remotable COM interfaces Fixes #860 — committed to microsoft/CsWin32 by AArnott a year ago
- Fix for non-remotable COM interfaces Fixes #860 — committed to microsoft/CsWin32 by AArnott a year ago
- Fix for non-remotable COM interfaces Fixes #860 — committed to microsoft/CsWin32 by AArnott a year ago
I am missing some history here. I am able to reproduce this issue and it is indeed an intentional block and I can understand why for .NET Framework. Let me do some research and see what I can find. This is by-design for .NET Framework due to how Remoting was implemented. For .NET Core, it is possible this is no longer a requirement since we no longer have Remoting. However, I need to root around and see if I can understand “why” this decision was made. COM supports this so I’m not sure why .NET has blocked it.
I have dug around history and I see the issue and it is annoying, but truthfully the current behavior is correct. First things first, this is by-design in both .NET Core and .NET Framework and won’t change.
The details on why are subtle but the gist is an
int*
in C# is ambiguous from the marshaller’s perspective. Is this a pointer value? a pointer to anint
to reference? a pointer to a pinnedint
array? The marshaller just doesn’t know and so a raw pointer type is invalid because of all the things it could be. One can see glimpses of this in theVARIANT
type, which is how parameters are defined in this scenario - notice thatVT_PTR
isn’t permitted, butVT_BYREF
is. In this case, the built-in marshaller for this sort of remote invocation is strictly following the rules for TLBs and raw pointers aren’t permitted.Note that if the interface is used for local COM interop this is marshalled as desired because we aren’t using the dispatch based marshaller. The out of proc nature is the problem here and is expressed as a
RemotingException
on .NET Framework, but a genericNotSupportedException
on .NET Core. More than willing to provide a better message if someone wants to file an issue.@AArnott The solution here is to express the intent using the C# keywords as precisely as possible. Using
out Some
is the preferred approach,[Out]
should be rarely needed.