microsoft/CsWin32

WINMM `PlaySound` does not allow passing byte arrays.

colejohnson66 opened this issue · 1 comments

Actual behavior

WINMD's PlaySound is documented as taking an LPCTSTR of the filename to play. However, if fdwSound is SND_MEMORY, the filename is interpreted as a WAVE file blob. CsWin32 does not allow this usage, but this is how System.Media.SoundPlayer works:

https://github.com/dotnet/runtime/blob/main/src/libraries/Common/src/Interop/Windows/WinMm/Interop.PlaySound.cs

[LibraryImport(Libraries.WinMM, EntryPoint = "PlaySoundW", StringMarshalling = StringMarshalling.Utf16)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool PlaySound(string soundName, IntPtr hmod, int soundFlags);

[LibraryImport(Libraries.WinMM, EntryPoint = "PlaySoundW")]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool PlaySound(byte[]? soundName, IntPtr hmod, int soundFlags);
internal static unsafe winmdroot.Foundation.BOOL PlaySound(string pszSound, SafeHandle hmod, winmdroot.Media.Audio.SND_FLAGS fdwSound)
{
    bool hmodAddRef = false;
    try
    {
        fixed (char* pszSoundLocal = pszSound)
        {
            winmdroot.Foundation.HMODULE hmodLocal;
            if (hmod is object)
            {
                hmod.DangerousAddRef(ref hmodAddRef);
                hmodLocal = (winmdroot.Foundation.HMODULE)hmod.DangerousGetHandle();
            }
            else
                hmodLocal = (winmdroot.Foundation.HMODULE )new IntPtr(0L);
            winmdroot.Foundation.BOOL __result = PInvoke.PlaySound(pszSoundLocal, hmodLocal, fdwSound);
            return __result;
        }
    }
    finally
    {
        if (hmodAddRef)
            hmod.DangerousRelease();
    }
}

[DllImport("WINMM.dll", ExactSpelling = true, EntryPoint = "PlaySoundW")]
[DefaultDllImportSearchPaths(DllImportSearchPath.System32)]
internal static extern winmdroot.Foundation.BOOL PlaySound(winmdroot.Foundation.PCWSTR pszSound, winmdroot.Foundation.HMODULE hmod, winmdroot.Media.Audio.SND_FLAGS fdwSound);

Expected behavior

Two overloads are generated: one taking a string, and one taking a Span<byte>. Both would ideally lower to the same P/Invoke around a void*.

Repro steps

  1. NativeMethods.txt content:
PlaySound

Context

  • CsWin32 version: 0.3.106
  • Target Framework: net8.0
  • LangVersion: Latest

CsWin32 generates overloads based on the metadata, which is based on the header files. Since the original function takes a string pointer, so does our PCWSTR extern method. The string overload is just a friendly version of that.
And just as the native function will let you point at a string or a byte array, you can do the same with PCWSTR. Just create it with a pointer to a byte array instead of a string, as per the documentation. You'll have to cast the pointer (as I expect you would have to do in C).