dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.18k stars 4.72k forks source link

Missing ref modifier in COM method call does not throw compilation error but results in empty output parameter #109214

Open IgrekEncy opened 21 hours ago

IgrekEncy commented 21 hours ago

Description

When calling a COM method that has a ref parameter in its signature, omitting ref in the C# method call does not produce a compilation error. However, as a result, the output parameter is not populated correctly. Adding ref in the method call resolves the issue, but the absence of a compilation error can lead to unexpected runtime behavior.

Reproduction Steps

I have an IDL interface:

  interface (IIpcExtensionManager, 5f900000-ec6f-4c54-a1bf-6c0b42a78c00) : IUnknown
  begin
    HRESULT ReloadStorage([in, out] struct TExecuteContext* ExecuteContext);
  end;

After converting it to DLL, using TlbImp.exe, I have:

  [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
  [ComImport]
  public interface IIpcExtensionManager
  {
    [MethodImpl(MethodImplOptions.InternalCall, MethodCodeType = MethodCodeType.Runtime)]
    void ReloadStorage([In, Out] ref TExecuteContext ExecuteContext);
  }

And I write .NET application, which calls:

extensionManager.ReloadStorage(TStorageType.stCurrentUser, executeContext);

I understand, that compiler does not make any warning (I did not use ref executeContext), but I expect executeContext will not be empty. I fill it inside calling DLL, but here returns empty structure. If I write

extensionManager.ReloadStorage(TStorageType.stCurrentUser, ref executeContext);

I will see me text in executeContext, so my DLL works correctly.

Expected behavior

  1. Compiler warning - should use "ref"
  2. Filling structure executeContext Any of this variants will fix the issue, I guess

Actual behavior

No compilation error is produced, leading to runtime behavior where the executeContext remains unmodified if ref is omitted.

Regression?

No response

Known Workarounds

Explicitly adding ref to the method call resolves the issue and ensures executeContext is populated correctly.

Configuration

No response

Other information

No response

KalleOlaviNiemitalo commented 18 hours ago

This is by design. Some COM interfaces (IIRC especially in Microsoft Office and VB6) are defined such that their methods take reference parameters, even though they won't modify the arguments. To make those easier to call from C#, the compiler detects the ComImportAttribute and passes a reference to a temporary copy if the call doesn't have ref.

KalleOlaviNiemitalo commented 18 hours ago

This extension should be defined in the conformance document of the C# compiler.

A conforming implementation of C# shall be accompanied by a document that defines all implementation-defined characteristics, and all extensions.

dotnet-policy-service[bot] commented 13 hours ago

Tagging subscribers to this area: @dotnet/interop-contrib See info in area-owners.md if you want to be subscribed.

AaronRobinsonMSFT commented 13 hours ago

This is really more of a Roslyn issue than anything in the runtime side of things. If we ever get capactiy to rewrite TlbImp.exe to target .NET Core, then it is possibly something we can help fix from the UX perspective.

/cc @jaredpar

dotnet-policy-service[bot] commented 13 hours ago

Tagging subscribers to this area: @dotnet/interop-contrib See info in area-owners.md if you want to be subscribed.

jaredpar commented 13 hours ago

Compiler warning - should use "ref"

This behavior is By Design. When calling methods on COM objects the compiler allows the callsite to omit the ref modifier. This behavior was introduced in C# 4.0 to make COM programming easier.