-
Notifications
You must be signed in to change notification settings - Fork 117
Description
Is your feature request related to a problem? Please describe.
As an example, here is the LsaOpenPolicy function signature generated by CsWin32:
internal static unsafe winmdroot.Foundation.NTSTATUS LsaOpenPolicy(
winmdroot.Security.Authentication.Identity.LSA_UNICODE_STRING? SystemName,
in winmdroot.Security.Authentication.Identity.LSA_OBJECT_ATTRIBUTES ObjectAttributes,
uint DesiredAccess,
out global::Windows.Win32.LsaCloseSafeHandle PolicyHandle
)When calling this function from C#, I must create my own unsafe wrapper for marshaling the systemName string parameter:
internal static unsafe NTSTATUS LsaOpenPolicy(
string? systemName,
LsaPolicyAccessMask desiredAccess,
out LsaCloseSafeHandle policyHandle)
{
LSA_UNICODE_STRING systemNameUnicode = default;
LSA_OBJECT_ATTRIBUTES attributes = default;
if (systemName is not null)
{
fixed (char* systemNamePointer = systemName)
{
// Marshal string as LSA_UNICODE_STRING
systemNameUnicode.Length = checked((ushort)(systemName.Length * sizeof(char)));
systemNameUnicode.MaximumLength = checked((ushort)((systemName.Length+1) * sizeof(char)));
systemNameUnicode.Buffer = (PWSTR)systemNamePointer;
return PInvoke.LsaOpenPolicy(
systemNameUnicode,
attributes,
checked((uint)desiredAccess),
out policyHandle);
}
}
else
{
return PInvoke.LsaOpenPolicy(
systemNameUnicode,
attributes,
checked((uint)desiredAccess),
out policyHandle);
}
}These safe buffer structures are used heavily in Win32 API, e.g., UNICODE_STRING, LSA_UNICODE_STRING , OEM_STRING, CRYPT_BUFFER, etc.
Describe the solution you'd like
I think that CsWin32 should automatically emit this kind of trivial type converters, to shield developers from unsafe code. Not having to deal with this would save developers a lot of time and bugs. But I understand the complexity of generating this code automatically.
Describe alternatives you've considered
Long before CsWin32, I used to manually create wrappers like this one for UNICODE_STRING:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct UnicodeString
{
private const ushort UnicodeCharLength = 2;
/// <summary>
/// Maximum number of unicode characters that can fit into the buffer, excluding the trailing 0.
/// </summary>
public const ushort MaxLength = ushort.MaxValue / UnicodeCharLength - 1;
public UnicodeString(string? text)
{
if (text == null)
{
this.Length = this.MaximumLength = 0;
this.Buffer = null;
}
else if (text.Length > MaxLength)
{
throw new ArgumentOutOfRangeException(nameof(text));
}
else
{
this.Buffer = text;
// Length of the unicode string.
this.Length = (ushort)(text.Length * UnicodeCharLength);
// Length of the unicode string, including the trailing \0 character.
// Important: Some Windows components, including the Local Security Authority (LSA) do not behave properly if Length==MaximumLength.
this.MaximumLength = (ushort)(this.Length + UnicodeCharLength);
}
}
/// <summary>
/// The length, in bytes, of the string stored in Buffer.
/// </summary>
/// <remarks>
/// If the string is null-terminated, Length does not include the trailing null character.
/// </remarks>
public ushort Length;
/// <summary>
/// The length, in bytes, of Buffer.
/// </summary>
public ushort MaximumLength;
/// <summary>
/// Pointer to a buffer used to contain a string of wide characters.
/// </summary>
[MarshalAs(UnmanagedType.LPWStr)]
public string? Buffer;
}Although the code above avoids unsafe, it is incompatible with trimming and can only be used as [In] parameter, not [Out].