How can i trap a Windows system message (like WM_SETTEXT) that was sent by some window (VLC player window in my case)? I've tried to inherit NativeWindow class and override WndProc like this:
class VLCFilter : NativeWindow
{
System.IntPtr iHandle;
const int WM_SETTEXT = 0x000C;
public VLCFilter()
{
Process p = Process.GetProcessesByName("vlc")[0];
iHandle = p.MainWindowHandle;
}
protected override void WndProc(ref Message aMessage)
{
base.WndProc(ref aMessage);
if (aMessage.HWnd != iHandle)
return false;
if (aMessage.Msg == WM_SETTEXT)
{
MessageBox.Show("VLC window text changed!");
}
}
}
I have checked with Microsoft Spy++ that WM_SETTEXT message is sent by VLC player but my code doesn't seem to get the work done. I've refered mainly to:
http://www.codeproject.com/kb/dotnet/devicevolumemonitor.aspx
I'm trying to make this work for some time with no success. What am I doing wrong? What I am not doing? Maybe there is easier way to do this?
My initial goal is to catch when VLC player (that could be playing somewhere in the background and is not emmbed in my application) repeats its playback (have noticed that WM_SETTEXT message is sent then and I'm trying to find it out like this).
Is your code even being reached? I'm guessing you've inherited from NativeWindow but haven't made your actual windows inherit from your VLCFilter class. Which is in fact going to be a really difficult thing because you'll probably have to rewrite System.Windows.Forms.Form... (I'm guessing there's inheritance in there, but honestly not sure the internal structure in the framework.)
Perhaps you should inherit from Form instead and then have your forms inherit from your new class instead of Form?
I suppose, you could use hook techniques. It's designed for such cases.
Also, this links could be useful, despite they are easiely googled.
http://www.codeproject.com/KB/cs/netwin32hooks.aspx
http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx
Hello and thanks for Your answers. ;)
Following the: http://www.codeproject.com/KB/system/WilsonSystemGlobalHooks.aspx did the trick and now I'm hooked up to event i wanted. Everything works fine, there's just one glitch: when ovverriding WndProc it starts recieving messages as soon as form is created. Is there a way to temporarily disable WndProc from recieving those messages and enable only when i want to get them?
Do your stuff before the call to the base implementation, else values in Message could have changed.
Somewhere in your code, you should be making a call to NativeWindow.AssignHandle. If you aren't (or if you're passing the wrong handle), then your overridden WndProc won't be called.
Edit: However, because VLC is running in a separate process, this technique won't work. The documentation for the NativeWindow.AssignHandle method states:
Note:
The handle to assign cannot be in a different application process.
Related
I have a Windows Desktop App with an auto-update method implemented. I want to show a custom Form (because I need to change the button texts) asking the user if he or she wants to download the new version or not when a new update is detected and I want to "block" all input actions in the Desktop App until the user has made his selection.
After reading Form.ShowDialog() documentation and several topics here saying "ShowDialog() is not making my windows modal" and several answers replying "You need to properly set the owner" I still don't understand how to set this owner. Of course if I make two forms and the first one shows the second, I can "block" the first one doing:
secondForm.ShowDialog(firstForm);
But I don't know how to make that the firstForm blocks all the application to prevent the user using a deprecated version of it.
I tried several approaches like getting the current id process (or trying to get it) and convert it to IWin32Window. But nothing seemed to work.
If you need it, I add here the code I'm using:
FormAsk formAsk = new FormAsk (param1, param2);
formAsk.StartPosition = FormStartPosition.CenterParent;
formAsk.TopLevel = true;
formAsk.TopMost = true;
formAsk.DialogResult = formAsk .ShowDialog();
if(formAsk.DialogResult.Equals(DialogResult.OK))
{
// do stuff
}
else
{
// do other stuff
}
I've also seen lots of solution implementing:
myForm.ShowDialog(this);
But VS start's crying because the types are not compatible. I'm using a MVVM pattern so I can't just set a Form as an owner because I don't have a main form. Just views in .xaml and views controllers in c#.
Edit: I would like to add few comments I learned after facing this issue that may help to understand better my situation.
The main method of the program executes the following:
[STAThread]
static void Main()
{
//stuff
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
try
{
//stuff
STAApplicationContext context = new STAApplicationContext();
Application.Run(context);
}
catch (Exception exc)
{
MessageBox.Show(exc.Message, Localization.Strings.error_popup_title);
Application.Exit();
}
}
}
And this context is the one that generates the service layer and views manager of the application. If I display the Forms in the service layer, showDialog() method can not "block" the input in the views but if I display them from the views (generated and handled by the view manager) i can. There's a communication between views and service, because actions triggered in the views have as consequence a service method call, but in this case the communication I want is the opposite: the service calling the methods in the view controllers to display the Forms by showDialog().
You need to pass an instance of the IWin32Window interface to the ShowDialog method.
IntPtr myWindowHandle = IntPtr(parent.Handle);
IWin32Window w = Control.FromHandle(myWindowHandle);
Do your Stuff in your first Form and whatever button you press to create your second Form just go like this. (Pseudo Code)
button1_click()
{
Form2 = new Form()
Form2.Owner = this;
}
and now from your Form2 you can talk to your Owner with this.Owner.Visible = false for example.
Thats how you make the Owner if thats what you asked for.
Thanks those who tried to help with your replies. However, although your answers probably will work in other circumstances, mine where a bit different.
Finally I achieved to solve it, I needed to do the handle with Forms in a higher level of abstraction. The information managed was retrieved from an asynchronous task so I couldn't use there a showDialog method and block the MainWindow of the application. Instead I did several threads, wait them and eventually show dialogs when I needed. It's not the best approach, but given the context is the only thing I could do.
In all the books regarding C#/.NET that I have seen till now, when they talk about Events,
they talk about Creating and Consuming Events.
I am curious to understand a bit how it works behind our code - what is the mechanism that runs it.
I know a little bit from the fact that the Windows Message Loop is acting like a Queue for the events that are raised.
For example, WM_KEYDOWN, or WM_LBUTTONDOWN, etc..
But what happens for example, If I create a class that does not inherit class Control, and this class raises an event?
(which another class, which also does not inherit class Control, receives it)
Will the raised event go thru the message loop?
It sound not so logic..
(yet assume that the project is a Windows Forms project, just the 2 classes - the sender and the receiver are not GUI classes at all, but simple classes that you wrote)
Any explanation or link to an article about the mechanism behind our code will be highly appreciated.
I hope I'm understanding your question correctly. I think we're talking about two things.
First - how events work in C#
Second - how a WinForms application written in C# knows when you've clicked a button.
Events in C# are their own distinct thing. You can write a console app, create your own event, listen to it, fire it, respond to it, etc... and it all just works. You subscribe to an event by called Add() and you unsubscribe by calling Remove(). The event itself keeps track of what methods are listening to it and when it's raised, calls all of those methods.
Jon Skeet explains it much better:
How do C# Events work behind the scenes?
But these events are just C# code. Related to, but distinct from the Win32 messages you're mentioning. In a Winforms application, when the user clicks a button, how does the application know about it? We can look using the debugger (turn off the 'My Code' https://msdn.microsoft.com/en-us/library/dn457346.aspx option) and set a breakpoint in the click event, you'll be able to see what is going on.
So in Windows.Forms.Controls.ControlNativeWindow there is a WndProc method that takes in a System.Windows.Forms.Message m.
Right before that is a 'debuggableCallback' method. That mirrors what you'd expect from a Win32API app.
Source here:
http://referencesource.microsoft.com/#System.Windows.Forms/winforms/Managed/System/WinForms/NativeWindow.cs,ad40308c5b6490dd
/// <include file='doc\NativeWindow.uex' path='docs/doc[#for="NativeWindow.DebuggableCallback"]/*' />
/// <devdoc>
/// Window message callback method. Control arrives here when a window
/// message is sent to this Window. This method packages the window message
/// in a Message object and invokes the wndProc() method. A WM_NCDESTROY
/// message automatically causes the releaseHandle() method to be called.
/// </devdoc>
/// <internalonly/>
private IntPtr DebuggableCallback(IntPtr hWnd, int msg, IntPtr wparam, IntPtr lparam) {
// Note: if you change this code be sure to change the
// corresponding code in Callback above!
Message m = Message.Create(hWnd, msg, wparam, lparam);
try {
if (weakThisPtr.IsAlive && weakThisPtr.Target != null) {
WndProc(ref m);
}
else {
DefWndProc(ref m);
}
}
finally {
if (msg == NativeMethods.WM_NCDESTROY) ReleaseHandle(false);
if (msg == NativeMethods.WM_UIUNSUBCLASS) ReleaseHandle(true);
}
return m.Result;
}
So, ultimately, if you're running on Windows, it's driven by the same Win32 API messages you'd expect. It's just that the System.Windows.Forms classes are written to encapsulate most of it from us.
I have a library that I originally created in Java, but I want to rewrite it (and update it) in C#. I've found IKVM, and I might have to go that route. But my first choice would be to rewrite the library in C#.
The library is a teaching tool reminiscent of turtle graphics or Karel the Robot. So I need to be able to open a non-modal window while still sending commands from the Main method.
To mimic the original, I would like to use code similar to what is shown below to start the program since that would reinforce the typical way that objects are declared and instantiated. As the name implies, MyCustomForm would be a subclass of Form.
MyCustomForm myform = new MyCustomForm(...);
I've searched through the site, but haven't found any other question similar to mine.
Is this possible in C#?
Yes, there are several ways. The easiest would be to call Application.DoEvents periodically. Here's a short example of how you might write it.
static class Program {
static bool formClosed = false;
[STAThread]
static void Main() {
MyCustomForm form = new MyCustomForm();
form.Show();
form.FormClosed += Form_FormClosed;
while(!formClosed) {
Application.DoEvents();
Thread.Sleep(10);
}
}
private static void Form_FormClosed(object sender, FormClosedEventArgs e) {
formClosed = true; }
}
There exists much better solutions than this example, though. The term you want to Google for is "C# game loop" despite that your application is not a game.
No, it is not possible.
Application.Run() starts the event pump which allows your application to respond to events. This includes events that come from the user (e.g. clicks, drags, maximize, close) and events that come from the O/S or your own program (such a request to repaint the window, respond to timers, shut down when the O/S is being shut own). All windows forms applications require this sort of event pump in order to provide typical functionality.
Yes, just use ShowDialog() on your form, simple like that, then when your form closes call Application.Exit(). There is no need for Application.DoEvents();
In my case I'm using this to allow user to login again with another user.
So I can close my main form e then show user login form again
I am having a problem that is strange to me but hopefully is not so strange to someone else. : ) Some background: I am working on a simple IM client that allows the user to broadcast messages to multiple recipients. The goal is to create a chat form for each of the recipients containing the text of the broadcast message, then show that form only if the recipient responds to the broadcast-er. However, when the application receives a response then attempts to locate the form for that particular chat session (using Application.OpenForms) it cannot find it UNLESS I .Show at the time it is created. I would like to avoid having to show this form when it is created because this means that the user will see a flash on the screen. The form doesn't seem to really be created until I show it, but it would seem there has to be a way to do this without showing first. Can anyone assist?
I can provide code snippets if needed, I didn't in this post because this feels more like a conceptual misunderstanding on my part than a bug in the code. Thanks in advance!
Instead of using the form as a base class, do it the other way, create a class that can reference a form. That way, you'll keep the class informed of the content, and reflect it on the form (if it's initialized), not the other way around.
You shouldn't rely on Forms as a basis of your objects. Using Application.OpenForms should be unnecessary.
public class Contact
{
string displayname = String.Empty;
List<Message> history = new List<Message>();
MessageForm theform = new MessageForm(this);
public void OnEvent(Message msg)
{
if(msg.Sender != me && !theform.Visible)
theform.Show();
}
public void Tell(string message)
{
}
}
etc
Keep your contacts in some sort of list, and things should be relatively simple.
(Be aware that windows forms aren't thread-safe, and will throw an exception if you try to alter any properties of any of the controls from a different thread than main)
windows form has methods like Hide(),Show() and Activate(). use these method for your problem.
Why not store a reference to the form with the chat session and use that to call .Show() when you need to display the form:
session.form.Show();
You can then create the form without showing it and you don't have the overhead of calling Application.OpenForms each time you want to reference it.
I know this is stating the obvious but OpenForms won't find a form that hasn't been shown because it's not open.
As the form handle does not get created until the form is shown you can assign it manually like so:
mf = new MainForm();
/* Need to assign a handle to MainForm instance manually
as handle does not get created until form is shown */
IntPtr handle = mf.Handle;
The following code implements a simple singleton that ensures only 1 instance of my application can be run. However, if another instance is started, I need to be able to grab that instance's command-line arguments, pass them to the initial instance, then terminate the second instance.
The issue comes in when I'm attempting to get hold of the first instance of the application. Once I've found the handle of that instance's main form, I pass it to the Control.FromHandle() method, expecting to get back a MainForm. Instead, the return value is always null. (Control.FromChildHandle() gives the same result.)
Therefore, my question is simply: what am I doing wrong? And is this even possible in .NET?
public class MainForm : Form
{
[DllImport("user32")]
extern static int ShowWindowAsync(IntPtr hWnd, int nCmdShow);
[DllImport("user32")]
extern static bool SetForegroundWindow(IntPtr hWnd);
private Mutex singletonMutex;
private void MainForm_Load(object sender, EventArgs e)
{
bool wasCreated;
singletonMutex = new Mutex(false, Application.ProductName + "Mutex", out wasCreated);
// returns false for every instance except the first
if (!wasCreated)
{
Process thisProcess = Process.GetCurrentProcess();
Process[] peerProcesses = Process.GetProcessesByName(thisProcess.ProcessName.Replace(".vshost", string.Empty));
foreach (Process currentProcess in peerProcesses)
{
if (currentProcess.Handle != thisProcess.Handle)
{
ShowWindowAsync(currentProcess.MainWindowHandle, 1); // SW_NORMAL
SetForegroundWindow(currentProcess.MainWindowHandle);
// always returns null !!!
MainForm runningForm = (MainForm) Control.FromHandle(currentProcess.MainWindowHandle);
if (runningForm != null)
{
runningForm.Arguments = this.Arguments;
runningForm.ProcessArguments();
}
break;
}
}
Application.Exit();
return;
}
}
Single-instance apps are well supported by the .NET framework. Check this thread for an example that does exactly what you need.
Control.FromHandle isn't going to work because the control you're looking for is in another process (and therefore in another appdomain).
You already have the WindowHandle but it's use is limited to the Win32 API. Nothing from WinForms is going to work.
You can send (WM_) messages but it's hard to get data across.
Options
use something low-level with a
temp-file.
use remoting (WCF)
Try the following
var form = (Form)(Control.FromHandle(myHandle));
EDIT
Re-read your question and realized you are looking at a handle in another process. There is no way to convert a handle in another process to a Form instance in the current process. My solution will only work for handles in the same process.
The only way to get ahold of the Form instance is to use Remoting. But that will require cooperation on the part of both processes which does not appear to be what you are looking for.
You are really trying to implement a singleton application. There are a few examples in the Internet (sorry, haven't really tried myself), e.g.
http://www.codeproject.com/KB/cs/SingletonApplication.aspx
http://www.nathanm.com/csharp-wpf-singleton-application/
You can't call code in another process directly, you need to use some form of inter-process communication
If you are communication only between processes started by the same user on the same computer you can use window messages (using WinAPI PostMessage and overriding WndProc), otherwise I think remoting is the easiest to use in .net
I use the Microsoft.VisualBasic.dll library described in the thread that nobugz pointed to. Yes, you can use it in C#. You just override the OnStartupNextInstance and pass the command line into your program in whatever way works best for you.
This is a whole lot easier than messing around with the threads manually.