Кошмар STA, MTA и OLE

Мне нужно включить приложение .NET в другое приложение .NET в качестве плагина. Интерфейс плагина требует, чтобы я наследовал форму шаблона. Затем форма прикрепляется к MDI при загрузке плагина.

Пока все работает, но всякий раз, когда я регистрируюсь для событий перетаскивания, устанавливаю режим автозаполнения для поля со списком или в различных других ситуациях, я получаю следующее исключение:

... текущий поток должен быть установлен в режим однопоточного подразделения (STA), прежде чем можно будет выполнять вызовы OLE. Убедитесь, что на вашей основной функции отмечен атрибут STAThreadAttribute...

Основное приложение работает в MTA и разработано другой компанией, поэтому я ничего не могу с этим поделать.

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

Кто-нибудь был в такой же ситуации? Могу ли я что-нибудь сделать, чтобы решить проблему?


person xsl    schedule 30.06.2009    source источник


Ответы (3)


Вы можете попытаться создать новый поток и вызвать CoInitialize с 0 на нем (апармент с резьбой) и запустить свое приложение в этом потоке. Однако вы не будете обновлять элементы управления непосредственно в этом потоке, вы должны использовать Control.Invoke для каждой модификации пользовательского интерфейса.

Я не знаю, сработает ли это наверняка, но вы можете попробовать.

person devdimi    schedule 30.06.2009
comment
Благодарим за ваше предложение. Я пробовал это, но получил ту же ошибку, когда вызывал элемент пользовательского интерфейса, созданный потоком MTA. - person xsl; 30.06.2009
comment
Может управлять запуском приложения? Чем вы могли бы обернуть его в контейнерное приложение. Создайте поток, инициализируйте COM и затем вызовите метод Main программы. В зависимости от приложения это может работать или не работать и вызывать сбой основной программы, но может быть вашим последним средством. (Перед декомпилированием и изменением основного приложения, что может быть незаконным.) Вы также можете попытаться связаться с компанией, которая его разработала. - person devdimi; 30.06.2009
comment
Запустите новый поток t, используйте t.SetApartmentState(System.Threading.ApartmentState.STA), t.Start() - и ничего не может пойти не так. Это просто работает, проверено в моем огромном приложении на работе. - person Harry; 08.06.2012

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

Проблема

private void TimerTick(object sender, EventArgs e)
{
   // pause timer
   this.timer.Stop();

        try
        {
            // get next frame
            UnsafeNativeMethods.SendMessage(this.captureHandle, WindowsMessageCameraGetFrame, 0, 0);

            // copy frame to clipboard
            UnsafeNativeMethods.SendMessage(this.captureHandle, WindowsMessageCameraCopy, 0, 0);

            // notify event subscribers
            if (this.ImageChanged != null)
            {
                IDataObject imageData = Clipboard.GetDataObject();

                Image image = (Bitmap)imageData.GetData(System.Windows.Forms.DataFormats.Bitmap);

                this.ImageChanged(this, new WebCamEventArgs(image.GetThumbnailImage(this.width, this.height, null, System.IntPtr.Zero)));
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show("Error capturing the video\r\n\n" + ex.Message);
            this.Stop();
        }
    }
   // restart timer
   Application.DoEvents();

   if (!this.isStopped)
   {
      this.timer.Start();
   }
}

Решение: переместите однопоточную логику в отдельный метод и вызовите этот метод из нового потока STA.

private void TimerTick(object sender, EventArgs e)
{
    // pause timer
    this.timer.Stop();

    // start a new thread because GetVideoCapture needs to be run in single thread mode
    Thread newThread = new Thread(new ThreadStart(this.GetVideoCapture));
    newThread.SetApartmentState(ApartmentState.STA);
    newThread.Start();

    // restart timer
    Application.DoEvents();

    if (!this.isStopped)
    {
        this.timer.Start();
    }
}

/// <summary>
/// Captures the next frame from the video feed.
/// This method needs to be run in single thread mode, because the use of the Clipboard (OLE) requires the STAThread attribute.
/// </summary>
private void GetVideoCapture()
{
    try
    {
        // get next frame
        UnsafeNativeMethods.SendMessage(this.captureHandle, WindowsMessageCameraGetFrame, 0, 0);

        // copy frame to clipboard
        UnsafeNativeMethods.SendMessage(this.captureHandle, WindowsMessageCameraCopy, 0, 0);

        // notify subscribers
        if (this.ImageChanged!= null)
        {
            IDataObject imageData = Clipboard.GetDataObject();

            Image image = (Bitmap)imageData.GetData(System.Windows.Forms.DataFormats.Bitmap);

            // raise the event
            this.ImageChanged(this, new WebCamEventArgs(image.GetThumbnailImage(this.width, this.height, null, System.IntPtr.Zero)));
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show("Error capturing video.\r\n\n" + ex.Message);
        this.Stop();
    }
}
person Gustavo Mori    schedule 16.02.2011

Обновление: компания выпустила новую версию STA. Вопрос уже не актуален.

person xsl    schedule 08.09.2009