microsoft / microsoft/CsWin32

Useless unmanaged warpers generated when has delegate pointers

Open
#1,629 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
C#
Stars
2.5k
Forks
124
Avg merge
1d 3h
Merged PRs (30d)
9

Description

Actual behavior

When a type has delegate pointer, it will add a unmanaged suffix in the name.

WinMD:

[StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe delegate JNI_ERROR InputJavaVM([In] JavaVM* vm);

[StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe delegate JNI_ERROR InputJavaVMWithArgs([In] JavaVM* vm, [Out] JNIEnv* penv, [Optional][In] JavaVMAttachArgs* args);

[StructLayout(LayoutKind.Auto, CharSet = CharSet.Auto)]
[UnmanagedFunctionPointer(CallingConvention.Winapi)]
public unsafe delegate int InputJavaVMWithVersion([In] JavaVM* vm, [Out] JNIEnv* penv, [In] JNI_VERSION version);

public struct JNIInvokeInterface
{
    public unsafe void* reserved0;
    public unsafe void* reserved1;
    public unsafe void* reserved2;

    public InputJavaVM DestroyJavaVM;
    public InputJavaVMWithArgs AttachCurrentThread;
    public InputJavaVM DetachCurrentThread;
    public InputJavaVMWithVersion GetEnv;
    public InputJavaVMWithArgs AttachCurrentThreadAsDaemon;
}

Generated:

[global::System.CodeDom.Compiler.GeneratedCode("Microsoft.Windows.CsWin32", "0.3.264+4d6898735f.RR")]
internal partial struct JNIInvokeInterface_unmanaged
{
    internal unsafe void* reserved0;
    internal unsafe void* reserved1;
    internal unsafe void* reserved2;

    internal unsafe delegate *unmanaged[Stdcall]<global::Java.Runtime.Native.JavaVM_unmanaged*,global::Java.Runtime.Native.JNI_ERROR> DestroyJavaVM;
    internal unsafe delegate *unmanaged[Stdcall]<global::Java.Runtime.Native.JavaVM_unmanaged*,global::Java.Runtime.Native.JNIEnv*,winmdroot.Runtime.Native.JavaVMAttachArgs*,global::Java.Runtime.Native.JNI_ERROR> AttachCurrentThread;
    internal unsafe delegate *unmanaged[Stdcall]<global::Java.Runtime.Native.JavaVM_unmanaged*,global::Java.Runtime.Native.JNI_ERROR> DetachCurrentThread;
    internal unsafe delegate *unmanaged[Stdcall]<global::Java.Runtime.Native.JavaVM_unmanaged*,global::Java.Runtime.Native.JNIEnv*,global::Java.Runtime.Native.JNI_VERSION,int> GetEnv;
    internal unsafe delegate *unmanaged[Stdcall]<global::Java.Runtime.Native.JavaVM_unmanaged*,global::Java.Runtime.Native.JNIEnv*,winmdroot.Runtime.Native.JavaVMAttachArgs*,global::Java.Runtime.Native.JNI_ERROR> AttachCurrentThreadAsDaemon;
}

I don't know what it is work for, but no one has used the original name.
And the type who use it generated both original and unmanaged one, but the members is same.

WinMD:

public struct JavaVM
{
    [Const]
    public unsafe JNIInvokeInterface* functions;
}

Generated:

[global::System.CodeDom.Compiler.GeneratedCode("Microsoft.Windows.CsWin32", "0.3.264+4d6898735f.RR")]
internal partial struct JavaVM
{
    internal unsafe winmdroot.Runtime.Native.JNIInvokeInterface_unmanaged* functions;
}
[global::System.CodeDom.Compiler.GeneratedCode("Microsoft.Windows.CsWin32", "0.3.264+4d6898735f.RR")]
internal partial struct JavaVM_unmanaged
{
    internal unsafe winmdroot.Runtime.Native.JNIInvokeInterface_unmanaged* functions;
}

Some method is using the original one and some is using the unmanaged one:

[DllImport("jvm", ExactSpelling = true),DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.ApplicationDirectory | DllImportSearchPath.AssemblyDirectory)]
internal static extern unsafe winmdroot.Runtime.Native.JNI_ERROR JNI_CreateJavaVM(out winmdroot.Runtime.Native.JavaVM_unmanaged* pvm, winmdroot.Runtime.Native.JNIEnv** penv, winmdroot.Runtime.Native.JavaVMInitArgs* args);

[DllImport("jvm", ExactSpelling = true),DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.ApplicationDirectory | DllImportSearchPath.AssemblyDirectory)]
internal static extern unsafe winmdroot.Runtime.Native.JNI_ERROR JNI_GetCreatedJavaVMs(ref winmdroot.Runtime.Native.JavaVM param0, int param1, int* param2);

And it makes the method which use the original one wrong:

WinMD:

[DllImport("jvm", ExactSpelling = true, PreserveSig = false)]
[StaticLibrary("jvm")]
public unsafe static extern JNI_ERROR JNI_GetCreatedJavaVMs([In][Out] JavaVM** param0, [In] int param1, [Out] int* param2);

Generated:

/// <inheritdoc cref="JNI_GetCreatedJavaVMs(ref winmdroot.Runtime.Native.JavaVM, int, int*)"/>
[OverloadResolutionPriority(1)]
internal static unsafe winmdroot.Runtime.Native.JNI_ERROR JNI_GetCreatedJavaVMs(ref winmdroot.Runtime.Native.JavaVM param0, int param1, out int param2)
{
    fixed (int* param2Local = &param2)
    {
        winmdroot.Runtime.Native.JNI_ERROR __result = PInvoke.JNI_GetCreatedJavaVMs(ref param0, param1, param2Local);
        return __result;
    }
}

[DllImport("jvm", ExactSpelling = true),DefaultDllImportSearchPaths(DllImportSearchPath.System32 | DllImportSearchPath.ApplicationDirectory | DllImportSearchPath.AssemblyDirectory)]
internal static extern unsafe winmdroot.Runtime.Native.JNI_ERROR JNI_GetCreatedJavaVMs(ref winmdroot.Runtime.Native.JavaVM param0, int param1, int* param2);

Expected behavior

Do not add unmanaged suffix if unnecessary.

I see #1545 has add generate wrapper structs for unmanaged delegates. Is that need to set something to enable it?

Repro steps

Use C#/Win32 to generate:

typedef int(_stdcall* MethodPtr)(void* args);

struct TestStruct
{
    MethodPtr Method;
};
Context
  • CsWin32 version: 0.3.269
  • Target Framework: net4.8.1
  • LangVersion (if explicitly set by project): latest

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the C#/Win32 reproduction involving a struct containing a delegate pointer, then trace how delegate-pointer fields cause unmanaged wrapper structs and suffixed types to be generated. Done means unnecessary unmanaged wrappers are not emitted and generated methods consistently use the appropriate original type.

Written by the indexing model from the issue text.

Assessment

Tech stack
csharp
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.