Элемент управления ActiveX без формы
Мы должны использовать сторонний элемент управления ActiveX.
Единственная проблема заключается в том, что слой в нашем программном обеспечении является бизнес-слоем и не имеет доступа к окну или форме. Он также работает на отдельных потоках (и должен работать из любого потока), которые не являются STA.
Вместо того, чтобы нарушать наше разделение пользовательского интерфейса от бизнес-логики, мы использовали это обходное решение, чтобы заставить его работать:
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();
Тогда в любое время, когда нам нужно ссылаться на элемент управления, мы вызываем _myActiveX.BeginInvoke()
или Invoke()
.
При утилизации этого класса (выход из нашего приложения) мы удаляем элемент управления и прерываем поток.
Мой вопрос в том, есть ли проблемы с этим? Есть ли лучший способ справиться с этим?
Есть ли лучший встроенный способ работы с элементом управления ActiveX из неизвестной многопоточной среды? Мы пытаемся написать наш класс таким образом, чтобы обернуть элемент управления, но он будет работать из любого потока.
UPDATE: в качестве ответа мы действительно предпочли бы использовать стандартный COM-объект и вообще не использовать элемент управления. Наша проблема заключалась в том, что мы получили бы ошибку "(Исключение из HRESULT: 0x8000FFFF (E_UNEXPECTED)" при первом методе или свойстве, которое мы вызываем для COM-объекта. Это довольно общая ошибка, которую мы не получаем при использовании ActiveX, любые идеи?
UPDATE: наш ocx является "CX25.ocx", используя tlbimp.exe, мы получаем CX25Lib.dll. Используя aximp.exe, мы получаем AxCX25Lib.dll и CX25Lib.dll. CX25Lib.dll не работает ни в одном случае. Работает AxCX25Lib.dll.
Ответы
Ответ 1
Я предполагаю, что это правильный способ сделать это.
Мы использовали мой код выше в тестовых средах в течение последних нескольких недель без проблем.
Если кто-то должен использовать ActiveX без формы, я предполагаю, что это один из способов сделать это.
Просто убедитесь, что вы вызываете _yourActiveXControl.CreateControl() непосредственно после вашего конструктора объектов ActiveX. Это упростило многие проблемы, которые мы изначально имели.
Ответ 2
Если вы вызываете элемент управления ActiveX из бизнес-уровня, это означает, что он должен быть доступен без пользовательского интерфейса, например. просто позвонив своим публичным методам. Почему бы просто не создать interop RCW для класса управления ActiveX и вызвать его методы напрямую?
Ответ 3
Мое решение - создать скрытую winform, в которой размещается элемент управления activex
Ответ 4
Я знаю, что это старый пост, но я бы рекомендовал использовать TPL в нашу современную эпоху.
Лучше использовать параллельную библиотеку задач вместо старого API потоков из-за особенностей обработки исключений, отмены, продолжения и возврата.
Вот пример:
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;
}
Некоторые моменты:
-
StaTaskScheduler
- это тип, найденный в ParallelExtensionsExtras пакет nuget. Это вам нужно, чтобы планировать выполнение задач в однопоточной квартире.
-
Я передаю 1 конструктору StaTaskScheduler
, чтобы он только создавал для меня один поток.
-
Application.ExitThread()
вызывается, чтобы остановить насос сообщений, который, в свою очередь, позволяет исполнению проходить через Application.Run()
, чтобы какой-то результат мог быть возвращен вызывающему.
Ответ 5
Метод CreateControl()
из AxHost и требует System.Windows.Forms
в качестве зависимости. Если вы хотите использовать ActiveX без UI, вы можете напрямую создать COM-объект ocx, используя собственный вызов.
[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 ;
}
Позже вы можете привести объект COM к необходимым интерфейсам
IOleObject iole = yourOCX as IOleObject;
IWMPCore iwmp = yourOCX as IWMPCore;
Я создал Windows Media Player ActiveX без пользовательского интерфейса или AxHost в С# по этой ссылке. Это может помочь кому-то, пытающемуся запустить ActiveX без UI.