1

I have network monitoring server VM and always on win10 PC which displays monitoring dashboard. I want to implement sound alert on win10 PC when monitoring server generates alert.

Monitoring server runs CentOS7 and has functionality to execute bash script on alert event. I installed and configured IIS and php7.3 on win10 PC. My idea is to use

curl http://x.x.x.x/index.php

from server when alert is generated. index.php executes powershell script on win10 PC which plays alert sound.

index.php

Shell_Exec ('powershell.exe C:\inetpub\wwwroot\sound.ps1');
echo Shell_Exec ('powershell.exe "Get-Process"); //This is for testing

sound.ps1

$sound = new-Object System.Media.SoundPlayer;
$sound.SoundLocation="C:\inetpub\wwwroot\alert.wav";
$sound.Play();

When powershell script executed localy sound is played. When win10 PC accessed with curl I get information about running processes on computer, but no sound is played. As I understand powershell script is executed not as logged in user so windows doesn't play the sound. How can I make this work? Or is there better solution for this?

EDIT: I added start-transcript line to sound.ps1:

start-transcript -Path C:\ProgramData\log.txt
$sound = new-Object System.Media.SoundPlayer;
$sound.SoundLocation="C:\inetpub\wwwroot\alert.wav";
$sound.Play();
stop-transcript

Strangely I got like 0.5s playback of alert.wav when doing http request.

EDIT2: Added Start-sleep -s 5 to the end of sound.ps1 and it works as intended. (alert.wav playback duration is 3s)

JarJar785
  • 19
  • 2

1 Answers1

0

In your sound.ps1, you can use some C# code to P\Invoke (tap into C/C++ libraries) into the Win32 API to make a service able to play sound. Use Add-Type to compile the code:

Add-Type @"
using System.Runtime.InteropServices;
namespace Audio
{
    internal static class NativeMethods
    {
        [DllImport("winmm.dll", EntryPoint = "PlaySound", SetLastError = true, CharSet = CharSet.Unicode, ThrowOnUnmappableChar = true)]
        public static extern bool PlaySound(
            string szSound,
            System.IntPtr hMod,
            PlaySoundFlags flags);

        [System.Flags]
        public enum PlaySoundFlags : int
        {
            SND_SYNC = 0x0000,/* play synchronously (default) */
            SND_ASYNC = 0x0001, /* play asynchronously */
            SND_NODEFAULT = 0x0002, /* silence (!default) if sound not found */
            SND_MEMORY = 0x0004, /* pszSound points to a memory file */
            SND_LOOP = 0x0008, /* loop the sound until next sndPlaySound */
            SND_NOSTOP = 0x0010, /* don't stop any currently playing sound */
            SND_NOWAIT = 0x00002000, /* don't wait if the driver is busy */
            SND_ALIAS = 0x00010000,/* name is a registry alias */
            SND_ALIAS_ID = 0x00110000, /* alias is a pre d ID */
            SND_FILENAME = 0x00020000, /* name is file name */
            SND_RESOURCE = 0x00040004, /* name is resource name or atom */
            SND_PURGE = 0x0040,  /* purge non-static events for task */
            SND_APPLICATION = 0x0080, /* look for application specific association */
            SND_SENTRY = 0x00080000, /* Generate a SoundSentry event with this sound */
            SND_RING = 0x00100000, /* Treat this as a "ring" from a communications app - don't duck me */
            SND_SYSTEM = 0x00200000 /* Treat this as a system sound */
        }
    }
    public static class Play
    {
        public static void PlaySound(string path, string file = "")
        {            
            NativeMethods.PlaySound(path + file, new System.IntPtr(), NativeMethods.PlaySoundFlags.SND_ASYNC | NativeMethods.PlaySoundFlags.SND_SYSTEM);
        }
    }
}
"@

And then you can invoke it like so:

[Audio.Play]::PlaySound("C:\Folder\Containing\Audio\File", "sound.wav")

Note that I have only tested this with a .wav file but I would imagine it works for any format that Windows has an internal codec for. You can also play around with the PlaySoundFlags if you need to tweak the sound behavior at all.

codewario
  • 19,553
  • 20
  • 90
  • 159
  • Your approach works when script executed manually, but doesn't work from http request. I added `start-transcript` to `sound.ps1` and got these errors: `Add-Type : (0) : Source file 'C:\Windows\TEMP\ek2k1awc.0.cs' could not be found` `FullyQualifiedErrorId : SOURCE_CODE_ERROR,Microsoft.PowerShell.Commands.AddTypeCommand` `Add-Type : Cannot add type. Compilation errors occurred.` Adnd some more compiler errors – JarJar785 Oct 24 '19 at 07:20
  • Hmm.... that's interesting. I'm not sure why that step would fail. I don't have an HTTP server to test through, but what if you compile that code into a DLL, and load it from your `sound.ps1` like so: `Add-Type -Path C:\Path\To\Audio.dll`, where `Audio.dll` would be your built library? – codewario Oct 24 '19 at 12:44