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.
Related
I'm writing Windows / Windows Phone 8.1 application that has needs to invoke a native C++/CX class in the background.
At the moment I have a code like this (inside an async method(
var list = await Task.Run(()=> {
var parser = new NativeParser();
return parser.process("someData"); //here I get the exception
});
on the call of .process() I get an exception saying WinRT information: The application called an interface that was marshalled for a different thread.
The problem is that I'd like to what exactly is the problem - isn't the native class also created on background (inside Task.Run?)
Edit:
I tried to do the same inside a ThreadPool call - and the same thing happens. Is there some weird C# to C++/CX interop thing that I'm not aware of?
IAsyncAction asyncAction = ThreadPool.RunAsync((workItem) =>
{
var parser = new NativeParser();
return parser.process("someData");
});
asyncAction.Completed = new AsyncActionCompletedHandler(
(IAsyncAction asyncInfo, AsyncStatus asyncStatus) =>
{
Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () =>
{
// update UI
}
});
Found the culprit - while stepping through the code I discovered that it crashed on the line where my C++/CX code was creating a ref new Foo();, where Foo was derived from Windows::UI::Xaml::DependencyObject (seemed like a nice base class at the moment as the only possible public unsealed types are those found under the Windows::UI::Xaml namespace, and users are not able to create their own public unsealed types).
Apparently you can only create and access XAML elements that inherit from DependencyObject from the XAML UI thread of an app, so I have to refactor the code to avoid inheriting from that one.
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.
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.
We are using Topaz Signature pads. They provide their APIs in the from of an ActiveX control which is to be put on a Winform control. Well, the way our project will work we do not want to have a form(at least not visible). We just want for the signature ActiveX control to get an image in the background.
static AxSigPlus sig = new AxSIGPLUSLib.AxSigPlus();
public static void Begin()
{
((System.ComponentModel.ISupportInitialize)(sig)).BeginInit();
sig.Name = "sig";
sig.Location = new System.Drawing.Point(0, 0);
sig.Size = new System.Drawing.Size(0, 0);
sig.Enabled = true;
sig.TabletState = 1; //error here
sig.SigCompressionMode = 0;
}
Ok so I get an error at the marked line. The exception is
Exception of type 'System.Windows.Forms.AxHost+InvalidActiveXStateException' was thrown.
What do I do to solve this problem? Would it just be easier to create a new hidden form and put the control on it so it's invisible?
Yes, that can't work this way. The AxHost wrapper requires its Handle to be created before it is usable. Which requires it to be a child control on a form whose Show() method is called.
You normally get two interop wrappers from an ActiveX control, an AxBlah.dll which contains the AxHost wrapper and a Blah.dll which wraps the COM interfaces. You'd only need to reference Blah.dll. Whether that will work is an open question, many ActiveX controls require a window handle to deal with thread synchronization.
If that doesn't work out, you'll need a host form. You can keep it invisible by pasting this code into the form class:
protected override void SetVisibleCore(bool value) {
if (!IsHandleCreated) CreateHandle();
value = false;
base.SetVisibleCore(value);
}
You have to call Application.Run() to pump the message loop.
You might be able to just use the COM object directly (it really depends how they implemented the control). Normally when you import the COM object into your references it will create a wrapper AxHost but it should also import the basic class objects. Find which that is then just create it as any normal class, do not use the AxHost version. If there doesn't seem to be any base class objects you can create the object using the Activator and either the CLSID or ProgID of the control. Something like:
object o = Activator.CreateInstance(Type.GetTypeFromProgID("prog.id"))
This is what I did (basically added invisible Ax control and invoked its methods thereafter):
using (AxRUNNERXLib.AxRunnerX crm = new AxRUNNERXLib.AxRunnerX ()) {
Controls.Add (crm);
crm.Visible = false;
crm.CustomerPrefix = m_SelCall.CustomerPrefix;
crm.LoadDefaultDescription ();
crm.SearchByID (m_SelCall.CustomerID);
crm.OperatorID = Form1.operatorID.ToString ();
crm.ShowHistory ();
Controls.Remove (crm);
}
Actually it ended up that Topaz provided an ActiveX control and a .Net wrapper around it. I switched to the .Net wrapper and it doesn't require being placed on a form or anything. I will leave the question up though because had it not been for that wrapper I would actually be dealing with it.
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.