I got this code to mute/unmute the master volume
private const int APPCOMMAND_VOLUME_MUTE = 0x80000;
private const int WM_APPCOMMAND = 0x319;
[DllImport("user32.dll")]
public static extern IntPtr SendMessageW(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
SendMessageW(this.Handle, WM_APPCOMMAND, this.Handle, (IntPtr)APPCOMMAND_VOLUME_MUTE);
I would to know how can I get the master volume level/precent because I want to know if the sound is already muted or not.
Edit:
or else I would like to split the mute/unmute sound so I will have two functions - one for mute and one for unmute.
thanks
Have a look at this project http://www.codeproject.com/KB/vista/CoreAudio.aspx
They created an own mixer control, that also reports the current volumne and the mute/unmute state:
defaultDevice.AudioEndpointVolume.OnVolumeNotification += new AudioEndpointVolumeNotificationDelegate(
AudioEndpointVolume_OnVolumeNotification);
// .. snip ..
void AudioEndpointVolume_OnVolumeNotification(AudioVolumeNotificationData data)
{
Console.WriteLine("New Volume {0}", data.MasterVolume);
Console.WriteLine("Muted {0}", data.Muted);
}
Does this help you?
EDIT: With this code and the class from the project you should be able to set and unset mute directly (without toggle):
MMDeviceEnumerator devEnum = new MMDeviceEnumerator();
MMDevice defaultDevice = devEnum.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
defaultDevice.AudioEndpointVolume.Mute = true; // or false
I could not do it for all Windows versions (xp, vista & 7).
Though, I achieved it by used external programs, such as NirCmd, and sent the command I needed.
not so good solution but it did solve my problem.
This thread shows how to control the master volume from C#.
You might also be interested in the responses to this question: Get Master Sound Volume in c#
Especially the NAudio managed wrapper.
Related
Some background
One of my current clients runs a chain of Internet points where customers an access the net through PC:s set up as "kiosks" (a custom-built application "locks" the computer until a user has signed in, and the running account is heavily restricted through the Windows group policy). Currently, each computer is running Windows XP and uses Active Desktop to display advertisements in the background. However, since my client has got problems with Active Desktop crashing on a daily basis (in addition to generally slowing down the computer) I have been asked to develop an application that replaces it.
The problem
I am trying to investigate whether it is possible to build a Windows forms application (using C#) that always stays in the background. The application should lie above the desktop (so that it covers any icons, files etc) but always behind all other running applications. I guess I'm really looking for a BottomMost property of the Form class (which doesn't exist, of course).
Any tips or pointers on how to achieve this would be highly appreciated.
This isn't directly supported by the .NET Form class, so you have two options:
1) Use the Win32 API SetWindowPos function.
pinvoke.net shows how to declare this for use in C#:
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
So in your code, call:
SetWindowPos(Handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
As you commented, this moves the form to the bottom of the z-order but doesn't keep it there. The only workaround I can see for this is to call SetWindowPos from the Form_Load and Form_Activate events. If your application is maximized and the user is unable to move or minimise the form then you might get away with this approach, but it's still something of a hack. Also the user might see a slight "flicker" if the form gets brought to the front of the z-order before the SetWindowPos call gets made.
2) subclass the form, override the WndProc function and intercept the WM_WINDOWPOSCHANGING Windows message, setting the SWP_NOZORDER flag (taken from this page).
I think the best way to do so is using the activated event handler and SendToBack method, like so:
private void Form1_Activated(object sender, EventArgs e)
{
this.SendToBack();
}
Set your window to be a child window of the desktop (the "Program Manager" or "progman" process). I've succeeded with this method in Windows XP (x86) and Windows Vista (x64).
I stumbled on this method while searching for a way to make a screensaver display as if it were wallpaper. It turns out, this is sort of built in to the system's .scr handler. You use screensaver.scr /p PID, where PID is the process id of another program to attach to. So write a program to find progman's handle, then invoke the .scr with that as the /p argument, and you have screensaver wallpaper!
The project I'm playing with now is desktop status display (shows the time, some tasks, mounted disks, etc), and it's built on Strawberry Perl and plain Win32 APIS (mainly the Win32::GUI and Win32::API modules), so the code is easy to port to or understand any dynamic language with similar Win32 API bindings or access to Windows' Scripting Host (eg, ActivePerl, Python, JScript, VBScript). Here's a relevant portion of the class that produces the window:
do { Win32::API->Import(#$_) or die "Win32::API can't import #$_ ($^E)" } for
[user32 => 'HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)'],
[user32 => 'HWND SetParent(HWND hWndChild, HWND hWndNewParent)'],
sub __screen_x {
Win32::GUI::GetSystemMetrics(SM_CXSCREEN)
}
sub __screen_y {
Win32::GUI::GetSystemMetrics(SM_CYSCREEN)
}
sub _create_window { # create window that covers desktop
my $self = shift;
my $wnd = $$self{_wnd} = Win32::GUI::Window->new(
-width => __screen_x(), -left => 0,
-height => __screen_y(), -top => 0,
) or die "can't create window ($^E)";
$wnd->SetWindowLong(GWL_STYLE,
WS_VISIBLE
| WS_POPUP # popup: no caption or border
);
$wnd->SetWindowLong(GWL_EXSTYLE,
WS_EX_NOACTIVATE # noactivate: doesn't activate when clicked
| WS_EX_NOPARENTNOTIFY # noparentnotify: doesn't notify parent window when created or destroyed
| WS_EX_TOOLWINDOW # toolwindow: hide from taskbar
);
SetParent($$wnd{-handle}, # pin window to desktop (bottommost)
(FindWindow('Progman', 'Program Manager') or die "can't find desktop window ($^E)")
) or die "can't pin to desktop ($^E)";
Win32::GUI::DoEvents; # allow sizing and styling to take effect (otherwise DC bitmaps are the wrong size)
}
This program buffers output to prevent flickering, which you'll probably want to do as well. I create a DC (device context) and PaintDesktop to it (you could use any bitmap with only a couple more lines -- CreateCompatibleBitmap, read in a file, and select the bitmap's handle as a brush), then create a holding buffer to keep a clean copy of that background and a working buffer to assemble the pieces -- on each loop, copy in background, then draw lines and brush bitmaps and use TextOut -- which is then copied to the original DC, at which time it appears on screen.
Yes, function SetWindowPos with flag HWND_BOTTOM should help you. But, from my experience: even after calling SetWindowPos as result of some user operations your window may bring to front.
subclass the form, override the WndProc function and intercept the Windows message(s) that are responsible for moving it up the z-order when it gets activated.
Create a Panel that cover your form, but what ever you want on that Panel, then in the Panel's Click-Event write this.sendback .
I've managed to get rid of the flickering when using setwindowpos...
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_NOZORDER = 0x0004;
const int WM_ACTIVATEAPP = 0x001C;
const int WM_ACTIVATE = 0x0006;
const int WM_SETFOCUS = 0x0007;
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const int WM_WINDOWPOSCHANGING = 0x0046;
[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd,
IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr BeginDeferWindowPos(int nNumWindows);
[DllImport("user32.dll")]
static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);
private void Window_Loaded(object sender, RoutedEventArgs e)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.AddHook(new HwndSourceHook(WndProc));
}
private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
if (msg == WM_SETFOCUS)
{
IntPtr hWnd = new WindowInteropHelper(this).Handle;
SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
handled = true;
}
return IntPtr.Zero;
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
HwndSource src = HwndSource.FromHwnd(windowHandle);
src.RemoveHook(new HwndSourceHook(this.WndProc));
}
Question also asked here.
I am on Windows 10, Unity Editor 2018.3.12f1
I have a requirement for very fast (500 Hz) and precise updates, "independent" from the Unity main thread. I am targeting windows desktop only, so the Multimedia Timers seemed as a good (the only?) fit. However I witness some weird crashes.
I tried this library which also includes a working example, and I also created this very simple mono behaviour to test it but with same results:
using System.Threading;
using System.Runtime.InteropServices;
using System;
public class MultimediaTimer : MonoBehaviour
{
private UInt32 userCtx = 0;
void Start()
{
Run();
//Thread r = new Thread(() => Run()); -- Ignore this
//r.Start();
}
void Run()
{
MultimediaTimerCallback Callback = new MultimediaTimerCallback(TimerCallbackMethod);
uint timerId = TimeSetEvent(100, 5, Callback, ref userCtx, 1);
Thread.Sleep(1000);
TimeKillEvent(timerId);
}
private static void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2)
{
Debug.Log("Hi");
}
private delegate void MultimediaTimerCallback(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);
[DllImport("winmm.dll", EntryPoint = "timeSetEvent")]
private static extern UInt32 TimeSetEvent(UInt32 msDelay, UInt32 msResolution, MultimediaTimerCallback callback, ref UInt32 userCtx, UInt32 eventType);
[DllImport("winmm.dll", EntryPoint = "timeKillEvent")]
private static extern UInt32 TimeKillEvent(UInt32 uTimerId);
}
When in Editor, when I press the play button, my sample game seems to be working correctly (ie prints in the Debug console, and with great precision when I used stopwatch to measure), but when i stop the game and try to run it again, the Editor hangs.
When i build and play, again everything seems normal (no debug messages there of course), but when i attempt to close the program via the 'X' button it hangs again.
My question is very simple. Is it possible to reliably call the multimedia timer from a Unity program? And if not could someone explain this behaviour I observe? I could encapsulate my timer logic in a separate process and use some form of IPC to achieve my requirement, but I would prefer for convenience to stay withing Unity. (Of course if you know any alternatives, by all means do say them)
I'm having problems with changing a window's transparency.
What I'm trying to accomplish
I develop a WPF application, which has some interaction with another application (a game developed in Unity3D). My goal is to "integrate" the game into my WPF app to make it look like the game is a natural part of the WPF app. So when I move my main window, I use PInvoke to move the game's window, too, I use PInvoke to hide the window borders of the game window etc. This all works decently.
However, a problem arises when I try to hide the game without closing it. My first approach was to use PInvoke to set the game window as hidden:
[DllImport("user32.dll")]
static extern bool ShowWindow(IntPtr hwnd, int show);
const int SW_HIDE = 0;
const int SW_Show = 5;
ShowWindow(Handle, SW_HIDE);
Using this function call the window disappeares, but as a consequence, the CPU usage of the game process jumps from 1% to 20%. So far, I have no clue why that is. Maybe it's related to Unity, maybe not. (CPU goes back to normal when I show the window again, though).
My second approach was not to hide the window using the ShowWindow-call but to set the window's opacity to 0 (as suggested in this thread). On my setup, this works just fine (without the CPU going crazy), but on other setups, the window will stay visible.
Updating the graphics driver solves the issue sometimes, but not always. I have an old Vista machine where everything works, and I also tried it on newer Dell Laptops with Windows 10 installed, where it does not work. I don't have the issue on my Mac with Windows 10 and Boot Camp. So I have no clue what causes this problem.
Did anyone experience similiar issues? Is there another way to hide a window or does anyone know why CPU goes insane when using the ShowWindow()-method?
Any hint is much appreciated.
EDIT
Would be nice to know why this post is downvoted so much, but okay. I think especially the CPU usage thing is an interesting issue and I am terribly sorry if someone thinks it's obvious and I am a moron.
For those who are also interested, here is a minimal WPF reconstruction of my problem:
MainWindow.xaml
<Window x:Class="SampleWpfApp.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow" Height="1024" Width="1280"
KeyDown="MainWindow_OnKeyDown">
<Grid>
</Grid>
</Window>
MainWindow.xaml.cs
public partial class MainWindow : Window
{
const int SW_HIDE = 0;
const int SW_SHOW = 5;
private Process gameProcess;
private IntPtr gameWindowHandle;
private bool windowHidden = false;
public MainWindow()
{
InitializeComponent();
this.SizeChanged += OnSizeChanged;
}
private void OnSizeChanged(object sender, SizeChangedEventArgs e)
{
StartGame();
}
private void MainWindow_OnKeyDown(object sender, KeyEventArgs e)
{
if (e.Key == Key.Space)
{
if (windowHidden)
{
NativeMethods.ShowWindow(gameWindowHandle, SW_HIDE);
windowHidden = false;
}
else
{
NativeMethods.ShowWindow(gameWindowHandle, SW_SHOW);
windowHidden = true;
}
}
}
private void StartGame()
{
var mainWindowHandle = new WindowInteropHelper(this).Handle;
var processStartInfo = new ProcessStartInfo(#"Path\To\SampleUnityApp.exe");
processStartInfo.Arguments += "-parentHWND " + mainWindowHandle.ToInt32() + " " + Environment.CommandLine + " ";
processStartInfo.CreateNoWindow = true;
processStartInfo.UseShellExecute = false;
gameProcess = Process.Start(processStartInfo);
gameProcess.WaitForInputIdle();
NativeMethods.EnumChildWindows(mainWindowHandle,
(hwnd, lparam) =>
{
// Set the window handle of the game
gameWindowHandle = hwnd;
return 0;
}, IntPtr.Zero);
}
static class NativeMethods
{
[DllImport("user32.dll")]
internal static extern bool ShowWindow(IntPtr hwnd, int show);
[DllImport("user32.dll")]
internal static extern bool EnumChildWindows(IntPtr hwnd, WindowEnumProc func, IntPtr lParam);
internal delegate int WindowEnumProc(IntPtr hwnd, IntPtr lparam);
}
}
The SampleUnityApp I used is just a blank Unity project with a main camera. No other game object. No code I wrote. It's created with Unity 5.3.4f1 Personal.
Screenshot of TaskManager with window visible
Screenshot of TaskManager with window hidden
The story:
I'm writing a music player controlled by voice. Previously the project used winamp for music -- which I'd like to do away with. I'd like to integrate the voice control with music player. The problem is, when changing the volume property of my media player object (mplayer.settings.volume = 5;), it changes the MASTER volume. Meaning any voice feedback will be completely inaudible while music is playing. Not cool when you're driving. If I fire up windows media player, I can change the volume of the music without affecting the master volume.. so there has to be a way.
I've thought of maybe finding out if there's an equalizer control buried in there, but the documentation on that is pathetic. -- either that or my google-fu is weak.
So does anyone know how I would go about separating master and music volume with windows media player control?
Particulars:
Target machine is XP(sp3), with .NET 4.0 I believe. Also, this is a console app.
Thanks in advance for any help
I have tested this in Windows Media Player VER 12, so I guess for most people there is a much easier way than using "user32.dll":
private static WMPLib.WindowsMediaPlayer Player;
public static void VolumeUp()
{
if (Player.settings.volume < 90)
{
Player.settings.volume = (Player.settings.volume + 10);
}
}
public static void VolumeDown()
{
if (Player.settings.volume > 1)
{
Player.settings.volume = (Player.settings.volume - (Player.settings.volume / 2));
}
}
No doubt this has been supported for some time now. It does not change the Master Volume and only the Media Player Volume is changed. The Windows Master Volume is left alone.
Hope this helps others out there that are not limited to XP SP3.
The only way I found of doing this was using Interop and WM_APPCOMMAND windows message:
private const int APPCOMMAND_VOLUME_MUTE = 0x80000;
private const int WM_APPCOMMAND = 0x319;
private const int APPCOMMAND_MICROPHONE_VOLUME_UP = 26 * 65536;
private const int APPCOMMAND_MICROPHONE_VOLUME_DOWN = 25 * 65536;
[DllImport("user32.dll")]
public static extern IntPtr SendMessageW(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
private void SetMicVolume()
{
SendMessageW(new WindowInteropHelper(this).Handle, WM_APPCOMMAND, new (IntPtr)APPCOMMAND_MICROPHONE_VOLUME_UP);//or _DOWN
}
I have created a custom action for my setup project and have successfully implemented a form that displays a progress bar for a download step in my install (I'm using a WebClient in my custom action code). So I have two questions that relate to each other.
Is there any way to show a download progress bar in the main setup window rather than creating a separate form that I display as I have done? I would prefer this.
If not, then what can I do to cause my form to display in front of the actual setup window when I call form.ShowDialog()? I've also called BringToFront() on it which doesn't work either. It's there, but it's always behind the main setup window. Seems there has to be some way to get the correct z-order.
Thanks for your help.
So I gave up on the idea of integrating the progress bar into the actual installer screen, but it's just plain ridiculous what it takes to get the Windows Form to display on top. I have to get a handle to the installer Window and send it to the background because bringing the progress bar window forward simply won't work. I've moved to Mac development now so coming back to this is just frustrating. I remember thinking C# .NET was pretty cool. It's got NOTHING on Cocoa/Objective-C.
It's infuriating having a method called BringToFront() that simply ignores you. Why do I have to drop down to Windows API code to do something as fundamental to a GUI as managing the the Z-Order? Z-Order? Seriously?
In case you're wondering, here's what I ended up doing (via google):
[DllImport("user32.dll", EntryPoint = "SetWindowPos")]
public static extern bool SetWindowPos(
IntPtr hWnd, // window handle
IntPtr hWndInsertAfter, // placement-order handle
int X, // horizontal position
int Y, // vertical position
int cx, // width
int cy, // height
uint uFlags); // window positioning flags
public const uint SWP_NOSIZE = 0x1;
public const uint SWP_NOMOVE = 0x2;
public const uint SWP_SHOWWINDOW = 0x40;
public const uint SWP_NOACTIVATE = 0x10;
[DllImport("user32.dll", EntryPoint = "GetWindow")]
public static extern IntPtr GetWindow(
IntPtr hWnd,
uint wCmd);
public const uint GW_HWNDFIRST = 0;
public const uint GW_HWNDLAST = 1;
public const uint GW_HWNDNEXT = 2;
public const uint GW_HWNDPREV = 3;
public static void ControlSendToBack(IntPtr control)
{
bool s = SetWindowPos(
control,
GetWindow(control, GW_HWNDLAST),
0, 0, 0, 0,
SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
}
I get a handle to the installer window and then call ControlSendToBack() on it. It works, but it sends it to the very back. I tried another method that would just send it back one position, but this wouldn't work either. Windows programming--as good as it was in 1995. Cool.
Another way of doing this is to use a BackgroundWorker. You let the Background Worker handle the downloading of the file so it doesn't prevent the UI being updated.
See this link on donnetperls