I am trying to record voice using NAudio in C# and I am stuck at two places:
1. A crash:
With a slightly modified form of code from THIS SO page, I am getting a NullReferenceException. Here is the crash log:
************** Exception Text **************
System.NullReferenceException: Object reference not set to an instance of an object.
at NAudio.Wave.WaveIn.Callback(IntPtr waveInHandle, WaveMessage message, IntPtr userData, WaveHeader waveHeader, IntPtr reserved)
at NAudio.Wave.WaveWindow.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
and the code is:
using System;
using System.Windows.Forms;
using System.Threading;
using NAudio.Wave;
public class FOO
{
static WaveIn s_WaveIn;
[STAThread]
static void Main(string[] args)
{
init();
Application.Run();
}
public static void record()
{
while (true)
{
Console.WriteLine("Hit Enter to START Recording.\n");
Console.ReadLine();
s_WaveIn.StartRecording();
Console.WriteLine("Hit Enter to STOP recording.\n");
Console.ReadLine();
s_WaveIn.StopRecording();
}
}
public static void DeviceInit(int rate, int channels)
{
s_WaveIn = new WaveIn();
s_WaveIn.WaveFormat = new WaveFormat(rate, channels);
s_WaveIn.BufferMilliseconds = 1000;
s_WaveIn.DataAvailable += new EventHandler<WaveInEventArgs>(SendCaptureSamples);
}
public static void init()
{
DeviceInit(44100, 2);
Thread t1 = new Thread(delegate() {
record();
});
t1.Start();
}
static void SendCaptureSamples(object sender, WaveInEventArgs e)
{
Console.WriteLine("Bytes recorded: {0}", e.BytesRecorded);
}
}
Most of the times, this happens when I start recording THIRD time. Any idea what could be causing this?
*2. Modifying rate and channels at runtime.*
In my actual code, I am resetting wave format using s_WaveIn.WaveFormat = new WaveFormat(new_rate, new_channels); before calling StartRecording(). I am not calling Dispose() because that would require resetting the DataAvailable callback, and for that, I would need another message loop. Is this approach correct or should I first call Dispose and then reinitialize s_WaveIn with new format?
Thank you.
It Seems that DataAvailable callback is getting called even when buffer is null.
I modified a function in WaveIn.cs file and its working fine now. I am not sure if this is correct, but for now, this is working for me.
private void Callback(IntPtr waveInHandle, WaveInterop.WaveMessage message, IntPtr userData, WaveHeader waveHeader, IntPtr reserved)
{
if (message == WaveInterop.WaveMessage.WaveInData)
{
GCHandle hBuffer = (GCHandle)waveHeader.userData;
WaveInBuffer buffer = (WaveInBuffer)hBuffer.Target;
if (buffer != null)
{
if (DataAvailable != null)
{
DataAvailable(this, new WaveInEventArgs(buffer.Data, buffer.BytesRecorded));
}
if (recording)
{
buffer.Reuse();
}
}
else
{
if (RecordingStopped != null)
{
RecordingStopped(this, EventArgs.Empty);
}
}
}
}
Your Main method looks odd. What is the thread for?
Just use this:
[STAThread]
static void Main(string[] args)
{
init();
Application.Run();
}
Your init method also runs the application. Why?
Try changing it to:
public static void init()
{
DeviceInit(44100, 2);
Thread t1 = new Thread(delegate() {
record();
});
t1.Start();
}
return; is not needed at the end of a method returning void.
I think, it has to be done in this way:
GCHandle hBuffer = (GCHandle)waveHeader.userData;
WaveInBuffer buffer = (WaveInBuffer)hBuffer.Target;
if (buffer == null)
{
return; // with this new line, everything works fine
}
if (DataAvailable != null)
{
DataAvailable(this, new WaveInEventArgs(buffer.Data, buffer.BytesRecorded));
}
if (recording)
{
buffer.Reuse();
}
else
{
if (RecordingStopped != null)
{
RecordingStopped(this, EventArgs.Empty);
}
}
I got the same NullReferenceException. The author did not add lines to the proposed class WaveIn.
Such use is not provided, as I understand it. Author library in the examples does not calls StopRecording (), and just stops to record incoming information, but continues to process it (for example to measure the volume), and calls StopRecording () for a complete stop to recive data to the data WaveIn.
Thus, I believe that it is necessary to use a new WaveIn after calling StopRecording ().
Related
I have a console application that contains quite a lot of threads. There are threads that monitor certain conditions and terminate the program if they are true. This termination can happen at any time.
I need an event that can be triggered when the program is closing so that I can cleanup all of the other threads and close all file handles and connections properly. I'm not sure if there is one already built into the .NET framework, so I'm asking before I write my own.
I was wondering if there was an event along the lines of:
MyConsoleProgram.OnExit += CleanupBeforeExit;
I am not sure where I found the code on the web, but I found it now in one of my old projects. This will allow you to do cleanup code in your console, e.g. when it is abruptly closed or due to a shutdown...
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler _handler;
enum CtrlType
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
private static bool Handler(CtrlType sig)
{
switch (sig)
{
case CtrlType.CTRL_C_EVENT:
case CtrlType.CTRL_LOGOFF_EVENT:
case CtrlType.CTRL_SHUTDOWN_EVENT:
case CtrlType.CTRL_CLOSE_EVENT:
default:
return false;
}
}
static void Main(string[] args)
{
// Some biolerplate to react to close window event
_handler += new EventHandler(Handler);
SetConsoleCtrlHandler(_handler, true);
...
}
Update
For those not checking the comments it seems that this particular solution does not work well (or at all) on Windows 7. The following thread talks about this
Full working example, works with ctrl-c, closing the windows with X and kill:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
namespace TestTrapCtrlC {
public class Program {
static bool exitSystem = false;
#region Trap application termination
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler _handler;
enum CtrlType {
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
private static bool Handler(CtrlType sig) {
Console.WriteLine("Exiting system due to external CTRL-C, or process kill, or shutdown");
//do your cleanup here
Thread.Sleep(5000); //simulate some cleanup delay
Console.WriteLine("Cleanup complete");
//allow main to run off
exitSystem = true;
//shutdown right away so there are no lingering threads
Environment.Exit(-1);
return true;
}
#endregion
static void Main(string[] args) {
// Some boilerplate to react to close window event, CTRL-C, kill, etc
_handler += new EventHandler(Handler);
SetConsoleCtrlHandler(_handler, true);
//start your multi threaded program here
Program p = new Program();
p.Start();
//hold the console so it doesn’t run off the end
while (!exitSystem) {
Thread.Sleep(500);
}
}
public void Start() {
// start a thread and start doing some processing
Console.WriteLine("Thread started, processing..");
}
}
}
Check also:
AppDomain.CurrentDomain.ProcessExit
I've had a similar problem, just my console App would be running in infinite loop with one preemptive statement on middle. Here is my solution:
class Program
{
static int Main(string[] args)
{
// Init Code...
Console.CancelKeyPress += Console_CancelKeyPress; // Register the function to cancel event
// I do my stuffs
while ( true )
{
// Code ....
SomePreemptiveCall(); // The loop stucks here wating function to return
// Code ...
}
return 0; // Never comes here, but...
}
static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
{
Console.WriteLine("Exiting");
// Termitate what I have to terminate
Environment.Exit(-1);
}
}
It sounds like you have the threads directly terminating the application? Perhaps it would be better to have a thread signal the main thread to say that the application should be terminated.
On receiving this signal, the main thread can cleanly shutdown the other threads and finally close itself down.
ZeroKelvin's answer works in Windows 10 x64, .NET 4.6 console app. For those who do not need to deal with the CtrlType enum, here is a really simple way to hook into the framework's shutdown:
class Program
{
private delegate bool ConsoleCtrlHandlerDelegate(int sig);
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerDelegate handler, bool add);
static ConsoleCtrlHandlerDelegate _consoleCtrlHandler;
static void Main(string[] args)
{
_consoleCtrlHandler += s =>
{
//DoCustomShutdownStuff();
return false;
};
SetConsoleCtrlHandler(_consoleCtrlHandler, true);
}
}
Returning FALSE from the handler tells the framework that we are not "handling" the control signal, and the next handler function in the list of handlers for this process is used. If none of the handlers returns TRUE, the default handler is called.
Note that when the user performs a logoff or shutdown, the callback is not called by Windows but is instead terminated immediately.
There is for WinForms apps;
Application.ApplicationExit += CleanupBeforeExit;
For Console apps, try
AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit;
But I am not sure at what point that gets called or if it will work from within the current domain. I suspect not.
Visual Studio 2015 + Windows 10
Allow for cleanup
Single instance app
Some goldplating
Code:
using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;
namespace YourNamespace
{
class Program
{
// if you want to allow only one instance otherwise remove the next line
static Mutex mutex = new Mutex(false, "YOURGUID-YOURGUID-YOURGUID-YO");
static ManualResetEvent run = new ManualResetEvent(true);
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);
private delegate bool EventHandler(CtrlType sig);
static EventHandler exitHandler;
enum CtrlType
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT = 1,
CTRL_CLOSE_EVENT = 2,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT = 6
}
private static bool ExitHandler(CtrlType sig)
{
Console.WriteLine("Shutting down: " + sig.ToString());
run.Reset();
Thread.Sleep(2000);
return false; // If the function handles the control signal, it should return TRUE. If it returns FALSE, the next handler function in the list of handlers for this process is used (from MSDN).
}
static void Main(string[] args)
{
// if you want to allow only one instance otherwise remove the next 4 lines
if (!mutex.WaitOne(TimeSpan.FromSeconds(2), false))
{
return; // singleton application already started
}
exitHandler += new EventHandler(ExitHandler);
SetConsoleCtrlHandler(exitHandler, true);
try
{
Console.BackgroundColor = ConsoleColor.Gray;
Console.ForegroundColor = ConsoleColor.Black;
Console.Clear();
Console.SetBufferSize(Console.BufferWidth, 1024);
Console.Title = "Your Console Title - XYZ";
// start your threads here
Thread thread1 = new Thread(new ThreadStart(ThreadFunc1));
thread1.Start();
Thread thread2 = new Thread(new ThreadStart(ThreadFunc2));
thread2.IsBackground = true; // a background thread
thread2.Start();
while (run.WaitOne(0))
{
Thread.Sleep(100);
}
// do thread syncs here signal them the end so they can clean up or use the manual reset event in them or abort them
thread1.Abort();
}
catch (Exception ex)
{
Console.ForegroundColor = ConsoleColor.Red;
Console.Write("fail: ");
Console.ForegroundColor = ConsoleColor.Black;
Console.WriteLine(ex.Message);
if (ex.InnerException != null)
{
Console.WriteLine("Inner: " + ex.InnerException.Message);
}
}
finally
{
// do app cleanup here
// if you want to allow only one instance otherwise remove the next line
mutex.ReleaseMutex();
// remove this after testing
Console.Beep(5000, 100);
}
}
public static void ThreadFunc1()
{
Console.Write("> ");
while ((line = Console.ReadLine()) != null)
{
if (line == "command 1")
{
}
else if (line == "command 1")
{
}
else if (line == "?")
{
}
Console.Write("> ");
}
}
public static void ThreadFunc2()
{
while (run.WaitOne(0))
{
Thread.Sleep(100);
}
// do thread cleanup here
Console.Beep();
}
}
}
The link mentioned above by Charle B in comment to flq
Deep down says:
SetConsoleCtrlHandler won't work on windows7 if you link to user32
Some where else in the thread it is suggested to crate a hidden window. So I create a winform and in onload I attached to console and execute original Main.
And then SetConsoleCtrlHandle works fine (SetConsoleCtrlHandle is called as suggested by flq)
public partial class App3DummyForm : Form
{
private readonly string[] _args;
public App3DummyForm(string[] args)
{
_args = args;
InitializeComponent();
}
private void App3DummyForm_Load(object sender, EventArgs e)
{
AllocConsole();
App3.Program.OriginalMain(_args);
}
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool AllocConsole();
}
For those interested in VB.net. (I searched the internet and couldn't find an equivalent for it) Here it is translated into vb.net.
<DllImport("kernel32")> _
Private Function SetConsoleCtrlHandler(ByVal HandlerRoutine As HandlerDelegate, ByVal Add As Boolean) As Boolean
End Function
Private _handler As HandlerDelegate
Private Delegate Function HandlerDelegate(ByVal dwControlType As ControlEventType) As Boolean
Private Function ControlHandler(ByVal controlEvent As ControlEventType) As Boolean
Select Case controlEvent
Case ControlEventType.CtrlCEvent, ControlEventType.CtrlCloseEvent
Console.WriteLine("Closing...")
Return True
Case ControlEventType.CtrlLogoffEvent, ControlEventType.CtrlBreakEvent, ControlEventType.CtrlShutdownEvent
Console.WriteLine("Shutdown Detected")
Return False
End Select
End Function
Sub Main()
Try
_handler = New HandlerDelegate(AddressOf ControlHandler)
SetConsoleCtrlHandler(_handler, True)
.....
End Sub
I'm navigating through a webapp using WatiN and I needed to download a document. However, I've found out that WatiN doesn't support the download with Internet Explorer 11. That's why I'm trying to do it using the method described here :
How to enable automatic downloads in IE11 (the top answer)
Using User32.dll SendMessage To Send Keys With ALT Modifier
Basically I'm calling user32.dll to handle the small popup DL window (F6 to select it, tab, enter, etc.)
However nothing happens, the popup isn't responding to my commands.
Is this the right way to do it ?
My code looks like this :
//Before my method
[DllImport("user32.dll")]
public static extern IntPtr SetActiveWindow(IntPtr hWnd);
[DllImport("user32.dll")]
public static extern int SendMessage(IntPtr hWnd, int Msg, uint wParam, uint lParam);
private void button1_Click(object sender, EventArgs e)
{
//some code....
IE myPopup = IE.AttachTo<IE>(Find.ByUrl("http://abcd.do"));
FileDownloadHandler fileDownloadHandler = new FileDownloadHandler("test");
myPopup.AddDialogHandler(fileDownloadHandler);
myPopup.Link(Find.ByUrl("httpabcdefg.do")).ClickNoWait();
ushort action = (ushort)260; //WM_SYSKEYDOWN
System.Threading.Thread.Sleep(2000);
ushort key = (ushort)System.Windows.Forms.Keys.F6;
ushort key2 = (ushort)System.Windows.Forms.Keys.Tab;
ushort key3 = (ushort)System.Windows.Forms.Keys.Enter;
ushort key4 = (ushort)System.Windows.Forms.Keys.Right;
SendMessage(myPopup.hWnd, action, key, 0);
SendMessage(myPopup.hWnd, action, key2, 0);
SendMessage(myPopup.hWnd, action, key3, 0);
SendMessage(myPopup.hWnd, action, key4, 0);
SendMessage(myPopup.hWnd, action, key4, 0);
SendMessage(myPopup.hWnd, action, key3, 0);
}
I know the popup (myPopup) is handled correctly, I can click on it using watin.
A couple of things are unclear to me, and I put the action WM_SYSKEYDOWN as default, to follow the example cited above.
Thanks in advance for any kind of help !
--------------EDIT-----------------
I managed to do it, I know it's not optimal at all but I dropped the Dllimport and the SendMessage all together and used SendKeys.Send instead. Just like that :
private void button1_Click(object sender, EventArgs e)
{
//some code....
IE myPopup = IE.AttachTo<IE>(Find.ByUrl("http://abcd.do"));
FileDownloadHandler fileDownloadHandler = new FileDownloadHandler("test");
myPopup.AddDialogHandler(fileDownloadHandler);
myPopup.Link(Find.ByUrl("httpabcdefg.do")).ClickNoWait();
SendKeys.Send("{F6}");
SendKeys.Send("{ENTER}");
SendKeys.Send("{RIGHT}");
SendKeys.Send("{RIGHT}");
SendKeys.Send("{ENTER}");
}
The code that you are using is not a proper way to automate and WatiN does not completely interact with Windows controls and help in windows is very little. I had the same problem in handling the windows controls as we had multiple versions of IE. IE 9.0 and higher have different file handling when compared to <= IE 8.0 version. The below code works fine for IE 9.0 and higher. Please make sure proper references are added (refer using's).
Refer below code and modify it as per you requirement.
using System.Threading;
using System.Windows.Automation;
using WatiN.Core;
using WatiN.Core.Native.Windows;
namespace TestFramework.Util
{
public static class WindowsHelper
{
#region Public Methods
/// <summary>
/// Download IE file.
/// </summary>
/// <param name="action">Action can be Save/Save As/Open/Cancel.</param>
/// <param name="path">Path where file needs to be saved (for Save As function).</param>
public static void DownloadIEFile(string action, string path = "", string regexPatternToMatch = "")
{
Browser browser = null;
if (Utility.Browser != null) // Utility.Browser is my WatiN browser instance.
{
if (string.IsNullOrEmpty(regexPatternToMatch))
{
browser = Utility.Browser;
}
else
{
Utility.Wait(() => (browser = Browser.AttachTo<IE>(Find.ByUrl(new System.Text.RegularExpressions.Regex(regexPatternToMatch)))) != null);
}
}
else
{
return;
}
// If doesn't work try to increase sleep interval or write your own waitUntill method
Thread.Sleep(3000);
// See information here (http://msdn.microsoft.com/en-us/library/windows/desktop/ms633515(v=vs.85).aspx)
Window windowMain = null;
Utility.Wait(() => (windowMain = new Window(NativeMethods.GetWindow(browser.hWnd, 5))).ProcessID != 0);
TreeWalker trw = new TreeWalker(Condition.TrueCondition);
AutomationElement mainWindow = trw.GetParent(AutomationElement.FromHandle(browser.hWnd));
Window windowDialog = null;
Utility.Wait(() => (windowDialog = new Window(NativeMethods.GetWindow(windowMain.Hwnd, 5))).ProcessID != 0);
windowDialog.SetActivate();
AutomationElementCollection amc = null;
Utility.Wait(() => (amc = AutomationElement.FromHandle(windowDialog.Hwnd).FindAll(TreeScope.Children, Condition.TrueCondition)).Count > 1);
foreach (AutomationElement element in amc)
{
// You can use "Save ", "Open", ''Cancel', or "Close" to find necessary button Or write your own enum
if (element.Current.Name.Equals(action))
{
// If doesn't work try to increase sleep interval or write your own waitUntil method
// WaitUntilButtonExsist(element,100);
Thread.Sleep(1000);
AutomationPattern[] pats = element.GetSupportedPatterns();
// Replace this for each if you need 'Save as' with code bellow
foreach (AutomationPattern pat in pats)
{
// '10000' button click event id
if (pat.Id == 10000)
{
InvokePattern click = (InvokePattern)element.GetCurrentPattern(pat);
click.Invoke();
}
}
}
else if (element.Current.Name.Equals("Save") && action == "Save As")
{
AutomationElementCollection bmc = element.FindAll(TreeScope.Children, Automation.ControlViewCondition);
InvokePattern click1 = (InvokePattern)bmc[0].GetCurrentPattern(AutomationPattern.LookupById(10000));
click1.Invoke();
Thread.Sleep(1000);
AutomationElementCollection main = mainWindow.FindAll(TreeScope.Children, Condition.TrueCondition);
foreach (AutomationElement el in main)
{
if (el.Current.LocalizedControlType == "menu")
{
// First array element 'Save', second array element 'Save as', third second array element 'Save and open'
InvokePattern clickMenu = (InvokePattern)
el.FindAll(TreeScope.Children, Condition.TrueCondition)[1].GetCurrentPattern(AutomationPattern.LookupById(10000));
clickMenu.Invoke();
Thread.Sleep(1000);
ControlSaveDialog(mainWindow, path);
break;
}
}
}
}
}
/// <summary>
/// Control for save dialog.
/// </summary>
/// <param name="mainWindow">Main window.</param>
/// <param name="path">Path.</param>
private static void ControlSaveDialog(AutomationElement mainWindow, string path)
{
// Obtain the save as dialog
var saveAsDialog = mainWindow
.FindFirst(TreeScope.Descendants,
new PropertyCondition(AutomationElement.NameProperty, "Save As"));
// Get the file name box
var saveAsText = saveAsDialog
.FindFirst(TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.NameProperty, "File name:"),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Edit)))
.GetCurrentPattern(ValuePattern.Pattern) as ValuePattern;
// Fill the filename box
saveAsText.SetValue(path);
Thread.Sleep(500);
Utility.PressKey("LEFT");
Utility.PressKey("LEFT");
Thread.Sleep(1000);
// Find the save button
var saveButton =
saveAsDialog.FindFirst(TreeScope.Descendants,
new AndCondition(
new PropertyCondition(AutomationElement.NameProperty, "Save"),
new PropertyCondition(AutomationElement.ControlTypeProperty, ControlType.Button)));
// Invoke the button
var pattern = saveButton.GetCurrentPattern(InvokePattern.Pattern) as InvokePattern;
pattern.Invoke();
}
#endregion
}
}
public static class Utility
{
public static IE Browser { get; set; }
// Wait specified number of seconds
public static void Wait(int seconds)
{
System.Threading.Thread.Sleep(seconds * 1000);
}
// Wait for condition to evaluate true, timeout after 30 seconds
public static void Wait(Func<bool> condition)
{
int count = 0;
while (!condition() && count < 30)
{
System.Threading.Thread.Sleep(1000);
count++;
}
}
//Send tab key press to browser
public static void PressTab()
{
System.Windows.Forms.SendKeys.SendWait("{TAB}");
System.Threading.Thread.Sleep(300);
}
//Send specified key press to browser
public static void PressKey(string keyname)
{
System.Windows.Forms.SendKeys.SendWait("{" + keyname.ToUpper() + "}");
System.Threading.Thread.Sleep(300);
}
}
a program of mine uses AxShockwaveFlash component used as stream player.
The problem is that my code works with most stream-providers (livestream, ustream, own3d.tv) but Justin.TV's player is somewhat problematic.
Before moving on the actual problem let me summarize my code;
Inherited FlashControl - this allows me to override the flashplayer's built-in menu:
public class FlashPlayer : AxShockwaveFlashObjects.AxShockwaveFlash // Customized Flash Player.
{
private const int WM_MOUSEMOVE = 0x0200;
private const int WM_MOUSEWHEEL = 0x020A;
private const int WM_LBUTTONDOWN = 0x0201;
private const int WM_LBUTTONUP = 0x0202;
private const int WM_LBUTTONDBLCLK = 0x0203;
private const int WM_RBUTTONDOWN = 0x0204;
private const int WM_RBUTTONUP = 0x0205;
public new event MouseEventHandler DoubleClick;
public new event MouseEventHandler MouseDown;
public new event MouseEventHandler MouseUp;
public new event MouseEventHandler MouseMove;
public FlashPlayer():base()
{
this.HandleCreated += FlashPlayer_HandleCreated;
}
void FlashPlayer_HandleCreated(object sender, EventArgs e)
{
this.AllowFullScreen = "true";
this.AllowNetworking = "all";
this.AllowScriptAccess = "always";
}
protected override void WndProc(ref Message m) // Override's the WndProc and disables Flash activex's default right-click menu and if exists shows the attached ContextMenuStrip.
{
if (m.Msg == WM_LBUTTONDOWN)
{
if (this.MouseDown != null) this.MouseDown(this, new MouseEventArgs(System.Windows.Forms.MouseButtons.Left, 1, Cursor.Position.X, Cursor.Position.Y, 0));
}
else if (m.Msg == WM_LBUTTONUP)
{
if (this.MouseUp != null) this.MouseUp(this, new MouseEventArgs(System.Windows.Forms.MouseButtons.None, 0, Cursor.Position.X, Cursor.Position.Y, 0));
}
else if (m.Msg == WM_MOUSEMOVE)
{
if (this.MouseMove != null) this.MouseMove(this, new MouseEventArgs(System.Windows.Forms.MouseButtons.None, 0, Cursor.Position.X, Cursor.Position.Y, 0));
}
else if (m.Msg == WM_RBUTTONDOWN)
{
if (this.ContextMenuStrip != null) this.ContextMenuStrip.Show(Cursor.Position.X, Cursor.Position.Y);
m.Result = IntPtr.Zero;
return;
}
else if (m.Msg == WM_LBUTTONDBLCLK)
{
if (this.DoubleClick != null) this.DoubleClick(this, new MouseEventArgs(System.Windows.Forms.MouseButtons.Left, 2, Cursor.Position.X, Cursor.Position.Y, 0));
m.Result = IntPtr.Zero;
return;
}
base.WndProc(ref m);
}
}
Player window code: (Player is an instance of FlashPlayer)
private void Player_Load(object sender, EventArgs e)
{
try
{
this.Text = string.Format("Stream: {0}", this._stream.Name); // set the window title.
this.Player.LoadMovie(0, this._stream.Movie); // load the movie.
if (this._stream.ChatAvailable && Settings.Instance.AutomaticallyOpenChat) this.OpenChatWindow();
}
catch (Exception exc)
{
// log stuff.
}
}
So this works great for livestream.com, ustream.com, own3d.tv but when it come's to justin.tv's player i'm getting a 1337 error (invalid embed code). So i tried to ask them for support but could't get a valid answer.
_stream.movie variable actually holds a valid URL for the stream source like;
http://cdn.livestream.com/grid/LSPlayer.swf?channel=%slug%&autoPlay=true (livestream sample)
or
http://www.justin.tv/widgets/live_embed_player.swf?channel=%slug%&auto_play=true&start_volume=100 (justin.tv sample)
Tried to urlencode the 'channel=%slug%&auto_play=true&start_volume=100' part for justin.tv but that did not work also.
So i started trying some work-arounds which at first place i thought setting flashVars variable of the control.
But i've a strange problem there, whenever i try to set flashVars variable it never get's set. I found a sample screenshot on the issue;
So if i was able to set the flashVariables may be i could work-around the justin.tv player's error. Btw, i also tried setting variables using Player.SetVariable(key,value) - that didn't work also.
Notes:
I'm running on .net 4.0 client profile.
Using the Flash10l.ocx.
Have generated the AxShockwaveFlashObjects.dll, ShockwaveFlashObjects.dll wrappers using "aximp.exe –source "C:\WINDOWS\system32\Macromed\Flash\Flash10l.ocx"
I recently had an issue with making justin.tv work, but in the end it was as simple as
axShockwaveFlash1.FlashVars = "auto_play=true&channel=adventuretimed&start_volume=25";
axShockwaveFlash1.Movie = "http://www.justin.tv/widgets/live_embed_player.swf";
and it works perfectly
How can i detect where the taskbar is located? I need to know for displaying my notification in the right corner. Thanks
Edit:
Thank you Hans Passant. I used that with this to get location. I hope is ok.
GetTaskbarLocation(TaskbarPosition.GetTaskbarPosition());
private void GetTaskbarLocation(Rectangle rc)
{
if (rc.X == rc.Y)
{
if (rc.Right < rc.Bottom)
taskbarLocation = TaskbarLocation.Left;
if (rc.Right > rc.Bottom)
taskbarLocation = TaskbarLocation.Top;
}
if (rc.X > rc.Y)
taskbarLocation = TaskbarLocation.Right;
if (rc.X < rc.Y)
taskbarLocation = TaskbarLocation.Bottom;
}
public static Rectangle GetTaskbarPosition() {
var data = new APPBARDATA();
data.cbSize = System.Runtime.InteropServices.Marshal.SizeOf(data);
IntPtr retval = SHAppBarMessage(ABM_GETTASKBARPOS, ref data);
if (retval == IntPtr.Zero) throw new Win32Exception("Please re-install Windows");
return new Rectangle(data.rc.left, data.rc.top,
data.rc.right - data.rc.left, data.rc.bottom - data.rc.top);
}
// P/Invoke goo:
private const int ABM_GETTASKBARPOS = 5;
[System.Runtime.InteropServices.DllImport("shell32.dll")]
private static extern IntPtr SHAppBarMessage(int msg, ref APPBARDATA data);
private struct APPBARDATA {
public int cbSize;
public IntPtr hWnd;
public int uCallbackMessage;
public int uEdge;
public RECT rc;
public IntPtr lParam;
}
private struct RECT {
public int left, top, right, bottom;
}
SHAppBarMessage(ABM_GETTASKBARPOS)
See the SHAppBarMessage Function and the ABM_GETTASKBARPOS Message for more info and the pinvoke page for SHAppBarMessage has a VB.Net sample that shouldn't be too difficult to translate.
The SHAppBarMessage function will return you information about the taskbar if you pass in the ABM_GETTASKBARPOS message. It has an out parameter which is a pointer to APPBARDATA that contains the screen cooridinates of the task bar. You can use to work out where on screen it is.
It's probably best to use the available API: NotifyIcon.ShowBalloonTip:
void Form1_DoubleClick(object sender, EventArgs e)
{
notifyIcon1.Visible = true;
notifyIcon1.ShowBalloonTip(20000, "Information", "This is the text",
ToolTipIcon.Info );
}
In Java, using JNA (adapted from other C# solutions above)
public static Rectangle getTaskbarPosition() throws Exception {
APPBARDATA data = new APPBARDATA();
data.cbSize = new WinDef.DWORD(data.size());
WinDef.UINT_PTR retval = Shell32.INSTANCE.SHAppBarMessage(ABM_GETTASKBARPOS, data);
if (retval == null) {
throw new Exception("Please re-install Windows");
}
return new Rectangle(data.rc.left, data.rc.top, data.rc.right - data.rc.left, data.rc.bottom - data.rc.top);
}
How can one determine, in code, how long the machine is locked?
Other ideas outside of C# are also welcome.
I like the windows service idea (and have accepted it) for simplicity and cleanliness, but unfortunately I don't think it will work for me in this particular case. I wanted to run this on my workstation at work rather than home (or in addition to home, I suppose), but it's locked down pretty hard courtesy of the DoD. That's part of the reason I'm rolling my own, actually.
I'll write it up anyway and see if it works. Thanks everyone!
I hadn't found this before, but from any application you can hookup a SessionSwitchEventHandler. Obviously your application will need to be running, but so long as it is:
Microsoft.Win32.SystemEvents.SessionSwitch += new Microsoft.Win32.SessionSwitchEventHandler(SystemEvents_SessionSwitch);
void SystemEvents_SessionSwitch(object sender, Microsoft.Win32.SessionSwitchEventArgs e)
{
if (e.Reason == SessionSwitchReason.SessionLock)
{
//I left my desk
}
else if (e.Reason == SessionSwitchReason.SessionUnlock)
{
//I returned to my desk
}
}
I would create a Windows Service (a visual studio 2005 project type) that handles the OnSessionChange event as shown below:
protected override void OnSessionChange(SessionChangeDescription changeDescription)
{
if (changeDescription.Reason == SessionChangeReason.SessionLock)
{
//I left my desk
}
else if (changeDescription.Reason == SessionChangeReason.SessionUnlock)
{
//I returned to my desk
}
}
What and how you log the activity at that point is up to you, but a Windows Service provides quick and easy access to windows events like startup, shutdown, login/out, along with the lock and unlock events.
The solution below uses the Win32 API. OnSessionLock is called when the workstation is locked, and OnSessionUnlock is called when it is unlocked.
[DllImport("wtsapi32.dll")]
private static extern bool WTSRegisterSessionNotification(IntPtr hWnd,
int dwFlags);
[DllImport("wtsapi32.dll")]
private static extern bool WTSUnRegisterSessionNotification(IntPtr
hWnd);
private const int NotifyForThisSession = 0; // This session only
private const int SessionChangeMessage = 0x02B1;
private const int SessionLockParam = 0x7;
private const int SessionUnlockParam = 0x8;
protected override void WndProc(ref Message m)
{
// check for session change notifications
if (m.Msg == SessionChangeMessage)
{
if (m.WParam.ToInt32() == SessionLockParam)
OnSessionLock(); // Do something when locked
else if (m.WParam.ToInt32() == SessionUnlockParam)
OnSessionUnlock(); // Do something when unlocked
}
base.WndProc(ref m);
return;
}
void OnSessionLock()
{
Debug.WriteLine("Locked...");
}
void OnSessionUnlock()
{
Debug.WriteLine("Unlocked...");
}
private void Form1Load(object sender, EventArgs e)
{
WTSRegisterSessionNotification(this.Handle, NotifyForThisSession);
}
// and then when we are done, we should unregister for the notification
// WTSUnRegisterSessionNotification(this.Handle);
I know this is an old question but i have found a method to get the Lock State for a given session.
I found my answer here but it was in C++ so i translated as much as i can to C# to get the Lock State.
So here goes:
static class SessionInfo {
private const Int32 FALSE = 0;
private static readonly IntPtr WTS_CURRENT_SERVER = IntPtr.Zero;
private const Int32 WTS_SESSIONSTATE_LOCK = 0;
private const Int32 WTS_SESSIONSTATE_UNLOCK = 1;
private static bool _is_win7 = false;
static SessionInfo() {
var os_version = Environment.OSVersion;
_is_win7 = (os_version.Platform == PlatformID.Win32NT && os_version.Version.Major == 6 && os_version.Version.Minor == 1);
}
[DllImport("wtsapi32.dll")]
private static extern Int32 WTSQuerySessionInformation(
IntPtr hServer,
[MarshalAs(UnmanagedType.U4)] UInt32 SessionId,
[MarshalAs(UnmanagedType.U4)] WTS_INFO_CLASS WTSInfoClass,
out IntPtr ppBuffer,
[MarshalAs(UnmanagedType.U4)] out UInt32 pBytesReturned
);
[DllImport("wtsapi32.dll")]
private static extern void WTSFreeMemoryEx(
WTS_TYPE_CLASS WTSTypeClass,
IntPtr pMemory,
UInt32 NumberOfEntries
);
private enum WTS_INFO_CLASS {
WTSInitialProgram = 0,
WTSApplicationName = 1,
WTSWorkingDirectory = 2,
WTSOEMId = 3,
WTSSessionId = 4,
WTSUserName = 5,
WTSWinStationName = 6,
WTSDomainName = 7,
WTSConnectState = 8,
WTSClientBuildNumber = 9,
WTSClientName = 10,
WTSClientDirectory = 11,
WTSClientProductId = 12,
WTSClientHardwareId = 13,
WTSClientAddress = 14,
WTSClientDisplay = 15,
WTSClientProtocolType = 16,
WTSIdleTime = 17,
WTSLogonTime = 18,
WTSIncomingBytes = 19,
WTSOutgoingBytes = 20,
WTSIncomingFrames = 21,
WTSOutgoingFrames = 22,
WTSClientInfo = 23,
WTSSessionInfo = 24,
WTSSessionInfoEx = 25,
WTSConfigInfo = 26,
WTSValidationInfo = 27,
WTSSessionAddressV4 = 28,
WTSIsRemoteSession = 29
}
private enum WTS_TYPE_CLASS {
WTSTypeProcessInfoLevel0,
WTSTypeProcessInfoLevel1,
WTSTypeSessionInfoLevel1
}
public enum WTS_CONNECTSTATE_CLASS {
WTSActive,
WTSConnected,
WTSConnectQuery,
WTSShadow,
WTSDisconnected,
WTSIdle,
WTSListen,
WTSReset,
WTSDown,
WTSInit
}
public enum LockState {
Unknown,
Locked,
Unlocked
}
[StructLayout(LayoutKind.Sequential)]
private struct WTSINFOEX {
public UInt32 Level;
public UInt32 Reserved; /* I have observed the Data field is pushed down by 4 bytes so i have added this field as padding. */
public WTSINFOEX_LEVEL Data;
}
[StructLayout(LayoutKind.Sequential)]
private struct WTSINFOEX_LEVEL {
public WTSINFOEX_LEVEL1 WTSInfoExLevel1;
}
[StructLayout(LayoutKind.Sequential)]
private struct WTSINFOEX_LEVEL1 {
public UInt32 SessionId;
public WTS_CONNECTSTATE_CLASS SessionState;
public Int32 SessionFlags;
/* I can't figure out what the rest of the struct should look like but as i don't need anything past the SessionFlags i'm not going to. */
}
public static LockState GetSessionLockState(UInt32 session_id) {
IntPtr ppBuffer;
UInt32 pBytesReturned;
Int32 result = WTSQuerySessionInformation(
WTS_CURRENT_SERVER,
session_id,
WTS_INFO_CLASS.WTSSessionInfoEx,
out ppBuffer,
out pBytesReturned
);
if (result == FALSE)
return LockState.Unknown;
var session_info_ex = Marshal.PtrToStructure<WTSINFOEX>(ppBuffer);
if (session_info_ex.Level != 1)
return LockState.Unknown;
var lock_state = session_info_ex.Data.WTSInfoExLevel1.SessionFlags;
WTSFreeMemoryEx(WTS_TYPE_CLASS.WTSTypeSessionInfoLevel1, ppBuffer, pBytesReturned);
if (_is_win7) {
/* Ref: https://msdn.microsoft.com/en-us/library/windows/desktop/ee621019(v=vs.85).aspx
* Windows Server 2008 R2 and Windows 7: Due to a code defect, the usage of the WTS_SESSIONSTATE_LOCK
* and WTS_SESSIONSTATE_UNLOCK flags is reversed. That is, WTS_SESSIONSTATE_LOCK indicates that the
* session is unlocked, and WTS_SESSIONSTATE_UNLOCK indicates the session is locked.
* */
switch (lock_state) {
case WTS_SESSIONSTATE_LOCK:
return LockState.Unlocked;
case WTS_SESSIONSTATE_UNLOCK:
return LockState.Locked;
default:
return LockState.Unknown;
}
}
else {
switch (lock_state) {
case WTS_SESSIONSTATE_LOCK:
return LockState.Locked;
case WTS_SESSIONSTATE_UNLOCK:
return LockState.Unlocked;
default:
return LockState.Unknown;
}
}
}
}
Note: The above code was extracted from a much larger project so if i missed a bit sorry. I havn't got time to test the above code but plan to come back in a week or two to check everything. I only posted it now because i didn't want to forget to do it.
NOTE: This is not an answer, but a (contribution) to Timothy Carter answer, because my reputation doesn't allow me to comment so far.
Just in case somebody tried the code from Timothy Carter's answer and did not get it to work right away in a Windows service, there's one property that need to be set to true in the constructor of the service.
Just add the line in the constructor:
CanHandleSessionChangeEvent = true;
And be sure not to set this property after the service is started otherwise an InvalidOperationException will be thrown.
If you're interested in writing a windows-service to "find" these events, topshelf (the library/framework that makes writing windows services much easier) has a hook.
public interface IMyServiceContract
{
void Start();
void Stop();
void SessionChanged(Topshelf.SessionChangedArguments args);
}
public class MyService : IMyServiceContract
{
public void Start()
{
}
public void Stop()
{
}
public void SessionChanged(SessionChangedArguments e)
{
Console.WriteLine(e.ReasonCode);
}
}
and now the code to wire up the topshelf service to the interface/concrete above
Everything below is "typical" topshelf setup.... except for 2 lines which I marked as
/* THIS IS MAGIC LINE */
Those are what get the SessionChanged method to fire.
I tested this with windows 10 x64. I locked and unlocked my machine and I got the desired result.
IMyServiceContract myServiceObject = new MyService(); /* container.Resolve<IMyServiceContract>(); */
HostFactory.Run(x =>
{
x.Service<IMyServiceContract>(s =>
{
s.ConstructUsing(name => myServiceObject);
s.WhenStarted(sw => sw.Start());
s.WhenStopped(sw => sw.Stop());
s.WhenSessionChanged((csm, hc, chg) => csm.SessionChanged(chg)); /* THIS IS MAGIC LINE */
});
x.EnableSessionChanged(); /* THIS IS MAGIC LINE */
/* use command line variables for the below commented out properties */
/*
x.RunAsLocalService();
x.SetDescription("My Description");
x.SetDisplayName("My Display Name");
x.SetServiceName("My Service Name");
x.SetInstanceName("My Instance");
*/
x.StartManually(); // Start the service manually. This allows the identity to be tweaked before the service actually starts
/* the below map to the "Recover" tab on the properties of the Windows Service in Control Panel */
x.EnableServiceRecovery(r =>
{
r.OnCrashOnly();
r.RestartService(1); ////first
r.RestartService(1); ////second
r.RestartService(1); ////subsequents
r.SetResetPeriod(0);
});
x.DependsOnEventLog(); // Windows Event Log
x.UseLog4Net();
x.EnableShutdown();
x.OnException(ex =>
{
/* Log the exception */
/* not seen, I have a log4net logger here */
});
});
My packages.config to provide hints about versions:
<package id="log4net" version="2.0.5" targetFramework="net45" />
<package id="Topshelf" version="4.0.3" targetFramework="net461" />
<package id="Topshelf.Log4Net" version="4.0.3" targetFramework="net461" />
In Windows Task Scheduler, you could create tasks that trigger on workstation lock and on workstation unlock. Each task could write a flag and timestamp to a file to state if the workstation is locked or unlocked and when it happened.
I realize that this is not a programmatic way. It is simpler than writing a service. It won't miss an event because your program happens to not be running at the time of lock/unlock transition.
Below is the 100% working code to find if the PC is locked or not.
Before using this use the namespace System.Runtime.InteropServices.
[DllImport("user32", EntryPoint = "OpenDesktopA", CharSet = CharSet.Ansi,SetLastError = true, ExactSpelling = true)]
private static extern Int32 OpenDesktop(string lpszDesktop, Int32 dwFlags, bool fInherit, Int32 dwDesiredAccess);
[DllImport("user32", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
private static extern Int32 CloseDesktop(Int32 hDesktop);
[DllImport("user32", CharSet = CharSet.Ansi,SetLastError = true,ExactSpelling = true)]
private static extern Int32 SwitchDesktop(Int32 hDesktop);
public static bool IsWorkstationLocked()
{
const int DESKTOP_SWITCHDESKTOP = 256;
int hwnd = -1;
int rtn = -1;
hwnd = OpenDesktop("Default", 0, false, DESKTOP_SWITCHDESKTOP);
if (hwnd != 0)
{
rtn = SwitchDesktop(hwnd);
if (rtn == 0)
{
// Locked
CloseDesktop(hwnd);
return true;
}
else
{
// Not locked
CloseDesktop(hwnd);
}
}
else
{
// Error: "Could not access the desktop..."
}
return false;
}