Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
AArnott opened this issue Oct 9, 2024 · 1 comment · May be fixed by #1296
Open

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

AArnott opened this issue Oct 9, 2024 · 1 comment · May be fixed by #1296
Assignees

Comments

@AArnott
Copy link
Member

AArnott commented Oct 9, 2024

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 #1294

@AArnott
Copy link
Member Author

AArnott commented Oct 9, 2024

I'm preparing a change to the friendly overload generation:

-pwszPrevCDFTag = pwszPrevCDFTag.Slice(0, wstrpwszPrevCDFTag.Length);
+pwszPrevCDFTag = pwszPrevCDFTag.Slice(0, wstrpwszPrevCDFTag.Length + 1);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant