I have an application I am testing out where audio is played and an action is performed when the audio is played. I have the audio captured in a progress bar which works flawlessly however after I start the program everything will work but the if statement below. Is there something I am missing? I am using NAudio to capture the audio etc as well.
All audio is going to the progress bar when selecting any audio source and will detect at the right level. When I click on a button in the Windows Form Application it right clicks fine, but when I have audio coming through past a certain level it doesn't right click again which is what I am trying to accomplish.
Here is the main code which gets the audio and the mouse buttons:
[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_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
public void DoMouseClick()
{
uint X = (uint)Cursor.Position.X;
uint Y = (uint)Cursor.Position.Y;
mouse_event(MOUSEEVENTF_RIGHTDOWN | MOUSEEVENTF_RIGHTUP, X, Y, 0, 0);
}
private WaveIn recorder;
public Form1()
{
InitializeComponent();
recorder = new WaveIn();
recorder.StartRecording();
MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active);
audioSourcesList.Items.AddRange(devices.ToArray());
}
private void timer1_Tick(object sender, EventArgs e)
{
if (audioSourcesList.SelectedItem != null)
{
var device = (MMDevice)audioSourcesList.SelectedItem;
device.AudioEndpointVolume.Mute = false;
Label.Text = (Math.Round(device.AudioMeterInformation.MasterPeakValue * 100)).ToString();
progressBar.Value = (int)(device.AudioMeterInformation.MasterPeakValue * 100);
}
}
Here is the block that starts the main program and where it seems to not work:
private void test()
{
Thread.Sleep(2500);
DoMouseClick();
if (progressBar.Value >= 5)
{
DoMouseClick();
Thread.Sleep(2000);
DoMouseClick();
}
}
I did not work with NAudio but set up the threads you need.
If the program stops when it reaches the if condition, it is because the control is for another thread and you are using it for another thread. With the BeginInvoke command, that allowed two threads to run asynchronously so that both threads had access to the required controls.
If you want more information, refer to this link Calling Synchronous Methods Asynchronously
change Form1 constructor to this
public Form1()
{
Thread thread = new Thread(delegate ()
{
recorder = new WaveIn();
recorder.StartRecording();
});
System.Timers.Timer t = new System.Timers.Timer();
t.Interval = 500;
t.Elapsed += T_Elapsed;
t.Start();
MMDeviceEnumerator enumerator = new MMDeviceEnumerator();
var devices = enumerator.EnumerateAudioEndPoints(DataFlow.All, DeviceState.Active);
audioSourcesList.Items.AddRange(devices.ToArray());
}
Delete the timer1_Tick method and add the following lines to the program
public delegate void InvokeDelegate();
private void T_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
this.BeginInvoke(new InvokeDelegate(InvokeMethod));
}
void InvokeMethod()
{
if (audioSourcesList.SelectedItem != null)
{
var device = (MMDevice)audioSourcesList.SelectedItem;
device.AudioEndpointVolume.Mute = false;
Label.Text = (Math.Round(device.AudioMeterInformation.MasterPeakValue * 100)).ToString();
progressBar.Value = (int)(device.AudioMeterInformation.MasterPeakValue * 100);
}
}
Related
This question already has an answer here:
C# Windows Forms Thread.Sleep() Doesn't work?
(1 answer)
Closed 2 years ago.
I would like to ask for help with some small C# project. I have a windows forms app, whose purpose is to write text entered by user. It basicaly consist of one text box and one button. When your press the button, the program takes the text and jumps into loop, where it always takes just one symbol and send it using SendKeys.Send(). This works without any problem, but recently I wanted to add a feature, which would allow me to stop the program with a keypress. I’m stuck at this point, because the programm is running in the "writing" loop. My only idea was to check for the keypress inside the loop, but I wasn’t able to find any other way of registering keypress, than KeyPress event. Does anyone have any idea??
my code:
public partial class Form1 : Form
{
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool RegisterHotKey(IntPtr hWnd, int id, int fsModifiers, int vk);
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern bool UnregisterHotKey(IntPtr hWnd, int id);
String textToWrite;
int delay;
bool continueWriting = true;
enum KeyModifier
{
None = 0,
Alt = 1,
Control = 2,
Shift = 4,
WinKey = 8
}
public Form1()
{
InitializeComponent();
this.KeyPreview = true;
int id = 0;
RegisterHotKey(this.Handle, id, (int)KeyModifier.Shift, Keys.A.GetHashCode());
}
private void pisBut_Click(object sender, EventArgs e)
{
textToWrite= textToWriteTextBox.Text;
Console.WriteLine(zadaniText);
continueWriting = true;
Thread.Sleep(5000);
try
{
delay = Convert.ToInt32(delayBetweenSymbolsTextBox.Text);
}
catch
{
Console.WriteLine("conversion failed!");
}
for (int i = 0; i < textToWrite.Length; i++)
{
// loop intended to take one char and send it
Random nahoda = new Random();
SendKeys.Send(zadaniText[i].ToString());
int finalDelay = nahoda.Next(delay - 40, delay);
Console.WriteLine(finalDelay);
Thread.Sleep(finalDelay);
if (continueWriting == false) // stop after setting this bool false
{
break;
}
}
}
private void zadani_PreviewKeyDown(object sender, PreviewKeyDownEventArgs e)
{
if (e.KeyCode == Keys.Enter)
{
// interrupt writing
}
}
protected override void WndProc(ref Message m)
{
base.WndProc(ref m);
if (m.Msg == 0x0312)
{
Keys key = (Keys)(((int)m.LParam >> 16) & 0xFFFF);
KeyModifier modifier = (KeyModifier)((int)m.LParam & 0xFFFF);
int id = m.WParam.ToInt32(); .
MessageBox.Show("Hotkey has been pressed!");
continueWriting = false;
}
}
private void ExampleForm_FormClosing(object sender, FormClosingEventArgs e)
{
UnregisterHotKey(this.Handle, 0);
}
}
Thanks!!
Your problem is probably related with the UI thread being blocked by repeated calls to Thread.Sleep, making the UI non-responsive. An easy way to solve this problem is be making the handler of the Click event asynchronous (adding the async modifier), and replacing the Thread.Sleep with await Task.Delay. This way the UI will remain responsive during the whole operation of sending the keys.
private async void Button_Click(object sender, EventArgs e)
{
//...
await Task.Delay(5000); // Instead of Thread.Sleep(5000);
//...
}
This may create another problem, that the user will be able to click the button again while a send-keys operation is in progress, but I am sure that you'll find a way to solve this problem. 😃
I would consider using Microsoft's Reactive Framework (aka Rx) - NuGet System.Reactive.Windows.Forms and add using System.Reactive.Linq; - then you can do this:
private IDisposable _subscription = null;
private void pisBut_Click(object sender, EventArgs e)
{
if (int.TryParse(delayBetweenSymbolsTextBox.Text, out delay))
{
Random random = new Random();
_subscription =
Observable
.Generate(
0, x => x < zadaniText.Length, x => x + 1, x => x,
x => TimeSpan.FromMilliseconds(x == 0 ? 5000.0 : random.Next(delay - 40, delay)))
.ObserveOn(this)
.Subscribe(i => SendKeys.Send(zadaniText[i].ToString()));
}
}
Now to stop the send keys midway you can do this: _subscription?.Dispose();.
I am trying to make a program (C#) which do this:
If I click my left mouse button the mouse should move to the left by a DeltaX.
The problem is that when I run the program, it simple opens the console application and nothing happens. I am not sure what I am doing wrong here.
It should keep running and checking if I click the left mouse button or not, and when I click it, the cursor should move to the left by a DeltaX.
code
using System.Data;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
namespace Teste
{
class Program
{
static void Main(string[] args)
{
var someClass = new Up();
someClass.Update();
}
}
public class Up
{
[DllImport("user32.dll")] static extern short GetAsyncKeyState(int vKey);
[DllImport("USER32.dll")] static extern short GetKeyState(int nVirtKey);
int msShootTime = 225;
System.DateTime lastClick = System.DateTime.Now;
bool isRunning = true;
public async void Update()
{
while (true)
{
if (isRunning)
{
await Task.Delay(10);
continue;
}
int res = GetKeyState((int)1);
if (res >= 0)
{
await Task.Delay(1);
continue;
}
Move(-10, 0, true);
}
}
private const int MOUSEEVENTF_LEFTDOWN = 0x02;
private const int MOUSEEVENTF_LEFTUP = 0x04;
private const int MOUSEEVENTF_RIGHTDOWN = 0x08;
private const int MOUSEEVENTF_RIGHTUP = 0x10;
[DllImport("user32.dll")]
static extern void mouse_event(int dwFlags, int dx, int dy, uint dwData, UIntPtr dwExtraInfo);
public void Move(int xDelta, int yDelta, bool pressDown = false)
{
if (pressDown)
{
if (System.DateTime.Now.Subtract(lastClick).TotalMilliseconds < msShootTime)
{
pressDown = false;
}
else
{
lastClick = System.DateTime.Now;
}
}
mouse_event(pressDown ? (MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP) : 0x0001, xDelta, yDelta, 0, UIntPtr.Zero);
}
}
}
Some mistakes were in code, I've added some comments directly to code
class Program
{
static async Task Main(string[] args) // made Main async
{
var someClass = new Up();
await someClass.Update(); // awaiting async method, you didn't wait it and app have to exit immediately
}
public class Up
{
[DllImport("user32.dll")]
private static extern short GetAsyncKeyState(int vKey);
[DllImport("user32.dll")]
private static extern short GetKeyState(int nVirtKey);
// just copied mouse_event and Flags from one of my projects but yours worked too
[DllImport("user32.dll")]
private static extern void mouse_event(MouseFlags dwFlags, int dx, int dy, uint dwData, UIntPtr dwExtraInfo);
[Flags]
private enum MouseFlags : uint
{
MOUSEEVENTF_ABSOLUTE = 0x8000, // If set, dx and dy contain normalized absolute coordinates between 0 and 65535. The event procedure maps these coordinates onto the display surface. Coordinate (0,0) maps onto the upper-left corner of the display surface, (65535,65535) maps onto the lower-right corner.
MOUSEEVENTF_LEFTDOWN = 0x0002, // The left button is down.
MOUSEEVENTF_LEFTUP = 0x0004, // The left button is up.
MOUSEEVENTF_MIDDLEDOWN = 0x0020, // The middle button is down.
MOUSEEVENTF_MIDDLEUP = 0x0040, // The middle button is up.
MOUSEEVENTF_MOVE = 0x0001, // Movement occurred.
MOUSEEVENTF_RIGHTDOWN = 0x0008, // The right button is down.
MOUSEEVENTF_RIGHTUP = 0x0010, // The right button is up.
MOUSEEVENTF_WHEEL = 0x0800, // The wheel has been moved, if the mouse has a wheel.The amount of movement is specified in dwData
MOUSEEVENTF_XDOWN = 0x0080, // An X button was pressed.
MOUSEEVENTF_XUP = 0x0100, // An X button was released.
MOUSEEVENTF_HWHEEL = 0x01000 // The wheel button is tilted.
}
int msShootTime = 225;
DateTime lastClick = DateTime.Now;
bool isRunning = false; // it was initially true
public async Task Update() // async Task
{
while (true)
{
if (isRunning) // it was always true
{
await Task.Delay(10);
continue; // this was always executed
}
isRunning = true; // added this
int res = GetKeyState((int)1);
if (res >= 0)
{
await Task.Delay(1);
isRunning = false; // added this
continue;
}
Move(-10, 0, true);
isRunning = false; // added this
}
}
public void Move(int xDelta, int yDelta, bool pressDown = false)
{
if (pressDown)
{
if (System.DateTime.Now.Subtract(lastClick).TotalMilliseconds < msShootTime)
{
pressDown = false;
}
else
{
lastClick = System.DateTime.Now;
}
}
// updated for new Flags enum
// I'm not sure if sending both MOUSEEVENTF_LEFTDOWN and MOUSEEVENTF_LEFTUP will give any effect
// try to send it sequentially with some delay: DOWN, then UP
mouse_event(pressDown ? (MouseFlags.MOUSEEVENTF_LEFTDOWN | MouseFlags.MOUSEEVENTF_LEFTUP) : MouseFlags.MOUSEEVENTF_MOVE, xDelta, yDelta, 0, UIntPtr.Zero);
}
}
}
Note: Microsoft recommends using SendInput instead of mouse_event.
This function has been superseded. Use SendInput instead.
Being new to .net, i am not able to get how to close the show dialog modal window once its open. As i have learnt we cannot close that automatically until explicitly its to be called. Here is my code:
//process - notepad.exe
Process p = Process.Start(process);
frm_Save fsave = new frm_Save();
Using (p)
{
do
{
if(!p.HasExited)
{
p.Refresh();
fsave.ShowDialog(); // it just stuck here and doesn't go to next line
}
}
while(!p.WaitForExit(1000));
}
//frm_Save.cs
public frm_Save()
{
InitializeComponent();
}
private void frm_Save_Load(...,....)
{
//
}
private void frm_Save_Shown(...,...)
{
Sleep(100);
Forms.Application.DoEvents();
Close();
}
As you have explained, you want to show a dialog with icon that you are saving a video in the background and prevent the user to do something. One regular way to do that is with a BackgroundWorker in your Dialog. Here is the code how it would work:
public class frm_Save : Form
{
public FrmProgress(List<TransferOptions> transferOptions)
{
InitializeComponent();
BackgroundWorker BgrdWorker = new System.ComponentModel.BackgroundWorker();
this.BgrdWorker.DoWork += new System.ComponentModel.DoWorkEventHandler(this.BgrdWorker_DoWork);
this.BgrdWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.BgrdWorker_RunWorkerCompleted);
}
private void FrmProgress_Load(object sender, EventArgs e)
{
// Show image and message...
}
private void BgrdWorker_DoWork(object sender, DoWorkEventArgs e)
{
// Call your video Process start Function
// after that
var stopWatch = new StopWatch();
stopWatch.Start()
while (true)
{
if (stopWatch.ElapsedMilliseconds >1000 || videoProcessHasReturnedSuccessfully)
{
break
}
}
}
private void BgrdWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
// inform the user the video processing is finished
this.Close();
}
}
Then in the main form of your console app when you want to start the whole process, you call:
frm_Save fsave = new frm_Save();
fsave.ShowDialog()
Tip: You can also use BgrdWorker.ProgressChanged to show the progress of background task to the user by communicating between the background task and the UI if necessary, but you have not requested that in your question.
This approach may work for you, note the use of TopMost.
using System.Runtime.InteropServices;
private static readonly IntPtr HWND_TOPMOST = new IntPtr(-1);
private const UInt32 SWP_NOSIZE = 0x0001;
private const UInt32 SWP_NOMOVE = 0x0002;
private const UInt32 TOPMOST_FLAGS = SWP_NOMOVE | SWP_NOSIZE;
[DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter,
int X, int Y, int cx, int cy, uint uFlags);
....
frm_Save fsave = new frm_Save();
fsave.Show();
SetWindowPos(frm_Save.Handle, HWND_TOPMOST, 0, 0, 0, 0, TOPMOST_FLAGS);
Process p = Process.Start(process);
using (p)
{
while (!p.WaitForExit(1000))
{
fsave.Refresh();
}
}
fsave.Close();
Trying to understand Timers and virtual clicks in C# Winforms. I want to have the program have an entered time value by the user (textbox1), then wait that amount of time and click the mouse, then increase the number counter (textbox2).
In the code below, the number counter immediately goes to 10, but the clicks are never ending, despite having a while loop set to stop the clicks at 10. I basically just want the program to wait a slightly random time (time entered to time entered +3), click the mouse, increase the counter, then pick a new random number and continue until 10 total clicks.
public Form1()
{
InitializeComponent();
}
private void NumbersOnly(object sender, KeyPressEventArgs e)
{
char ch = e.KeyChar;
if (!Char.IsDigit(ch) && ch != 8)
{
e.Handled = true;
}
}
static System.Timers.Timer _timer;
int numberofclicks = 0;
[DllImport("user32.dll")]
static extern void mouse_event(int dwFlags, int dx, int dy, int dwData, int dwExtraInfo);
private const int MOUSEEVENTF_MOVE = 0x0001;
private const int MOUSEEVENTF_LEFTDOWN = 0x0002;
private const int MOUSEEVENTF_LEFTUP = 0x0004;
private const int MOUSEEVENTF_RIGHTDOWN = 0x0008;
private const int MOUSEEVENTF_RIGHTUP = 0x0010;
private const int MOUSEEVENTF_MIDDLEDOWN = 0x0020;
private const int MOUSEEVENTF_MIDDLEUP = 0x0040;
private const int MOUSEEVENTF_ABSOLUTE = 0x8000;
private void StartClicked(object sender, EventArgs e)
{
numberofclicks = 0;
Random rsn = new Random();
while (numberofclicks < 10)
{
string startseconds = textBox1.Text;
int timerstartseconds = Convert.ToInt32(startseconds);
int timertime = rsn.Next(timerstartseconds * 1000, ((timerstartseconds + 3) * 1000));
_timer = new System.Timers.Timer(timertime);
_timer.Elapsed += _timer_Elapsed;
_timer.Enabled = true;
textBox2.Clear();
numberofclicks++;
string numbertextbox = numberofclicks.ToString();
textBox2.Text = numbertextbox;
}
}
void _timer_Elapsed(object sender, ElapsedEventArgs e)
{
LeftClick();
}
public static void LeftClick()
{
mouse_event(MOUSEEVENTF_LEFTDOWN, Control.MousePosition.X, Control.MousePosition.Y, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, Control.MousePosition.X, Control.MousePosition.Y, 0, 0);
}
The problem lies in a fact that StartClicked event handler does not block itself. It is just a simple method that without any delay loops 10 times changing the method level variable and modifying(even creating new!) Timer properties.
It is possible to do what you attempted to do in such a manner(single method with simple loop), but you will have to use async event handler and will have no need for a timer. And as the another answer already discusses how to do it with classic timers, I will give you such async-based solution:
private async void StartClicked(object sender, EventArgs e)
{
numberofclicks = 0;
Random rsn = new Random();
while (numberofclicks < 10)
{
string startseconds = textBox1.Text;
int timerstartseconds = Convert.ToInt32(startseconds);
int timertime = rsn.Next(timerstartseconds * 1000, ((timerstartseconds + 3) * 1000));
await Task.Delay(timertime);
LeftClick();
textBox2.Clear();
numberofclicks++;
string numbertextbox = numberofclicks.ToString();
textBox2.Text = numbertextbox;
}
}
For more information on async-await you can read MSDN on async-await.
When you say _timer.Enabled = true;, the rest of your code keeps executing. Then at some time later, when the timer ticks, the Elapsed event is triggered.
Additionally, each timer you create keeps on ticking - they don't only fire once.
Also, there are many different Timer classes in the .NET framework. For a simple winforms app like yours, I would stick with the System.Windows.Forms.Timer version. You don't have to worry about threading and such that way.
You might be better served with something like this:
private Random rsn = new Random();
private int numberofclicks = 0;
private System.Windows.Forms.Timer _timer;
// set the timer's tick event handler in form_load or similar.
// you could also just drag a timer onto the form and double-click it.
private void StartClicked(object sender, EventArgs e)
{
// don't allow starting again until finished
btnStart.Enabled = false;
numberofclicks = 0;
_timer.Interval = /* get randomish interval */
_timer.Start();
}
void _timer_Tick(object sender, EventArgs e)
{
_timer.Stop();
LeftClick();
numberofclicks++;
if (numberofclicks >= 10) {
btnStart.Enabled = true;
}
else {
_timer.Interval = /* get randomish interval */
_timer.Start();
}
}
I'm trying to get the active title bar and test it if it equals certain value, I open a new browser window. I used the code below inside a timer tick event
The problem is that on each timer tick, the Process.Start will run twice, which means I see two windows of firefox opening the page www.googlepromo.com.
public static void timerTick()
{
foreach (string s in ttlkeywords) {
if ((GetActiveWindowTitle().Contains(s)) && s.Length>2)
{
System.Diagnostics.Process.Start("http://www.googlepromo.com");
Thread.Sleep(60000);
}
}
}
The function below is the one I use to get the active title bar. I don't think the problem is in the function below but I write it anyway:
// Detect active windows Title Bar
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
private static string GetActiveWindowTitle()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
The timer :
Timer timer = new Timer();
timer.Interval = 5000;
timer.OnTimerTick += timerTick;
Thread timerThread = timer.Start();
timer.Run();
Can someone please explain why the browser windows run twice instead of just a single time ?