2016-05-18 1 views
1

Как можно использовать элемент управления ActiveX в проекте типа ClassLibrary?Как использовать компонент ActiveX в ClassLibrary без Winforms

Я намерен называть его позже из приложения WPF, но я не хочу размещать элемент управления в любом месте формы, поэтому я не хочу использовать WindowsFormsHost; главным образом потому, что я хотел бы использовать эту свою библиотеку в консольном приложении и Windows Service.

В этом случае элемент управления ActiveX, который я хочу использовать, представляет собой компонент видеоанализа. Кроме того, я хочу, чтобы мой компонент регистрировался в развернутой среде.

ответ

2

Я думаю, что общеизвестно, что вам нужны Winforms, чтобы иметь возможность использовать элемент управления ActiveX. Ну, не совсем так. Вам нужен winform-подобный цикл сообщений и STAThread.

Начнем с представления дизайна моего решения. Я предпочитаю разделять код на столько слоев, сколько необходимо при работе с чем-то неизвестным, поэтому вы можете найти некоторые слои избыточными. Я призываю вас помочь мне улучшить решение, чтобы найти равновесие.

Помните, что при необходимости необходимо реализовать интерфейс IDisposable во всех внешних слоях.

ActiveXCore - класс, содержащий элемент управления ActiveX, объявленный как личное поле. В этом классе вы используете только код, как в Winforms.

CoreAPI - внутренний класс API, который предоставляет методы ActiveXCore. Я узнал, что полезно отметить методы с [STAThreadAttribute], так как у меня были проблемы без него, хотя это может быть специфично только для этого случая.

PublicAPI - мой основной класс библиотеки, который будет называться в проектах-ссылках.

В настоящее время в ActiveXCore действительно нет рекомендаций. В CoreAPI выборочный метод будет

[STAThreadAttribute] 
internal bool Init() 
{ 
    try 
    { 
     _core = new ActiveXCore(); 
     //... 

     return true; 
    } 
    catch (System.Runtime.InteropServices.COMException) 
    { 
     //handle the exception 
    } 
    return false; 
} 

Чтобы иметь возможность правильно запустить эти вам необходимо будет Winforms как цикл обработки сообщений, как это (рисунки не мой вообще, я просто немного изменил код). Вам не нужно тип проекта Winforms, но вы должны ссылаться на System.Windows.Forms ассамблее

public class MessageLoopApartment : IDisposable 
{ 
    public static MessageLoopApartment Apartament 
    { 
     get 
     { 
      if (_apartament == null) 
       _apartament = new MessageLoopApartment(); 
      return _apartament; 
     } 
    } 

    private static MessageLoopApartment _apartament; 
    private Thread _thread; // the STA thread 

    private TaskScheduler _taskScheduler; // the STA thread's task scheduler 

    public TaskScheduler TaskScheduler { get { return _taskScheduler; } } 

    /// <summary>MessageLoopApartment constructor</summary> 
    public MessageLoopApartment() 
    { 
     var tcs = new TaskCompletionSource<TaskScheduler>(); 

     // start an STA thread and gets a task scheduler 
     _thread = new Thread(startArg => 
     { 
      EventHandler idleHandler = null; 

      idleHandler = (s, e) => 
      { 
       // handle Application.Idle just once 
       Application.Idle -= idleHandler; 
       // return the task scheduler 
       tcs.SetResult(TaskScheduler.FromCurrentSynchronizationContext()); 
      }; 

      // handle Application.Idle just once 
      // to make sure we're inside the message loop 
      // and SynchronizationContext has been correctly installed 
      Application.Idle += idleHandler; 
      Application.Run(); 
     }); 

     _thread.SetApartmentState(ApartmentState.STA); 
     _thread.IsBackground = true; 
     _thread.Start(); 
     _taskScheduler = tcs.Task.Result; 
    } 

    /// <summary>shutdown the STA thread</summary> 
    public void Dispose() 
    { 
     Dispose(true); 
     GC.SuppressFinalize(this); 
    } 

    protected virtual void Dispose(bool disposing) 
    { 
     if (_taskScheduler != null) 
     { 
      var taskScheduler = _taskScheduler; 
      _taskScheduler = null; 

      // execute Application.ExitThread() on the STA thread 
      Task.Factory.StartNew(
       () => Application.ExitThread(), 
       CancellationToken.None, 
       TaskCreationOptions.None, 
       taskScheduler).Wait(); 

      _thread.Join(); 
      _thread = null; 
     } 
    } 

    /// <summary>Task.Factory.StartNew wrappers</summary> 
    public void Invoke(Action action) 
    { 
     Task.Factory.StartNew(action, 
      CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Wait(); 
    } 

    public TResult Invoke<TResult>(Func<TResult> action) 
    { 
     return Task.Factory.StartNew(action, 
      CancellationToken.None, TaskCreationOptions.None, _taskScheduler).Result; 
    } 

    public Task Run(Action action, CancellationToken token) 
    { 
     return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler); 
    } 

    public Task<TResult> Run<TResult>(Func<TResult> action, CancellationToken token) 
    { 
     return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler); 
    } 

    public Task Run(Func<Task> action, CancellationToken token) 
    { 
     return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler).Unwrap(); 
    } 

    public Task<TResult> Run<TResult>(Func<Task<TResult>> action, CancellationToken token) 
    { 
     return Task.Factory.StartNew(action, token, TaskCreationOptions.None, _taskScheduler).Unwrap(); 
    } 
} 

И тогда вы можете предоставить такие методы, как этот

public bool InitLib() 
{ 
    return MessageLoopApartment.Apartament.Run(() => 
    { 
     ca = new CoreAPI(); 
     bool initialized = ca.Init(); 
    }, CancellationToken.None).Result; 
} 

в случае, если метод будет недействительным

public void InitLib() 
{ 
    MessageLoopApartment.Apartament.Run(() => 
    { 
     ca = new CoreAPI(); 
     ca.Init(); 
    }, CancellationToken.None).Wait(); 
} 

Что касается авто Регистрация в я разработал что-то вроде этого (я называю его от CoreAPI)

internal static class ComponentEnvironment 
{ 
    internal static void Prepare() 
    { 
     CopyFilesAndDeps(); 

     if (Environment.Is64BitOperatingSystem) 
      RegSvr64(); 
     RegSvr32(); //you may notice no "else" here 
     //in my case for 64 bit I had to copy and register files for both arch 
    } 

    #region unpack and clean files 

    private static void CopyFilesAndDeps() 
    { 
     //inspect what file you need 
    } 

    #endregion unpack and clean files 

    #region register components 

    private static void RegSvr32() 
    { 
     string dllPath = @"xxx\x86\xxx.dll"; 
     Process.Start("regsvr32", "/s " + dllPath); 
    } 

    private static void RegSvr64() 
    { 
     string dllPath = @"xxx\x64\xxx.dll"; 
     Process.Start("regsvr32", "/s " + dllPath); 
    } 

    #endregion register components 
} 

Я провел много дней и ночей, чтобы спроектировать этот шаблон многократного использования, поэтому я надеюсь, что это поможет кому-то.

Смежные вопросы