SaveFileDialog: InvalidOperationException из-за параметра "owner" в многопоточном приложении - PullRequest
3 голосов
/ 20 мая 2011

Извините за длинный пост, но я попытался объяснить проблему очень подробно, чтобы не возникало путаницы.Последнее предложение содержит актуальный вопрос.

Я программирую многопоточное приложение на C # /. NET.

Приложение состоит из главного окна, которое визуализирует данные, поступающие издатчик давления.Данные датчика собираются в собственном потоке.

Данные также регистрируются в экземпляре класса ListView:

enter image description here

Существует возможностьсохранить зарегистрированные данные в файл на диске с помощью кнопки «Сохранить» (должен открыть экземпляр класса .NET SaveFileDialog).

Этот SaveFileDialog также выполняется в собственном потоке.Теперь возникает проблема при вызове метода SaveFileDialog.ShowDialog():

System.InvalidOperationException не обработан. Message = "Операция с несколькими потоками недопустима: доступ к элементу управления tlpMain осуществляется из потока, отличного от потока, в котором он находился.создано на."Source = "System.Windows.Forms"

Проблема возникает из-за того, что владелец (главное окно) SaveFileDialog работает в другом потоке.

Вот код, который создает поток для SaveFileDialog ():

private void bSave_Click(object sender, EventArgs e)
{
    Thread saveFileDialog = new Thread(OpenSaveFileDialog);
    saveFileDialog.SetApartmentState(ApartmentState.STA);
    saveFileDialog.Start();
}

Код для метода OpenSaveFileDialog ():

private void OpenSaveFileDialog()
{
    SaveFileDialog saveFileDialog = new SaveFileDialog();
    saveFileDialog.Filter = "Text Files (*.txt)|*.txt|CSV (*.csv)|*.csv|All Files (*.*)|*.*";
    saveFileDialog.FilterIndex = 0;

    /* Call "ShowDialog" with an owner ("this.Parent") to achieve, so that
     * the parent window is blocked and "unclickable".
     * 
     * Danger of an "InvalidOperationException" because "this.Parent" control
     * is running (was created) in another thread.
     * But "this.Parent" should not be modified by this method call.
     */
    DialogResult pressedButton = saveFileDialog.ShowDialog(this.Parent);
    ...

Выдается только InvalidOperationException / отображается при запуске приложения с помощью отладчика Visual Studio.Пока это не проблема - пока приложение работает "нормально".

Но я бы хотел избежать этой проблемы.

Я пытался построить метод-обертку (SaveFileDialog):

private void OpenSaveFileDialog()
{
    SaveFileDialog saveFileDialog = new SaveFileDialog();
    ...
    SaveFileDialog(saveFileDialog, this.Parent);
}

Метод обертки:

private void SaveFileDialog(SaveFileDialog saveFileDialog, Control owner)
{
    if (owner.InvokeRequired)
        BeginInvoke(new dSaveFileDialog(SaveFileDialog), new object[] { saveFileDialog, owner });
    else
    {
        DialogResult pressedButton = saveFileDialog.ShowDialog(owner);
        ...

Это приводит к TargetInvocationException, хотя метод Main() помечен [STAThreadAttribute]:

InnerException:System.Threading.ThreadStateException Message = "Для выполнения OLE-вызовов текущий поток должен быть переведен в однопотоковый режим (STA). Убедитесь, что в вашей функции Main помечен атрибут STAThreadAttribute. Это исключение возникает только в том случае, если к отладчику подключенпроцесс."Source = "System.Windows.Forms"

Кто-нибудь знает, как открыть SaveFileDialog таким образом, чтобы главное окно было заблокировано ("не кликабельно") без(нить) беда?

Спасибо.

Ответы [ 3 ]

2 голосов
/ 20 мая 2011

Межпотоковое исключение, которое вы получаете во время отладки: Managed Debugging Assistant .Обычно они не активны вне отладчика.Это объясняет, почему вы не видите, когда запускаете приложение за пределами Visual Studio.

Похоже, вы обнаружили, что самостоятельно не можете ничего сделать с элементом пользовательского интерфейса из потока, отличного от основного пользовательского интерфейса.нить.Вы используете методы ISynchronizeInvoke, а именно Invoke или BeginInvoke, чтобы упорядочить выполнение операции в потоке пользовательского интерфейса, чтобы вы могли безопасно получить доступ к элементам пользовательского интерфейса.

Я все еще вижу проблему с вашимкод хотя.В методе OpenSaveFileDialog, который выполняется в рабочем потоке, вы вызываете конструктор для SaveFileDiaglog, который, конечно, является элементом пользовательского интерфейса.Вы просто не можете этого сделать.Это стоит повторить.Вы не можете сделать что-либо для Form или Control из рабочего потока.Это включает в себя вызов конструктора.

1 голос
/ 23 мая 2011

Извините за поздний ответ.

Прежде всего, спасибо за ваши быстрые и полезные ответы.

Совет, который не возможен

делать что-либо с формой или элементом управления рабочий поток

мне очень помогли.

Я обычно не занимаюсь программированием GUI для Microsoft Windows, и поэтому я не очень знаком с ним.

Так что я пересмотрел предыдущий исходный код, потому что хотел решить актуальную проблему (не выполняет GUI из рабочего потока) и хотел бы иметь чистую и логичную структуру кода.

Поэтому я прочитал в темах об объектной модели компонентов (COM) Window и используемой модели потоков:

Теперь код выглядит так:

Главное окно («Поток пользовательского интерфейса») запускается в ApartmentState STA

...
ThreadStart threadStart = delegate { RunMainWindow(mainWindow); };
Thread mainWindowThread = new Thread(threadStart);

mainWindowThread.SetApartmentState(ApartmentState.STA);
mainWindowThread.Start();
...

Кнопка «Сохранить» Обработчик событий (главное окно):

private void bSave_Click(object sender, EventArgs e)
{
            OpenSaveFileDialog();
}

Метод " OpenSaveFileDialog " (главное окно):

private void OpenSaveFileDialog()
{
            SaveFileDialog saveFileDialog = new SaveFileDialog();
            ...

            DialogResult pressedButton = saveFileDialog.ShowDialog();
            ...
}

Еще есть место для оптимизации (наверняка), но я доволен этим - предварительным - результатом.

Так что большое спасибо за вашу помощь.

0 голосов
/ 24 октября 2012

Следуйте за этим постом в Microsoft: http://blogs.msdn.com/b/smondal/archive/2011/05/11/10059279.aspx

Всего два метода, и все готово!

...