microsoft / CsWin32

A source generator to add a user-defined set of Win32 P/Invoke methods and supporting types to a C# project.
MIT License
2.1k stars 90 forks source link

ref STRUCT* now generated as STRUCT** #898

Closed Rans4ckeR closed 1 year ago

Rans4ckeR commented 1 year ago

Actual behavior

It seems I can no longer use several functions (BCryptEnumContexts, BCryptEnumContextFunctions, BCryptResolveProviders) because of invalid signatures.

The DllImport now has an [Optional] attribute and seems to cause the overloads that previously were generated as ref STRUCT* now to be generated as STRUCT**.

Example below for BCryptEnumContexts ppBuffer.

0.2.206-beta generates:

[SupportedOSPlatform("windows6.0.6000")]
internal static unsafe winmdroot.Foundation.NTSTATUS BCryptEnumContexts(winmdroot.Security.Cryptography.BCRYPT_TABLE dwTable, ref uint pcbBuffer, winmdroot.Security.Cryptography.CRYPT_CONTEXTS** ppBuffer)
{
    fixed (uint* pcbBufferLocal = &pcbBuffer)
    {
        winmdroot.Foundation.NTSTATUS __result = PInvoke.BCryptEnumContexts(dwTable, pcbBufferLocal, ppBuffer);
        return __result;
    }
}

[DllImport("bcrypt.dll", ExactSpelling = true)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
[SupportedOSPlatform("windows6.0.6000")]
internal static extern unsafe winmdroot.Foundation.NTSTATUS BCryptEnumContexts(winmdroot.Security.Cryptography.BCRYPT_TABLE dwTable, uint* pcbBuffer, [Optional] winmdroot.Security.Cryptography.CRYPT_CONTEXTS** ppBuffer);

Expected behavior

0.2.188-beta generates:

[SupportedOSPlatform("windows6.0.6000")]
internal static unsafe winmdroot.Foundation.NTSTATUS BCryptEnumContexts(winmdroot.Security.Cryptography.BCRYPT_TABLE dwTable, ref uint pcbBuffer, ref winmdroot.Security.Cryptography.CRYPT_CONTEXTS* ppBuffer)
{
    fixed (winmdroot.Security.Cryptography.CRYPT_CONTEXTS** ppBufferLocal = &ppBuffer)
    {
        fixed (uint* pcbBufferLocal = &pcbBuffer)
        {
            winmdroot.Foundation.NTSTATUS __result = PInvoke.BCryptEnumContexts(dwTable, pcbBufferLocal, ppBufferLocal);
            return __result;
        }
    }
}

[DllImport("bcrypt.dll", ExactSpelling = true)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
[SupportedOSPlatform("windows6.0.6000")]
internal static extern unsafe winmdroot.Foundation.NTSTATUS BCryptEnumContexts(winmdroot.Security.Cryptography.BCRYPT_TABLE dwTable, uint* pcbBuffer, winmdroot.Security.Cryptography.CRYPT_CONTEXTS** ppBuffer);

Repro steps

  1. NativeMethods.txt content:

    BCryptAddContextFunction
    BCryptEnumContextFunctions
    BCryptEnumContexts
    BCryptFreeBuffer
    BCryptRemoveContextFunction
    BCryptResolveProviders
    NCryptOpenStorageProvider
    NCryptEnumAlgorithms
    NCRYPT_KEY_HANDLE
    IGroupPolicyObject
    CoInitializeEx
    CoCreateInstance
    CoUninitialize
    RegCreateKeyEx
    RegOpenKeyEx
    RegDeleteValue
    RegSetKeyValue
    RegGetValue
    CryptEnumOIDInfo
    BCRYPT_ECCKEY_BLOB
    PCSTR
    CRYPT_INTEGER_BLOB
    NTE_NO_MORE_ITEMS
    szOID_ECC_CURVE_*
    CLSID_GroupPolicyObject
    NCryptProviderName
    BCryptGetProperty
    BCryptOpenAlgorithmProvider
    BCryptCloseAlgorithmProvider
    BCRYPT_ECC_CURVE_NAMES
    CryptFindOIDInfo
    BCryptGenerateKeyPair
    BCryptSetProperty
    BCryptDestroyKey
    BCRYPT_KEY_LENGTHS_STRUCT
    ECC_CURVE_TYPE_ENUM
    ECC_CURVE_ALG_ID_ENUM
    ALG_CLASS_*
    ALG_TYPE_*
    ALG_SID_*
    CALG_*
    CRYPT_*
    BCRYPT_*
    NCRYPT_*
    MS_PRIMITIVE_PROVIDER
    MS_PLATFORM_CRYPTO_PROVIDER
    GPO_OPEN_*
    GPO_SECTION_*
    HKEY_*
    REG_VALUE_TYPE
    SSL_F12_EXTRA_CERT_CHAIN_POLICY_STATUS
    CERT_INFO
  2. NativeMethods.json content (if present):

  3. Any of your own code that should be shared?

Context

AArnott commented 1 year ago

I'm afraid this is by design, since the ppBuffer is in fact allowed to be NULL and the C# ref syntax doesn't make it intuitive as to how to pass NULL in. All such optional parameters are therefore declared as pointers instead of using ref, in or out. This shouldn't block any of your scenarios, as far as we're aware. It does unfortunately mean you'll need to update your callers. One thing you could do is add a partial class to declare your own overload of this method that doesacceptref, and internally obtain its pointer and pass it on to theextern` method. If you have a lot of callers of this method, this will allow you to avoid fixing them all up individually.

AArnott commented 1 year ago

137 tracks improving usability of this and possibly bringing ref parameters back.