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.txtcontent:
ShellWindows
IShellWindows
CSIDL_DESKTOP
IServiceProvider
ShellWindowTypeConstants
ShellWindowFindWindowOptions
NativeMethods.jsoncontent (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 anintto reference? a pointer to a pinnedintarray? 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 theVARIANTtype, which is how parameters are defined in this scenario - notice thatVT_PTRisn’t permitted, butVT_BYREFis. 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
RemotingExceptionon .NET Framework, but a genericNotSupportedExceptionon .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 Someis the preferred approach,[Out]should be rarely needed.