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

image

Repro steps

  1. NativeMethods.txt content:
ShellWindows
IShellWindows
CSIDL_DESKTOP
IServiceProvider
ShellWindowTypeConstants
ShellWindowFindWindowOptions
  1. NativeMethods.json content (if present):
  1. 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

Most upvoted comments

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 an int to reference? a pointer to a pinned int 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 the VARIANT type, which is how parameters are defined in this scenario - notice that VT_PTR isn’t permitted, but VT_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 generic NotSupportedException 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.