Question

I need to play sound when click on button. I found this dll which is in c++. So I use p invoke, but an error pop up:

Error 2 The best overloaded method match for 'WinCE.Sound.PlaySound(string, System.IntPtr, int)' has some invalid arguments C:\Users\Fero\Documents\route-loader-recorder\WinCE\Sound.cs 44 17 WinCE

And also this:

Error 3 Argument '3': cannot convert from 'WinCE.PlaySoundFlags' to 'int' C:\Users\Fero\Documents\route-loader-recorder\WinCE\Sound.cs 44 51 WinCE

Any thoughts?

My code is:

namespace Sound
{
    public enum PlaySoundFlags : int {
        SND_SYNC = 0x0,     // play synchronously (default)
        SND_ASYNC = 0x1,    // play asynchronously
        SND_NODEFAULT = 0x2,    // silence (!default) if sound not found
        SND_MEMORY = 0x4,       // pszSound points to a memory file
        SND_LOOP = 0x8,     // loop the sound until next sndPlaySound
        SND_NOSTOP = 0x10,      // don't stop any currently playing sound
        SND_NOWAIT = 0x2000,    // don't wait if the driver is busy
        SND_ALIAS = 0x10000,    // name is a registry alias
        SND_ALIAS_ID = 0x110000,// alias is a predefined ID
        SND_FILENAME = 0x20000, // name is file name
        SND_RESOURCE = 0x40004, // name is resource name or atom
    };

    public class Sound
    {
        [DllImport("winmm.dll", SetLastError = true)]
        public static extern int PlaySound(
            string szSound,
            IntPtr hModule,
            int flags);

        public static void Beep() {
            Play(@"\Windows\Voicbeep");
        }

        public static void Play(string fileName) {
            try {
                PlaySound(fileName, IntPtr.Zero, (PlaySoundFlags.SND_FILENAME | PlaySoundFlags.SND_SYNC));
            } catch (Exception ex) {
                MessageBox.Show("Can't play sound file. " + ex.ToString());
            }
        }
    }
}
Was it helpful?

Solution

Your declaration of PlaySound is wrong in a variety of ways.

First of all, do not set SetLastError to true. The documentation of PlaySound makes no mention of GetLastError, which means that PlaySound makes no promise to call SetLastError. The only error reporting it does is via its return value.

It is easier to declare the return type to be bool, which is a better match for C++ BOOL.

Finally, having gone to the trouble of declaring that nice enum, you may as well use it in your p/invoke. Put it together like this:

[DllImport("winmm.dll")]
public static extern bool PlaySound(
    string szSound,
    IntPtr hModule,
    PlaySoundFlags flags
);

Also do note that Win32 API functions do not throw exceptions. These API functions are designed for interop and not all languages support SEH exception handling. So it will not throw, and indicates errors only by the boolean return value.

Your calling code should be:

public static void Play(string fileName) 
{
    if (!PlaySound(fileName, IntPtr.Zero,
        PlaySoundFlags.SND_FILENAME | PlaySoundFlags.SND_SYNC))
    {
        MessageBox.Show("Can't play sound file.");
    }
}

Note that the final parameter of PlaySound has type DWORD. That's an unsigned 32 bit integer and so strictly speaking your enum should use uint as its base type.

OTHER TIPS

Cast it to int like this:

PlaySound(fileName, IntPtr.Zero, (int)(PlaySoundFlags.SND_FILENAME | PlaySoundFlags.SND_SYNC));

Also, no exception will ever be thrown on P/Invokes in general. You need to check what function returns, and then the Marshal.GetLastWin32Error() value, but in this case PlaySound do not return value in GetLastWin32Error.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top