microsoft / microsoft/CsWin32

Friendly overload should preserve null terminator for `ref Span<char>` parameters

Open
#1,295 1 comment 0 reactions 1 assignee View on GitHub

@AArnott is already working on this.

Since Oct 9, 2024.

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

Description

I am trying to work with CryptCATCDFEnumMembersByCDFTagEx, which enumerates file members in the CatalogFiles section of a catalog definition file (CDF). Support for this is newly added since Microsoft.Windows.SDK.Win32Metadata 62.0.23-preview.

The original API syntax is:

LPWSTR WINAPI CryptCATCDFEnumMembersByCDFTagEx(
  _In_    CRYPTCATCDF                  *pCDF,
  _Inout_ LPWSTR                       pwszPrevCDFTag,
  _In_    PFN_CDF_PARSE_ERROR_CALLBACK pfnParseError,
  _In_    CRYPTCATMEMBER               **ppMember,
  _In_    BOOL                         fContinueOnError,
  _In_    LPVOID                       pvReserved
);

The API returns a LPWSTR string pointer (of a null-terminated string) that identifies a file member in the CatalogFiles section. To enumerate the entire file, this return value needs to be passed into subsequent calls with parameter pwszPrevCDFTag, which is also of type LPWSTR.

The generated code from CsWin32 looks like:

/// <summary>Enumerates catalog-level attributes within the CatalogHeader section of a catalog definition file (CDF).</summary>
/// <param name="pCDF">A pointer to a [CRYPTCATCDF](/windows/desktop/api/mscat/ns-mscat-cryptcatcdf) structure.</param>
/// <param name="pPrevAttr">A pointer to a [CRYPTCATATTRIBUTE](/windows/desktop/api/mscat/ns-mscat-cryptcatattribute) structure for a catalog attribute in the CDF pointed to by <i>pCDF</i>.</param>
/// <param name="pfnParseError">A pointer to a user-defined function to handle file parse errors.</param>
/// <returns>Upon success, this function returns a pointer to a [CRYPTCATATTRIBUTE](/windows/desktop/api/mscat/ns-mscat-cryptcatattribute) structure. The <b>CryptCATCDFEnumCatAttributes</b> function returns a <b>NULL</b> pointer if it fails.</returns>
/// <remarks>You typically call this function in a loop to enumerate all of the catalog header attributes in a CDF. Before entering the loop, set <i>pPrevAttr</i> to <b>NULL</b>. The function returns a pointer to the first attribute. Set <i>pPrevAttr</i> to the return  value of the function for subsequent iterations of the loop.</remarks>
[DllImport("WINTRUST.dll", ExactSpelling = true)]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern unsafe winmdroot.Security.Cryptography.Catalog.CRYPTCATATTRIBUTE* CryptCATCDFEnumCatAttributes(winmdroot.Security.Cryptography.Catalog.CRYPTCATCDF* pCDF, winmdroot.Security.Cryptography.Catalog.CRYPTCATATTRIBUTE* pPrevAttr, winmdroot.Security.Cryptography.Catalog.PFN_CDF_PARSE_ERROR_CALLBACK pfnParseError);

/// <inheritdoc cref="CryptCATCDFEnumMembersByCDFTagEx(winmdroot.Security.Cryptography.Catalog.CRYPTCATCDF*, winmdroot.Foundation.PWSTR, winmdroot.Security.Cryptography.Catalog.PFN_CDF_PARSE_ERROR_CALLBACK, winmdroot.Security.Cryptography.Catalog.CRYPTCATMEMBER**, winmdroot.Foundation.BOOL, void*)"/>
internal static unsafe winmdroot.Foundation.PWSTR CryptCATCDFEnumMembersByCDFTagEx(in winmdroot.Security.Cryptography.Catalog.CRYPTCATCDF pCDF, ref Span<char>pwszPrevCDFTag, winmdroot.Security.Cryptography.Catalog.PFN_CDF_PARSE_ERROR_CALLBACK pfnParseError, in winmdroot.Security.Cryptography.Catalog.CRYPTCATMEMBER* ppMember, winmdroot.Foundation.BOOL fContinueOnError, void* pvReserved)
{
	if (pwszPrevCDFTag != null && pwszPrevCDFTag.LastIndexOf('\0') == -1)				throw new ("Required null terminator missing.", "pwszPrevCDFTag");
	fixed (winmdroot.Security.Cryptography.Catalog.CRYPTCATMEMBER** ppMemberLocal = &ppMember)
	{
		fixed (char* ppwszPrevCDFTag = pwszPrevCDFTag)
		{
			fixed (winmdroot.Security.Cryptography.Catalog.CRYPTCATCDF* pCDFLocal = &pCDF)
			{
				winmdroot.Foundation.PWSTR wstrpwszPrevCDFTag = ppwszPrevCDFTag;
				winmdroot.Foundation.PWSTR __result = PInvoke.CryptCATCDFEnumMembersByCDFTagEx(pCDFLocal, wstrpwszPrevCDFTag, pfnParseError, ppMemberLocal, fContinueOnError, pvReserved);
				pwszPrevCDFTag = pwszPrevCDFTag.Slice(0, wstrpwszPrevCDFTag.Length);
				return __result;
			}
		}
	}
}

The generated code has return type of PWSTR, and the corresponding parameter pwszPrevCDFTag has type of ref Span<char>. I tried calling PWSTR.AsSpan() on the return value, but seems the null terminator \0 would be missing after the conversion and caused ArgumentException exception. Seeing Value is being an internal member of PWSTR, what is the recommended way of using the return value as pwszPrevCDFTag argument in this scenario?

Thanks for any advice.

Originally posted by @codgician in https://github.com/microsoft/CsWin32/discussions/1294

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.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.