Events in .NET winforms: more of what works and what doesn't
-
- Posts: 26
- Joined: Wed Nov 30, 2005 11:23 pm
Events in .NET winforms: more of what works and what doesn't
Here are some more attempts at getting a .net winform to receive mm events:
WHAT DIDN'T WORK
try 1 auto launch script to launch winform and subscribe to events. delegate events to a method in the app
problem GetObject doesn't seem to get the currently running version of the winform. error is thrown. CreateObject also yields wrong results
try 2 same as #1 but make the winform single instance so CreateObject will always get the correct version
problem the only way I can figure out how to make an app single instance involves using the static Main() for much of the work. CreateObject bypasses Main and only calls constructor
try 3 same as #1 but use threading and Invoke/BeginInvoke to fire winform method
problem error about not being able to use Invoke/BeginInvoke without having a handle to the window. unsure how to do this in vbscript. again, GetObject didn't work here in trying to resolve it
WHAT WORKED (BUT SUCKS)
solution auto launch script registers for events and launches the winform. event methods create temp files when events are fired. (note: at the beginning of the onstart, delete old files or future runs will think that these events are new). the winform has a file system watcher (available in .net 2.0) which reacts to the file creation and deletes the files sequentially
why it sucks using files as events is simply lame. i also did a version where only 1 file was used but you come into IO collisions/exceptions when too many events happen too quickly. also, keeping the files in order when events fire quickly is more advanced than just using a timestamp with seconds as the greatest granularity
solution launch 2 .net apps: 1 winform, 1 console. the console one can handle events just fine. so set up a cross process connection between them either via .net remoting or through socket connections and have the console app delegate the events to the winform
why it sucks way too much overhead. and i refuse to keep a client server relationship on the same box just to delegate event callls
WHAT DIDN'T WORK
try 1 auto launch script to launch winform and subscribe to events. delegate events to a method in the app
problem GetObject doesn't seem to get the currently running version of the winform. error is thrown. CreateObject also yields wrong results
try 2 same as #1 but make the winform single instance so CreateObject will always get the correct version
problem the only way I can figure out how to make an app single instance involves using the static Main() for much of the work. CreateObject bypasses Main and only calls constructor
try 3 same as #1 but use threading and Invoke/BeginInvoke to fire winform method
problem error about not being able to use Invoke/BeginInvoke without having a handle to the window. unsure how to do this in vbscript. again, GetObject didn't work here in trying to resolve it
WHAT WORKED (BUT SUCKS)
solution auto launch script registers for events and launches the winform. event methods create temp files when events are fired. (note: at the beginning of the onstart, delete old files or future runs will think that these events are new). the winform has a file system watcher (available in .net 2.0) which reacts to the file creation and deletes the files sequentially
why it sucks using files as events is simply lame. i also did a version where only 1 file was used but you come into IO collisions/exceptions when too many events happen too quickly. also, keeping the files in order when events fire quickly is more advanced than just using a timestamp with seconds as the greatest granularity
solution launch 2 .net apps: 1 winform, 1 console. the console one can handle events just fine. so set up a cross process connection between them either via .net remoting or through socket connections and have the console app delegate the events to the winform
why it sucks way too much overhead. and i refuse to keep a client server relationship on the same box just to delegate event callls
OK, Interesting, I'm not .NET coder but have you tried this way?
Open WinForm, On MM event Check FindWindow() to get Handle then use PostMessage() to send Custom Message to WinForm Handle and Handle On Message inside WinForm. All thrum WinAPI and without problems.
Note When you get Handle with FindWindow() save it sonwhere so that next time you skip that test and only Check For NULL value in saved Variable too see if your app is closed or not. Saves Speed and works (In Delphi).
Open WinForm, On MM event Check FindWindow() to get Handle then use PostMessage() to send Custom Message to WinForm Handle and Handle On Message inside WinForm. All thrum WinAPI and without problems.
Note When you get Handle with FindWindow() save it sonwhere so that next time you skip that test and only Check For NULL value in saved Variable too see if your app is closed or not. Saves Speed and works (In Delphi).
Best regards,
Peke
MediaMonkey Team lead QA/Tech Support guru
Admin of Free MediaMonkey addon Site HappyMonkeying
How to attach PICTURE/SCREENSHOTS to forum posts
Peke
MediaMonkey Team lead QA/Tech Support guru
Admin of Free MediaMonkey addon Site HappyMonkeying
How to attach PICTURE/SCREENSHOTS to forum posts
-
- Posts: 26
- Joined: Wed Nov 30, 2005 11:23 pm
Great idea. Unfortunately, when I tried this scriptI got this error
Code: Select all
Sub OnStartup
Dim hWnd
hWnd = FindWindow("Untitled - Notepad", vbNullString)
End Sub
Code: Select all
Error #13 - Microsoft.VBScript runtime error
Type mismatch: 'FindWindow'
File: "C:\PROGRA~1\MEDIAM~1\Scripts\Auto\test.vbs", Line: 3, Column:2
You can't use API through VBScript, unless you use some ActiveX object that can do that. C#.Net can easily do what you want, you only need to add the P/Invoke signature first.
Code: Select all
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
IntPtr hWnd = FindWindow("Winamp v1.x", null);
Extensions: ExternalTools, ExtractFields, SongPreviewer, LinkedTracks, CleanImport, and some other scripts (Need Help with Addons > List of All Scripts).
-
- Posts: 26
- Joined: Wed Nov 30, 2005 11:23 pm
Thanks for the C# code but I'm not sure how it helps solve the original problem: MM's VBScript still doesn't have a window handle through which it can send event notifications.
I've actually built everything without events now. However, if at any point in C# you call new SDBApplicationClass(); then when you shut down MM you get the "COM Server Warning" message box. This occurs regardless of whether you shut down the winform app or MM 1st.
I'm very close to giving up on programming with MM. Despite how good it looks like it could be, it's becoming damn impossible with C#. I'm playing whack-a-mole here. Every time I find one way to solve this events problem, it pops up somewhere else in a different idiosyncrasy.
I've actually built everything without events now. However, if at any point in C# you call new SDBApplicationClass(); then when you shut down MM you get the "COM Server Warning" message box. This occurs regardless of whether you shut down the winform app or MM 1st.
I'm very close to giving up on programming with MM. Despite how good it looks like it could be, it's becoming damn impossible with C#. I'm playing whack-a-mole here. Every time I find one way to solve this events problem, it pops up somewhere else in a different idiosyncrasy.
Indeed. The best way to get rid of this problem most of the times is to terminate my own application when it is exiting (you know, using something like Process.Kill()). Normally there should be no special requirements to make C# (or .Net) play nice. You can explicitly call a function to release the COM handle, but that doesn't change much, the MM warning is still there. In my opinion, and as other applications seem to do it, MM shouldn't give a warning but just exit.when you shut down MM you get the "COM Server Warning" message box.
Extensions: ExternalTools, ExtractFields, SongPreviewer, LinkedTracks, CleanImport, and some other scripts (Need Help with Addons > List of All Scripts).
-
- Posts: 26
- Joined: Wed Nov 30, 2005 11:23 pm
And whack-a-mole continues...
Now I can do my own version of events, I can call SDBApplicationClass() from my app, and (now thanks to Steegy's Process.Kill() suggestion) I also close it without getting the COM server warning dialog.
However, now MediaMonkey cannot perform its own correct shutdown. This is inconvenient since it means that MM cannot save its previous state to resume on next launch -- such as the now playing playlist.
Here is the code I tried
Now I can do my own version of events, I can call SDBApplicationClass() from my app, and (now thanks to Steegy's Process.Kill() suggestion) I also close it without getting the COM server warning dialog.
However, now MediaMonkey cannot perform its own correct shutdown. This is inconvenient since it means that MM cannot save its previous state to resume on next launch -- such as the now playing playlist.
Here is the code I tried
Code: Select all
protected override void OnClosed(EventArgs e) {
base.OnClosed(e);
Process.GetCurrentProcess().Kill();
}
Code: Select all
protected override void OnClosed(EventArgs e) {
base.OnClosed(e);
Process[] procs = Process.GetProcessesByName("MEDIAM~1");
foreach (Process p in procs)
p.Kill();
}
I used Process.Kill() to terminate my own test application when it exits. You should never terminate MM that way!
Extensions: ExternalTools, ExtractFields, SongPreviewer, LinkedTracks, CleanImport, and some other scripts (Need Help with Addons > List of All Scripts).
-
- Posts: 26
- Joined: Wed Nov 30, 2005 11:23 pm
-
- Posts: 26
- Joined: Wed Nov 30, 2005 11:23 pm