Ожидание изменения текста буфера обмена (ошибка) - PullRequest
4 голосов
/ 30 мая 2011

Я пытаюсь обнаружить каждый раз, когда данные буфера обмена изменяются. Итак, я установил таймер и постоянно проверял Clipboard.GetText() на наличие изменений.

Я использую следующий код:

public void WaitForNewClipboardData()
{
    //This is in WPF, Timer comes from System.Timers
    Timer timer = new Timer(100);
    timer.Elapsed += new ElapsedEventHandler(
        delegate(object a, ElapsedEventArgs b){
            if (Clipboard.GetText() != ClipBoardData)
            {
                SelectedText.Text = Clipboard.GetText();
                ClipBoardData = Clipboard.GetText();
                timer.Stop();
            }
        });
    timer.Start();
}

Я получаю следующую ошибку при запуске:

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

Кто-нибудь знает почему?

Ответы [ 5 ]

3 голосов
/ 30 мая 2011

Ваш метод обращается к классу Clipboard, который является вызовом OLE, которому требуется, чтобы вызывающий абонент находился в режиме STA.Скорее всего, проблема здесь в вашем таймере, который работает в другом потоке.Вот ссылка, которая поможет вам с дополнительной информацией об этом:

http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/2411f889-8e30-4a6d-9e28-8a46e66c0fdb/

Кроме того, здесь есть ссылка на полную статью о том, как контролировать буфер обмена, нажав вСобытия Windows:

http://www.radsoftware.com.au/articles/clipboardmonitor.aspx

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

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

Это потоковая модель практически любого управляемого приложения с графическим интерфейсом Windows.Поток, выполняющий код ergo, взаимодействующий с потоком пользовательского интерфейса, должен быть одним и тем же.Если вы поддерживаете SynchronizationContext вашего начального пути где-то (вы можете поместить это в статическую переменную), вы можете публиковать на нем сообщения.Эти сообщения будут выполняться в правильном потоке, и вы не получите эту ошибку.

public partial class App : Application
{
    public static SynchronizationContext SynchronizationContext;

    protected override void OnStartup(StartupEventArgs e)
    {
        // This is my preferred way of accessing the correct SynchronizationContext in a WPF app
        SynchronizationContext = SynchronizationContext.Current;

        base.OnStartup(e);

        var mainWindow = MainWindow;

        var t = new Thread(() => {
            Thread.Sleep(3000);

            SynchronizationContext.Post(state => {
                mainWindow.Hide(); // this will not throw an exception
            }, null);

            mainWindow.Close(); // this will throw an exception
        });

        t.Start();
    }
}

Так что, в основном, когда вы работаете с разными потоками (то есть таймерами, а что нет), вам нужно запомнитьчто исходный поток запуска является особым (учитывая, что это поток STA).Чтобы вызывать методы для объектов, принадлежащих этому специальному потоку, вы проходите через SynchronizationContext, который я предоставил в качестве статического члена для моего класса App.

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

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

Опрос в буфере обмена с таймером - очень плохая практика. Буфер обмена является общим ресурсом, и вы будете мешать другим приложениям, которые осуществляют мониторинг буфера обмена (через надлежащее уведомление буфера обмена, т.е. следуя правилам). И вы будете сталкиваться с тем, что пытается сделать пользователь. Например, когда пользователь пытается скопировать данные в буфер обмена, и вы открыли их в цикле опроса, чтобы проверить их, он получит ошибки «невозможно открыть буфер обмена» или произойдет сбой. Пожалуйста, ознакомьтесь со средствами просмотра буфера обмена в MSDN: http://msdn.microsoft.com/en-us/library/ff468802(v=VS.85).aspx

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

Не совсем уверен с этим, но вы пытались вызвать изменение текста? У меня была та же самая ошибка (хотя в очень другом сценарии), и вызов метода для изменения свойства элемента управления решил ее.

Я создал делегата со строковым параметром:

public delegate void TextBoxChangeDelegate(string text);

Затем метод, который будет выполнять фактическое изменение:

void TextBoxChange(string text)
{
   MyTextBox.Text = text;
}

Затем я вызываю этот метод в моем процессе потока (и в вашем случае, в событии таймера):

public void ThreadService()
{
  while(Running)
  {
    Invoke(new TextBoxChangeDelegate(TextBoxChange), new object[] { "New Value: "+ strNewValue });
  }

}

Это было в WinForms. Это было тогда, когда я впервые узнал, что изменение свойств элемента управления в потоке, отличном от потока пользовательского интерфейса, вызывает проблемы. Извините, если это даже близко не к тому, что вы пытаетесь.

0 голосов
/ 30 мая 2011

Это потому, что вы не можете использовать элемент управления windows в делегате потока, в основном, timer - это поток и передача делегата, который с помощью элемента управления windows создает проблему.проверьте, помогает ли это http://social.msdn.microsoft.com/Forums/en-US/winforms/thread/2411f889-8e30-4a6d-9e28-8a46e66c0fdb/

...