Skip to content

Improve optional out interface arguments (e.g. IWbemServices.GetObject) and other minor tweaks#1544

Merged
jevansaks merged 6 commits into
mainfrom
user/jevansaks/optionaloutinterfaces
Nov 15, 2025
Merged

Improve optional out interface arguments (e.g. IWbemServices.GetObject) and other minor tweaks#1544
jevansaks merged 6 commits into
mainfrom
user/jevansaks/optionaloutinterfaces

Conversation

@jevansaks

@jevansaks jevansaks commented Nov 13, 2025

Copy link
Copy Markdown
Member

One reason that parameters were still showing up as pointers in the friendly overloads was because of [optional, out] parameters. For example, IWbemServices.GetObject has two out parameters which are optional and are also interfaces. As we've seen with other optional out/ref parameters, we have to leave these as pointers in the ABI so that they can be passed as "null". #1511 improved things for optional out/ref types that are trivially marshallable, but a case that is particularly unpleasant to deal with as pointers is COM interfaces.

This change improves things so that on the friendly overload that includes optional parameters, the out/ref interfaces are upgraded to managed interfaces and the implementation handles the COM marshalling both for source-generated COM and built-in COM. The helpers also handle Unsafe.NullRef<T> being passed in for the optional parameters in case you want to omit some, same as you can do with the upgraded optional value type params.

I added a test case that demonstrates calling IWbemServices.GetObject which is much simpler than the unmanaged alternative.

I also fixed a couple other minor things:

  • Added the GeneratedCode attribute to PCWSTR and friends
  • NativeOverlapped should always be a pointer even when not [Retained]
  • VARIANT_unmanaged in signatures should be ComVariant

Here's what the generated code looks like (for a function that has just one optional out parameter of this kind) if you don't want to build locally to take a look:

internal static unsafe void DeleteClass(this winmdroot.System.Wmi.IWbemServices @this, SafeHandle strClass, winmdroot.System.Wmi.WBEM_GENERIC_FLAG_TYPE lFlags, winmdroot.System.Wmi.IWbemContext pCtx, ref winmdroot.System.Wmi.IWbemCallResult ppCallResult)
{
	bool strClassAddRef = false;
	winmdroot.System.Wmi.IWbemCallResult_unmanaged* __ppCallResult_native = null;
	bool __ppCallResult_present = !Unsafe.IsNullRef<winmdroot.System.Wmi.IWbemCallResult>(ref ppCallResult);
	try
	{
		winmdroot.Foundation.BSTR strClassLocal;
		if (strClass is object)
		{
			strClass.DangerousAddRef(ref strClassAddRef);
			strClassLocal = (winmdroot.Foundation.BSTR)strClass.DangerousGetHandle();
		}
		else
			throw new ArgumentNullException(nameof(strClass));
		if (__ppCallResult_present)
		{
			__ppCallResult_native = (winmdroot.System.Wmi.IWbemCallResult_unmanaged*)global::System.Runtime.InteropServices.Marshalling.ComInterfaceMarshaller<winmdroot.System.Wmi.IWbemCallResult>.ConvertToUnmanaged(ppCallResult);
		}
		@this.DeleteClass(strClassLocal, lFlags, pCtx, __ppCallResult_present ? &__ppCallResult_native : null);
		if (__ppCallResult_present)
		{
			ppCallResult = global::System.Runtime.InteropServices.Marshalling.ComInterfaceMarshaller<winmdroot.System.Wmi.IWbemCallResult>.ConvertToManaged(__ppCallResult_native);
		}
	}
	finally
	{
		if (strClassAddRef)
			strClass.DangerousRelease();
		global::System.Runtime.InteropServices.Marshalling.ComInterfaceMarshaller<winmdroot.System.Wmi.IWbemCallResult>.Free(__ppCallResult_native);
	}
}

Fixes #1066, Fixes #1528, Fixes #1216, Fixes #1185, Fixes #1066, Fixes #137

Add generated code attribute to PCWSTR

Add test case that exercises optional out

Fix failure that happens sometimes from GetAllWindows test running in parallel with tests that create windows

Add built-in com generation for optional out parameters
@jevansaks jevansaks requested a review from manodasanW November 14, 2025 17:13
Comment thread test/GenerationSandbox.Tests/BasicTests.cs
@jevansaks jevansaks enabled auto-merge (squash) November 15, 2025 02:15
@jevansaks jevansaks merged commit fe382a4 into main Nov 15, 2025
7 checks passed
@jevansaks jevansaks deleted the user/jevansaks/optionaloutinterfaces branch November 15, 2025 02:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants