How to programmatically set the system volume? - c#

How can I change the Windows System Sound Volume using a C# Application?

I'm a bit late to the party but if you are looking now there's a nuget package available (AudioSwitcher.AudioApi.CoreAudio) that simplifies audio interactions. Install it then it’s as simple as:
CoreAudioDevice defaultPlaybackDevice = new CoreAudioController().DefaultPlaybackDevice;
Debug.WriteLine("Current Volume:" + defaultPlaybackDevice.Volume);
defaultPlaybackDevice.Volume = 80;

Here is the code:
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace Test
{
public class Test
{
private const int APPCOMMAND_VOLUME_MUTE = 0x80000;
private const int APPCOMMAND_VOLUME_UP = 0xA0000;
private const int APPCOMMAND_VOLUME_DOWN = 0x90000;
private const int WM_APPCOMMAND = 0x319;
[DllImport("user32.dll")]
public static extern IntPtr SendMessageW(IntPtr hWnd, int Msg,
IntPtr wParam, IntPtr lParam);
private void Mute()
{
SendMessageW(this.Handle, WM_APPCOMMAND, this.Handle,
(IntPtr)APPCOMMAND_VOLUME_MUTE);
}
private void VolDown()
{
SendMessageW(this.Handle, WM_APPCOMMAND, this.Handle,
(IntPtr)APPCOMMAND_VOLUME_DOWN);
}
private void VolUp()
{
SendMessageW(this.Handle, WM_APPCOMMAND, this.Handle,
(IntPtr)APPCOMMAND_VOLUME_UP);
}
}
}
Found on dotnetcurry
When using WPF you need to use new WindowInteropHelper(this).Handle instead of this.Handle (thanks Alex Beals)

If the tutorials provided in the other answers are too involved you could try an implementation like this using the keybd_event function
[DllImport("user32.dll")]
static extern void keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
Usage:
keybd_event((byte)Keys.VolumeUp, 0, 0, 0); // increase volume
keybd_event((byte)Keys.VolumeDown, 0, 0, 0); // decrease volume

In case you wish to set it to an exact value using the Core Audio APIs:
using CoreAudioApi;
public class SystemVolumeConfigurator
{
private readonly MMDeviceEnumerator _deviceEnumerator = new MMDeviceEnumerator();
private readonly MMDevice _playbackDevice;
public SystemVolumeConfigurator()
{
_playbackDevice = _deviceEnumerator.GetDefaultAudioEndpoint(EDataFlow.eRender, ERole.eMultimedia);
}
public int GetVolume()
{
return (int)(_playbackDevice.AudioEndpointVolume.MasterVolumeLevelScalar * 100);
}
public void SetVolume(int volumeLevel)
{
if (volumeLevel < 0 || volumeLevel > 100)
throw new ArgumentException("Volume must be between 0 and 100!");
_playbackDevice.AudioEndpointVolume.MasterVolumeLevelScalar = volumeLevel / 100.0f;
}
}

You can add this library https://gist.github.com/sverrirs/d099b34b7f72bb4fb386 to your project and change the volume like this;
VideoPlayerController.AudioManager.SetMasterVolume(100);
The library also includes options for changing application volume, mute, getting current volume level etc.
The namespace is called "Video Player Controller" but I used it in a Windows Forms App to change the system volume and it worked fine, so the "video" part is arbitrary.

C# code:
[Guid("5CDF2C82-841E-4546-9722-0CF74078229A"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IAudioEndpointVolume
{
int _0(); int _1(); int _2(); int _3();
int SetMasterVolumeLevelScalar(float fLevel, Guid pguidEventContext);
int _5();
int GetMasterVolumeLevelScalar(out float pfLevel);
int _7(); int _8(); int _9(); int _10(); int _11(); int _12();
}
[Guid("D666063F-1587-4E43-81F1-B948E807363F"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDevice
{
int Activate(ref System.Guid id, int clsCtx, int activationParams, out IAudioEndpointVolume aev);
}
[Guid("A95664D2-9614-4F35-A746-DE8DB63617E6"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
interface IMMDeviceEnumerator
{
int _0();
int GetDefaultAudioEndpoint(int dataFlow, int role, out IMMDevice endpoint);
}
[ComImport, Guid("BCDE0395-E52F-467C-8E3D-C4579291692E")] class MMDeviceEnumeratorComObject { }
public class Audio
{
private static readonly IAudioEndpointVolume _MMVolume;
static Audio()
{
var enumerator = new MMDeviceEnumeratorComObject() as IMMDeviceEnumerator;
enumerator.GetDefaultAudioEndpoint(0, 1, out IMMDevice dev);
var aevGuid = typeof(IAudioEndpointVolume).GUID;
dev.Activate(ref aevGuid, 1, 0, out _MMVolume);
}
public static int Volume
{
get
{
_MMVolume.GetMasterVolumeLevelScalar(out float level);
return (int)(level * 100);
}
set
{
_MMVolume.SetMasterVolumeLevelScalar((float)value / 100, default);
}
}
}
Usage:
Audio.Volume = 50;
More info on MSDN.

My code is a bit different but still using CoreAudio
downloaded the pkg :
nuget install AudioSwitcher.AudioApi.CoreAudio -Version 3.0.0.1
using AudioSwitcher.AudioApi.CoreAudio;
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
CoreAudioDevice defaultPlaybackDevice = new CoreAudioController().DefaultPlaybackDevice;
double vol = defaultPlaybackDevice.Volume;
defaultPlaybackDevice.Volume = defaultPlaybackDevice.Volume - 5.0;
defaultPlaybackDevice.Volume = defaultPlaybackDevice.Volume + 5.0;
}
}

Related

c# Is there a function to get Monitor (Primary and Secondary) Scale %? [duplicate]

I know this has been asked before, but I've tried all the answers I've found and none of them seem to work for me. Answers seem to work on a single monitor, or require a window handle, or to be in a WPF application. I've a C# class library with no UI that is called from a different language all together.
I've been asked to determine the scaling factor, e.g. 1, 1.25, 1.5, etc. for each monitor attached to the current PC in a C# class library.
I also need to provide the resolution and colour depth for each monitor. The registry does hold the DpiValue, whatever that is, in Windows 10 under
Computer\HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings
However I have no idea how to map those to a Screen in order to get the matching resolution returned in
System.Windows.Forms.Screen.AllScreens
So does anyone have a way of getting this information?
I believe I have finally (after a long time of searching) found an answer that works, it even works on my high DPI Surface Book 2 screen. I have tested it as much as I can, and so far it's always returned the correct value.
Here's how I did it, thanks to whoever posted the code fragments in the past where I gathered this from.
First you need a structure to call EnumDisplaySettings in user32.dll
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
private const int CCHDEVICENAME = 0x20;
private const int CCHFORMNAME = 0x20;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public ScreenOrientation dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
Then you need to declare the external function call
[DllImport("user32.dll")]
public static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);
Then you need to use it to calculate the screen scaling
Screen[] screenList = Screen.AllScreens;
foreach (Screen screen in screenList)
{
DEVMODE dm = new DEVMODE();
dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
EnumDisplaySettings(screen.DeviceName, -1, ref dm);
var scalingFactor = Math.Round(Decimal.Divide(dm.dmPelsWidth, screen.Bounds.Width), 2);
}
Hope others find this useful.
I think you can get scaling factor for each monitor like this.
public void GetScalingFactor()
{
List<double> physicalWidths = new List<double>();
//Get physical width for each monitor
ManagementObjectSearcher searcher = new ManagementObjectSearcher("\\root\\wmi", "SELECT * FROM WmiMonitorBasicDisplayParams");
foreach (ManagementObject monitor in searcher.Get())
{
//Get the physical width (inch)
double width = (byte)monitor["MaxHorizontalImageSize"] / 2.54;
physicalWidths.Add(width);
}
//Get screen info for each monitor
Screen[] screenList = Screen.AllScreens;
int i = 0;
foreach (Screen screen in screenList)
{
//Get the physical width (pixel)
double physicalWidth;
if (i < physicalWidths.Count)
{
//Get the DPI
uint x, y;
GetDpi(screen, DpiType.Effective, out x, out y);
//Convert inch to pixel
physicalWidth = physicalWidths[i] * x;
}
else
{
physicalWidth = SystemParameters.PrimaryScreenWidth;
}
i++;
//Calculate the scaling
double scaling = 100 * (physicalWidth / screen.Bounds.Width);
double scalingFactor = physicalWidth / screen.Bounds.Width;
//Output the result
Console.WriteLine(scalingFactor);
}
}
And you need also add these codes to use to get the monitor DPI (these code is from https://stackoverflow.com/a/29463627/12949439, thanks #Koopakiller):
public void GetDpi(Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062(v=vs.85).aspx
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511(v=vs.85).aspx
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
Unfortunately, the answer of user3225503 seems not to work (anymore?)
My scenario: WIN10 20H2, WPF-App with dpi-awareness "PerMonitor", Framework 4.7.2, 2 Monitors with different resolutions and different screen scalings ("Horror scenario"):
the dm.dmPelsWidth member of the DEVMODE structure has always the physical resolution of my monitors, so the scaling is always 1.0.
All what we want is to restore our program and its windows like we left it in the last session right? This seems to be incredibly hard, thanks to MS!
But a different approach seems to work:
Switch on per-monitor dpi-awareness in the manifest file of your application:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect :
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
Always use GetPlacement and SetPlacement win32-api calls for storing/restoring window placements
SetPlacement will set the wrong dialog width/height if the dialog is on a secondary display and each display has different scalings. So we need a new factor depending on scaling factors of each display to correct this in the Loading-event of the window:
event code:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
There are some more goodies in the code example, e.g. serialization of the WINDOWPLACEMENT structure. don't forget to create a member "Placement" in your application settings! Tell me if this works for you:
Example code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Windows;
namespace DpiApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
var plc = ScreenExtensions.GetPlacement(shwnd.Handle);
Properties.Settings.Default.Placement = plc.ToString();
Properties.Settings.Default.Save();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
}
public static class ScreenExtensions
{
public const string User32 = "user32.dll";
public const string shcore = "Shcore.dll";
public static void GetDpi(this System.Windows.Forms.Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
public static double GetScalingForPoint(System.Drawing.Point aPoint)
{
var mon = MonitorFromPoint(aPoint, 2/*MONITOR_DEFAULTTONEAREST*/);
uint dpiX, dpiY;
GetDpiForMonitor(mon, DpiType.Effective, out dpiX, out dpiY);
return (double)dpiX / 96.0;
}
[DllImport(User32)]
private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
[DllImport(shcore)]
private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
[DllImport(User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport(User32, CharSet = CharSet.Auto, SetLastError = true)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
public static WINDOWPLACEMENT GetPlacement(IntPtr hWnd)
{
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(hWnd, ref placement);
return placement;
}
public static bool SetPlacement(IntPtr hWnd, WINDOWPLACEMENT aPlacement)
{
bool erg = SetWindowPlacement(hWnd, ref aPlacement);
return erg;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINTSTRUCT
{
public int x;
public int y;
public POINTSTRUCT(int x, int y)
{
this.x = x;
this.y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public RECT(Rect r)
{
this.left = (int)r.Left;
this.top = (int)r.Top;
this.right = (int)r.Right;
this.bottom = (int)r.Bottom;
}
public static RECT FromXYWH(int x, int y, int width, int height)
{
return new RECT(x, y, x + width, y + height);
}
public Size Size
{
get { return new Size(this.right - this.left, this.bottom - this.top); }
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT
{
public int length;
public uint flags;
public uint showCmd;
public POINTSTRUCT ptMinPosition;
public POINTSTRUCT ptMaxPosition;
public RECT rcNormalPosition;
public override string ToString()
{
byte[] StructBytes = RawSerialize(this);
return System.Convert.ToBase64String(StructBytes);
}
public void ReadFromBase64String(string aB64)
{
byte[] b64 = System.Convert.FromBase64String(aB64);
var NewWP = ReadStruct<WINDOWPLACEMENT>(b64, 0);
length = NewWP.length;
flags = NewWP.flags;
showCmd = NewWP.showCmd;
ptMinPosition.x = NewWP.ptMinPosition.x;
ptMinPosition.y = NewWP.ptMinPosition.y;
ptMaxPosition.x = NewWP.ptMaxPosition.x;
ptMaxPosition.y = NewWP.ptMaxPosition.y;
rcNormalPosition.left = NewWP.rcNormalPosition.left;
rcNormalPosition.top = NewWP.rcNormalPosition.top;
rcNormalPosition.right = NewWP.rcNormalPosition.right;
rcNormalPosition.bottom = NewWP.rcNormalPosition.bottom;
}
static public T ReadStruct<T>(byte[] aSrcBuffer, int aOffset)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
Buffer.BlockCopy(aSrcBuffer, aOffset, buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
static public T ReadStruct<T>(Stream fs)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
fs.Read(buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
public static byte[] RawSerialize(object anything)
{
int rawsize = Marshal.SizeOf(anything);
byte[] rawdata = new byte[rawsize];
GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned);
Marshal.StructureToPtr(anything, handle.AddrOfPinnedObject(), false);
handle.Free();
return rawdata;
}
}
}
}

How to get scaling factor for each monitor, e.g. 1, 1.25, 1.5

I know this has been asked before, but I've tried all the answers I've found and none of them seem to work for me. Answers seem to work on a single monitor, or require a window handle, or to be in a WPF application. I've a C# class library with no UI that is called from a different language all together.
I've been asked to determine the scaling factor, e.g. 1, 1.25, 1.5, etc. for each monitor attached to the current PC in a C# class library.
I also need to provide the resolution and colour depth for each monitor. The registry does hold the DpiValue, whatever that is, in Windows 10 under
Computer\HKEY_CURRENT_USER\Control Panel\Desktop\PerMonitorSettings
However I have no idea how to map those to a Screen in order to get the matching resolution returned in
System.Windows.Forms.Screen.AllScreens
So does anyone have a way of getting this information?
I believe I have finally (after a long time of searching) found an answer that works, it even works on my high DPI Surface Book 2 screen. I have tested it as much as I can, and so far it's always returned the correct value.
Here's how I did it, thanks to whoever posted the code fragments in the past where I gathered this from.
First you need a structure to call EnumDisplaySettings in user32.dll
[StructLayout(LayoutKind.Sequential)]
public struct DEVMODE
{
private const int CCHDEVICENAME = 0x20;
private const int CCHFORMNAME = 0x20;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmDeviceName;
public short dmSpecVersion;
public short dmDriverVersion;
public short dmSize;
public short dmDriverExtra;
public int dmFields;
public int dmPositionX;
public int dmPositionY;
public ScreenOrientation dmDisplayOrientation;
public int dmDisplayFixedOutput;
public short dmColor;
public short dmDuplex;
public short dmYResolution;
public short dmTTOption;
public short dmCollate;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 0x20)]
public string dmFormName;
public short dmLogPixels;
public int dmBitsPerPel;
public int dmPelsWidth;
public int dmPelsHeight;
public int dmDisplayFlags;
public int dmDisplayFrequency;
public int dmICMMethod;
public int dmICMIntent;
public int dmMediaType;
public int dmDitherType;
public int dmReserved1;
public int dmReserved2;
public int dmPanningWidth;
public int dmPanningHeight;
}
Then you need to declare the external function call
[DllImport("user32.dll")]
public static extern bool EnumDisplaySettings(string lpszDeviceName, int iModeNum, ref DEVMODE lpDevMode);
Then you need to use it to calculate the screen scaling
Screen[] screenList = Screen.AllScreens;
foreach (Screen screen in screenList)
{
DEVMODE dm = new DEVMODE();
dm.dmSize = (short)Marshal.SizeOf(typeof(DEVMODE));
EnumDisplaySettings(screen.DeviceName, -1, ref dm);
var scalingFactor = Math.Round(Decimal.Divide(dm.dmPelsWidth, screen.Bounds.Width), 2);
}
Hope others find this useful.
I think you can get scaling factor for each monitor like this.
public void GetScalingFactor()
{
List<double> physicalWidths = new List<double>();
//Get physical width for each monitor
ManagementObjectSearcher searcher = new ManagementObjectSearcher("\\root\\wmi", "SELECT * FROM WmiMonitorBasicDisplayParams");
foreach (ManagementObject monitor in searcher.Get())
{
//Get the physical width (inch)
double width = (byte)monitor["MaxHorizontalImageSize"] / 2.54;
physicalWidths.Add(width);
}
//Get screen info for each monitor
Screen[] screenList = Screen.AllScreens;
int i = 0;
foreach (Screen screen in screenList)
{
//Get the physical width (pixel)
double physicalWidth;
if (i < physicalWidths.Count)
{
//Get the DPI
uint x, y;
GetDpi(screen, DpiType.Effective, out x, out y);
//Convert inch to pixel
physicalWidth = physicalWidths[i] * x;
}
else
{
physicalWidth = SystemParameters.PrimaryScreenWidth;
}
i++;
//Calculate the scaling
double scaling = 100 * (physicalWidth / screen.Bounds.Width);
double scalingFactor = physicalWidth / screen.Bounds.Width;
//Output the result
Console.WriteLine(scalingFactor);
}
}
And you need also add these codes to use to get the monitor DPI (these code is from https://stackoverflow.com/a/29463627/12949439, thanks #Koopakiller):
public void GetDpi(Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
//https://msdn.microsoft.com/en-us/library/windows/desktop/dd145062(v=vs.85).aspx
[DllImport("User32.dll")]
private static extern IntPtr MonitorFromPoint([In]System.Drawing.Point pt, [In]uint dwFlags);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx
[DllImport("Shcore.dll")]
private static extern IntPtr GetDpiForMonitor([In]IntPtr hmonitor, [In]DpiType dpiType, [Out]out uint dpiX, [Out]out uint dpiY);
//https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511(v=vs.85).aspx
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
Unfortunately, the answer of user3225503 seems not to work (anymore?)
My scenario: WIN10 20H2, WPF-App with dpi-awareness "PerMonitor", Framework 4.7.2, 2 Monitors with different resolutions and different screen scalings ("Horror scenario"):
the dm.dmPelsWidth member of the DEVMODE structure has always the physical resolution of my monitors, so the scaling is always 1.0.
All what we want is to restore our program and its windows like we left it in the last session right? This seems to be incredibly hard, thanks to MS!
But a different approach seems to work:
Switch on per-monitor dpi-awareness in the manifest file of your application:
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<!-- The combination of below two tags have the following effect :
1) Per-Monitor for >= Windows 10 Anniversary Update
2) System < Windows 10 Anniversary Update -->
<dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings"> PerMonitor</dpiAwareness>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
</windowsSettings>
Always use GetPlacement and SetPlacement win32-api calls for storing/restoring window placements
SetPlacement will set the wrong dialog width/height if the dialog is on a secondary display and each display has different scalings. So we need a new factor depending on scaling factors of each display to correct this in the Loading-event of the window:
event code:
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
There are some more goodies in the code example, e.g. serialization of the WINDOWPLACEMENT structure. don't forget to create a member "Placement" in your application settings! Tell me if this works for you:
Example code:
using System;
using System.Collections.Generic;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using System.Windows;
namespace DpiApp
{
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
}
private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
var plc = ScreenExtensions.GetPlacement(shwnd.Handle);
Properties.Settings.Default.Placement = plc.ToString();
Properties.Settings.Default.Save();
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
if (string.IsNullOrWhiteSpace(Properties.Settings.Default.Placement))
return;
ScreenExtensions.WINDOWPLACEMENT placement = new ScreenExtensions.WINDOWPLACEMENT();
placement.ReadFromBase64String(Properties.Settings.Default.Placement);
System.Windows.Interop.HwndSource shwnd = System.Windows.Interop.HwndSource.FromVisual(this) as System.Windows.Interop.HwndSource;
double PrimaryMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(1, 1));
double CurrentMonitorScaling = ScreenExtensions.GetScalingForPoint(new System.Drawing.Point(placement.rcNormalPosition.left, placement.rcNormalPosition.top));
double RescaleFactor = CurrentMonitorScaling / PrimaryMonitorScaling;
double width = placement.rcNormalPosition.right - placement.rcNormalPosition.left;
double height = placement.rcNormalPosition.bottom - placement.rcNormalPosition.top;
placement.rcNormalPosition.right = placement.rcNormalPosition.left + (int)(width / RescaleFactor + 0.5);
placement.rcNormalPosition.bottom = placement.rcNormalPosition.top + (int)(height / RescaleFactor + 0.5);
ScreenExtensions.SetPlacement(shwnd.Handle, placement);
}
}
public static class ScreenExtensions
{
public const string User32 = "user32.dll";
public const string shcore = "Shcore.dll";
public static void GetDpi(this System.Windows.Forms.Screen screen, DpiType dpiType, out uint dpiX, out uint dpiY)
{
var pnt = new System.Drawing.Point(screen.Bounds.Left + 1, screen.Bounds.Top + 1);
var mon = MonitorFromPoint(pnt, 2/*MONITOR_DEFAULTTONEAREST*/);
GetDpiForMonitor(mon, dpiType, out dpiX, out dpiY);
}
public static double GetScalingForPoint(System.Drawing.Point aPoint)
{
var mon = MonitorFromPoint(aPoint, 2/*MONITOR_DEFAULTTONEAREST*/);
uint dpiX, dpiY;
GetDpiForMonitor(mon, DpiType.Effective, out dpiX, out dpiY);
return (double)dpiX / 96.0;
}
[DllImport(User32)]
private static extern IntPtr MonitorFromPoint([In] System.Drawing.Point pt, [In] uint dwFlags);
[DllImport(shcore)]
private static extern IntPtr GetDpiForMonitor([In] IntPtr hmonitor, [In] DpiType dpiType, [Out] out uint dpiX, [Out] out uint dpiY);
[DllImport(User32, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl);
[DllImport(User32, CharSet = CharSet.Auto, SetLastError = true)]
[ResourceExposure(ResourceScope.None)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SetWindowPlacement(IntPtr hWnd, [In] ref WINDOWPLACEMENT lpwndpl);
public enum DpiType
{
Effective = 0,
Angular = 1,
Raw = 2,
}
public static WINDOWPLACEMENT GetPlacement(IntPtr hWnd)
{
WINDOWPLACEMENT placement = new WINDOWPLACEMENT();
placement.length = Marshal.SizeOf(placement);
GetWindowPlacement(hWnd, ref placement);
return placement;
}
public static bool SetPlacement(IntPtr hWnd, WINDOWPLACEMENT aPlacement)
{
bool erg = SetWindowPlacement(hWnd, ref aPlacement);
return erg;
}
[StructLayout(LayoutKind.Sequential)]
public struct POINTSTRUCT
{
public int x;
public int y;
public POINTSTRUCT(int x, int y)
{
this.x = x;
this.y = y;
}
}
[StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int left;
public int top;
public int right;
public int bottom;
public RECT(int left, int top, int right, int bottom)
{
this.left = left;
this.top = top;
this.right = right;
this.bottom = bottom;
}
public RECT(Rect r)
{
this.left = (int)r.Left;
this.top = (int)r.Top;
this.right = (int)r.Right;
this.bottom = (int)r.Bottom;
}
public static RECT FromXYWH(int x, int y, int width, int height)
{
return new RECT(x, y, x + width, y + height);
}
public Size Size
{
get { return new Size(this.right - this.left, this.bottom - this.top); }
}
}
[StructLayout(LayoutKind.Sequential)]
public struct WINDOWPLACEMENT
{
public int length;
public uint flags;
public uint showCmd;
public POINTSTRUCT ptMinPosition;
public POINTSTRUCT ptMaxPosition;
public RECT rcNormalPosition;
public override string ToString()
{
byte[] StructBytes = RawSerialize(this);
return System.Convert.ToBase64String(StructBytes);
}
public void ReadFromBase64String(string aB64)
{
byte[] b64 = System.Convert.FromBase64String(aB64);
var NewWP = ReadStruct<WINDOWPLACEMENT>(b64, 0);
length = NewWP.length;
flags = NewWP.flags;
showCmd = NewWP.showCmd;
ptMinPosition.x = NewWP.ptMinPosition.x;
ptMinPosition.y = NewWP.ptMinPosition.y;
ptMaxPosition.x = NewWP.ptMaxPosition.x;
ptMaxPosition.y = NewWP.ptMaxPosition.y;
rcNormalPosition.left = NewWP.rcNormalPosition.left;
rcNormalPosition.top = NewWP.rcNormalPosition.top;
rcNormalPosition.right = NewWP.rcNormalPosition.right;
rcNormalPosition.bottom = NewWP.rcNormalPosition.bottom;
}
static public T ReadStruct<T>(byte[] aSrcBuffer, int aOffset)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
Buffer.BlockCopy(aSrcBuffer, aOffset, buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
static public T ReadStruct<T>(Stream fs)
{
byte[] buffer = new byte[Marshal.SizeOf(typeof(T))];
fs.Read(buffer, 0, Marshal.SizeOf(typeof(T)));
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T temp = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return temp;
}
public static byte[] RawSerialize(object anything)
{
int rawsize = Marshal.SizeOf(anything);
byte[] rawdata = new byte[rawsize];
GCHandle handle = GCHandle.Alloc(rawdata, GCHandleType.Pinned);
Marshal.StructureToPtr(anything, handle.AddrOfPinnedObject(), false);
handle.Free();
return rawdata;
}
}
}
}

how to connect mobile camera or other camera to the picture box in c#,winforms?

Started learning c# from past 1 month,I have a question that whether is it possibility to connect and display mobile camera or other web camera in the picture box.,in c# .net framework in windows forms. thanks in advance
You question is a bit too broad for this site. The answer is "Yes it is possible" but it is not as simple as writing a few lines of code in C#. You will need some kind of toolkit/library/framework that allows you to connect to the camera and display the video. There are tons of them out there, each having pros and cons, some simple, some complicated.
If you are using Visual Studio, in the Solution Explorer right-click on your Project name and select "Manage Nuget Packages". In the Window that opens select "Browse" and then type "camera" into the search box. You will see a number of packages that provide support for using cameras.
There are several ways to communicate with the camera.
I use this code to communicate with the camera.
public class CameraAPI
{
public bool IsAvailable { get; set; }
[DllImport("avicap32.dll")]
public static extern IntPtr capCreateCaptureWindowA(byte[] lpszWindowName, int dwStyle, int x, int y, int nWidth, int nHeight, IntPtr hWndParent, int nID);
[DllImport("avicap32.dll")]
public static extern bool capGetDriverDescriptionA(short wDriver, byte[] lpszName, int cbName, byte[] lpszVer, int cbVer);
[DllImport("User32.dll")]
public static extern bool SendMessage(IntPtr hWnd, int wMsg, bool wParam, long lParam);
[DllImport("User32.dll")]
public static extern bool SendMessage(IntPtr hWnd, int wMsg, short wParam, long lParam);
[System.Runtime.InteropServices.DllImport("user32")]
public static extern bool DestroyWindow(IntPtr hWnd);
public const int WM_USER = 0x400;
public const int WS_CHILD = 0x40000000;
public const int WS_VISIBLE = 0x10000000;
public const int SWP_NOMOVE = 0x2;
public const int SWP_NOZORDER = 0x4;
public const int WM_CAP_DRIVER_CONNECT = WM_USER + 10;
public const int WM_CAP_DRIVER_DISCONNECT = WM_USER + 11;
public const int WM_CAP_SET_CALLBACK_FRAME = WM_USER + 5;
public const int WM_CAP_SET_PREVIEW = WM_USER + 50;
public const int WM_CAP_SET_PREVIEWFORMAT = WM_USER + 45;
public const int WM_CAP_SET_PREVIEWRATE = WM_USER + 52;
public const int WM_CAP_START = WM_USER;
public const int WM_CAP_SAVEDIB = WM_CAP_START + 25;
public const int WM_CAP_EDIT_COPY = (WM_CAP_START + 30);
private IntPtr hWnd;
private IntPtr mControlPtr;
private int mWidth;
private int mHeight;
public CameraAPI(IntPtr handel, int width, int height)
{
mControlPtr = handel; //handle of video dom
mWidth = width; //video width
mHeight = height; //video height
}
public void StartPreviewWebcam()
{
if (hWnd != null)
DestroyWindow(hWnd);
byte[] lpszName = new byte[100];
byte[] lpszVer = new byte[100];
capGetDriverDescriptionA(0, lpszName, 100, lpszVer, 0);
hWnd = capCreateCaptureWindowA(lpszName, WS_CHILD | WS_VISIBLE, 0, 0, mWidth, mHeight, mControlPtr, 0);
if (SendMessage(hWnd, WM_CAP_DRIVER_CONNECT, 0, 0))
{
SendMessage(hWnd, WM_CAP_SET_PREVIEWRATE, 100, 0);
SendMessage(hWnd, WM_CAP_SET_PREVIEW, true, 0);
IsAvailable = true;
}
else
{
IsAvailable = false;
}
}
public void CloseWebcam()
{
SendMessage(hWnd, WM_CAP_DRIVER_DISCONNECT, 0, 0);
IsAvailable = false;
}
public void SavePictureByPath(string path)
{
IntPtr hBmp = Marshal.StringToHGlobalAnsi(path);
SendMessage(hWnd, WM_CAP_SAVEDIB, 0, hBmp.ToInt64());
}
public byte[] TakePicture()
{
byte[] imgByteArray = null;
var path = Application.StartupPath + #"\Image.png";
try
{
DeleteExist(path);
SavePictureByPath(path);
if (File.Exists(path))
{
using (Image img = Image.FromFile(path))
{
imgByteArray = ImageToByteArray(img);
}
}
}
catch (Exception exp)
{
var a = 1;
}
finally
{
DeleteExist(path);
}
return imgByteArray;
}
public void DeleteExist(string path)
{
if (File.Exists(path))
{
File.Delete(path);
}
}
public byte[] ImageToByteArray(System.Drawing.Image imageIn)
{
using (var ms = new MemoryStream())
{
imageIn.Save(ms, System.Drawing.Imaging.ImageFormat.Gif);
return ms.ToArray();
}
}
}

C# error on build

Hi I am getting an 'Inconsistent accessibility: parameter type'clsWebCamArgs' is less accessible than delegate 'ctrlCamera.WebCamEventHandler'. Can anyone give any insight as to whats wrong with the code below.
using System;
using System.ComponentModel;
using System.ComponentModel.Design;
using System.Drawing;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace PayrollSystem
{
[ToolboxBitmap(typeof(ctrlCamera), "Camera.ico")] // toolbox bitmap
[Designer("Sytem.Windows.Forms.Design.ParentControlDesigner,System.Design", typeof(IDesigner))] // make composite
public partial class ctrlCamera : UserControl
{
public ctrlCamera(){InitializeComponent();}
// property variables
private int m_TimeToCapture_milliseconds = 100;
private int m_Width = 320;
private int m_Height = 240;
private int mCapHwnd;
private ulong m_FrameNumber = 0;
// global variables to make the video capture go faster
private PayrollSystem.clsWebCamArgs x = new PayrollSystem.clsWebCamArgs();
private IDataObject tempObj;
private Image tempImg;
private bool bStopped = true;
// event delegate
public delegate void WebCamEventHandler(object source, PayrollSystem.clsWebCamArgs e);
// fired when a new image is captured
public event WebCamEventHandler ImageCaptured;
#region API Declarations
[DllImport("user32", EntryPoint = "SendMessage")]
public static extern int SendMessage(int hWnd, uint Msg, int wParam, int lParam);
[DllImport("avicap32.dll", EntryPoint = "capCreateCaptureWindowA")]
public static extern int capCreateCaptureWindowA(string lpszWindowName, int dwStyle, int X, int Y, int nWidth, int nHeight, int hwndParent, int nID);
[DllImport("user32", EntryPoint = "OpenClipboard")]
public static extern int OpenClipboard(int hWnd);
[DllImport("user32", EntryPoint = "EmptyClipboard")]
public static extern int EmptyClipboard();
[DllImport("user32", EntryPoint = "CloseClipboard")]
public static extern int CloseClipboard();
#endregion
#region API Constants
public const int WM_USER = 1024;
public const int WM_CAP_CONNECT = 1034;
public const int WM_CAP_DISCONNECT = 1035;
public const int WM_CAP_GET_FRAME = 1084;
public const int WM_CAP_COPY = 1054;
public const int WM_CAP_START = WM_USER;
public const int WM_CAP_DLG_VIDEOFORMAT = WM_CAP_START + 41;
public const int WM_CAP_DLG_VIDEOSOURCE = WM_CAP_START + 42;
public const int WM_CAP_DLG_VIDEODISPLAY = WM_CAP_START + 43;
public const int WM_CAP_GET_VIDEOFORMAT = WM_CAP_START + 44;
public const int WM_CAP_SET_VIDEOFORMAT = WM_CAP_START + 45;
public const int WM_CAP_DLG_VIDEOCOMPRESSION = WM_CAP_START + 46;
public const int WM_CAP_SET_PREVIEW = WM_CAP_START + 50;
#endregion
#region Control Properties
public int TimeToCapture_milliseconds
{
get { return m_TimeToCapture_milliseconds; }
set { m_TimeToCapture_milliseconds = value; }
}
public int CaptureHeight
{
get { return m_Height; }
set { m_Height = value; }
}
public int CaptureWidth
{
get { return m_Width; }
set { m_Width = value; }
}
public ulong FrameNumber
{
get { return m_FrameNumber; }
set { m_FrameNumber = value; }
}
#endregion
#region Start and Stop Capture Functions
public void Start(ulong FrameNum)
{
try
{
// for safety, call stop, just in case we are already running
this.Stop();
// setup a capture window
mCapHwnd = capCreateCaptureWindowA("WebCap", 0, 0, 0, m_Width, m_Height, this.Handle.ToInt32(), 0);
// connect to the capture device
Application.DoEvents();
SendMessage(mCapHwnd, WM_CAP_CONNECT, 0, 0);
SendMessage(mCapHwnd, WM_CAP_SET_PREVIEW, 0, 0);
// set the frame number
m_FrameNumber = FrameNum;
// set the timer information
this.timer1.Interval = m_TimeToCapture_milliseconds;
bStopped = false;
this.timer1.Start();
}
catch (Exception excep)
{
MessageBox.Show("An error ocurred while starting the video capture. Check that your webcamera is connected properly and turned on.\r\n\n" + excep.Message);
this.Stop();
}
}
public void Stop()
{
try
{
// stop the timer
bStopped = true;
this.timer1.Stop();
// disconnect from the video source
Application.DoEvents();
SendMessage(mCapHwnd, WM_CAP_DISCONNECT, 0, 0);
}
catch (Exception excep)
{ // don't raise an error here.
}
}
#endregion
#region Video Capture Code
private void timer1_Tick(object sender, EventArgs e)
{
try
{
// pause the timer
this.timer1.Stop();
// get the next frame;
SendMessage(mCapHwnd, WM_CAP_GET_FRAME, 0, 0);
// copy the frame to the clipboard
SendMessage(mCapHwnd, WM_CAP_COPY, 0, 0);
// paste the frame into the event args image
if (ImageCaptured != null)
{
// get from the clipboard
tempObj = Clipboard.GetDataObject();
tempImg = (Bitmap)tempObj.GetData(DataFormats.Bitmap);
x.WebCamImage = tempImg.GetThumbnailImage(m_Width, m_Height, null, IntPtr.Zero);
// raise the event
this.ImageCaptured(this, x);
}
// restart the timer
Application.DoEvents();
if (!bStopped)
this.timer1.Start();
}
catch (Exception excep)
{
MessageBox.Show("An error ocurred while capturing the video image. The video capture will now be terminated.\r\n\n" + excep.Message);
this.Stop(); // stop the process
}
}
#endregion
}
}
You declare
public delegate void WebCamEventHandler(object source, PayrollSystem.clsWebCamArgs e);
as public, meaning it should be accessible from everywhere (even from other assemblies).
But it seems (though I can't find it the code you show) that PayrollSystem.clsWebCamArgs are not declared public, and so not as accessible as the delegate.
So the compiler is giving you an error, because it's not possible to access WebCamEventHandler from other assemblies if the type of one of it's arguments is not accessible.
To solve this, you can either change the declaration of WebCamEventHandler to internal or the declaration of clsWebCamArgs to public.

Control another application using C#

I need to control other application by simulating mouse movement and keyboard input. How do I accomplish this in C#? Is it even possible?
Have you looked at White TestStack?
Sample code:
Application application = Application.Launch("foo.exe");
Window window = application.GetWindow("bar", InitializeOption.NoCache);
Button button = window.Get<Button>("save");
button.Click();
I don't think it can get better than that. The library is created by ThoughtWorks.
See "To send a keystroke to a different application" on this page:
http://msdn.microsoft.com/en-us/library/ms171548.aspx
You can use p/invoke, I stole the following code for mouse clicks in random spots on a button with a known handle:
[Flags]
public enum MouseEventFlags
{
LEFTDOWN = 0x00000002,
LEFTUP = 0x00000004,
MIDDLEDOWN = 0x00000020,
MIDDLEUP = 0x00000040,
MOVE = 0x00000001,
ABSOLUTE = 0x00008000,
RIGHTDOWN = 0x00000008,
RIGHTUP = 0x00000010
}
[StructLayout(LayoutKind.Sequential)]
public struct Rectangle
{
public int X;
public int Y;
public int Width;
public int Height;
}
private static void Click(IntPtr Handle)
{
lock (typeof(MouseAction))
{
Rectangle buttonDesign;
GetWindowRect(Handle, out buttonDesign);
Random r = new Random();
int curX = 10 + buttonDesign.X + r.Next(100 - 20);
int curY = 10 + buttonDesign.Y + r.Next(60 - 20);
SetCursorPos(curX, curY);
//Mouse Right Down and Mouse Right Up
mouse_event((uint)MouseEventFlags.LEFTDOWN, curX, curY, 0, 0);
mouse_event((uint)MouseEventFlags.LEFTUP, curX, curY, 0, 0);
}
}
[DllImport("user32.dll")]
static extern bool SetCursorPos(int X, int Y);
[DllImport("user32.dll")]
private static extern void mouse_event(
long dwFlags, long dx, long dy, long cButtons, long dwExtraInfo);
[DllImport("user32.dll")]
static extern bool GetWindowRect(IntPtr hWnd, out Rectangle rect);
you can use AutoIT, it's freeware, and it has a dll version that you can import into C# (DllImport). It allows you to click on controls, write strings to edit boxes, make combobox selections etc on another application from C#.
Use the SendMessage Native Win32 API. DllImport this method from the User32.dll. You can use this API to send both keyboard & mouse messages
I tried to do the same: to use the mouse i used this class
(you need to add using System.Runtime.InteropServices;)
public class MouseClick1 //public is important here**
{
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern void mouse_event(uint dwFlags, uint dx, uint dy, uint cButtons, uint dwExtraInfo);
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
public int CoordX { get; set; }
public int CoordY { get; set; }
public void Click1()
{
Cursor.Position = new Point(CoordX, CoordY);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
}
and after on your form1 (presuming the name is form1) you can
public partial class form1 : Form
{
MouseClick1 button1 = new MouseClick1();
MouseClick1 button2 = new MouseClick1();
[...]
public void Simulate_button1and2_Click(object sender, EventArgs e)
{
button1.CoordX = 1652; //random coordinates
button1.CoordY = 225;
button2.CoordX = 1650;
button2.CoordY = 250;
button1.Click1();
button1.Click2();
}
}
To have the coordinates on your pointer, I use a timer and a label:
private void timer1_Tick(object sender, EventArgs e)
{
Coord.Text = Cursor.Position.X.ToString() + " : " + Cursor.Position.Y.ToString(); //Premet d'avoir les coord en direct
}
Work fine with me.

Categories