2015-11-06 3 views
0

Недавно я добавил форму для входа в приложение. Эта форма отображается перед всплывающим экраном, который отображается при загрузке основной формы приложения, и создаются различные объекты ввода-вывода.Захват экрана из потока, на котором он не был создан?

До формы входа это как мой Program.cs бы запустить приложение

if (mutex.WaitOne(TimeSpan.Zero, true)) 
{ 
    Application.EnableVisualStyles(); 
    Application.SetCompatibleTextRenderingDefault(false); 

    SplashScreen.ShowSplashScreen(); 
    Application.Run(MainForm.Instance); 

    mutex.ReleaseMutex(); 
} 

С новым именем входа для приложения теперь стали как так

if (mutex.WaitOne(TimeSpan.Zero, true)) 
{ 
    Application.EnableVisualStyles(); 
    Application.SetCompatibleTextRenderingDefault(false); 

    UserSessionSelection ussDialog = new UserSessionSelection(); 
    if (ussDialog.ShowDialog() == DialogResult.OK) 
    { 
     SplashScreen.ShowSplashScreen(); 
     Application.Run(MainForm.Instance); 
    } 

    mutex.ReleaseMutex(); 
} 

Вот это SplashScreen класс

public partial class SplashScreen : Form 
{ 
    public static SplashScreen Instance { get { return lazyInstance.Value; } } 
    private static readonly Lazy<SplashScreen> lazyInstance = 
     new Lazy<SplashScreen>(() => new SplashScreen()); 

    private SplashScreen() 
    { 
     InitializeComponent(); 
     CenterToScreen(); 
     TopMost = true; 
    } 

    static public void NewLoadingUpdate(String message, int percent) 
    { 
     NewUpdateDelegate nud = new NewUpdateDelegate(NewLoadingUpdateInternal); 
     SplashScreen.Instance.Invoke(nud, new object[] { message, percent }); 
    } 

    static private void NewLoadingUpdateInternal(String message, int percent) 
    { 
     SplashScreen.Instance.lblLoadingText.Text = message; 
     SplashScreen.Instance.pgProgress.Value = percent; 
    } 

    private delegate void NewUpdateDelegate(String message, int percent); 
    private delegate void CloseDelegate(); 

    static public void ShowSplashScreen() 
    { 
     Thread thread = new Thread(new ThreadStart(SplashScreen.ShowForm)); 
     thread.IsBackground = true; 
     thread.SetApartmentState(ApartmentState.STA); 
     thread.Start(); 
    } 

    static private void ShowForm() 
    { 
     Application.Run(SplashScreen.Instance); 
    } 

    static public void CloseForm() 
    { 
     SplashScreen.Instance.Invoke(new CloseDelegate(SplashScreen.CloseFormInternal)); 
    } 

    static private void CloseFormInternal() 
    { 
     SplashScreen.Instance.Close(); 
    } 
} 

Ошибка, связанная с ошибкой ShowForm Конкретный текст:

An unhandled exception of type 'System.InvalidOperationException' 
occurred in System.Windows.Forms.dll 

Additional information: Cross-thread operation not valid: Control 
'SplashScreen' accessed from a thread other than the thread it was created on. 

Ошибка возникает примерно в 1/20 раза, когда приложение запускается. Я никогда не сталкивался с этим до формы входа в систему.

Любые идеи относительно того, что вызывает это?

EDIT: Для тех, кто опаздывает на вечеринку, я думаю, что этот вопрос поможет. Wait for a thread to actually start in c#

ответ

2

Вам необходимо создать SplashScreen в той же теме, где вы его используете.

Но подождите, вот что я делаю, не так ли? Ну, нет, вы видите довольно типичное состояние гонки.

Ядро вашей проблемы, я подозреваю, использует Lazy, чтобы инициализировать заставку в сочетании с тем, что не дожидаясь, пока форма будет создана в вашем методе ShowSplashScreen. В вашей основной форме вы ссылаетесь на SplashScreen.Instance. Теперь, если первая нить, которая пыталась прочитать экземпляр, - это ваш контур сообщения с заставкой, вы в порядке - это 19 из 20.

Однако вполне возможно, что основной поток пользовательского интерфейса попадает туда первым - т в ShowSplashScreen. В этом случае экран заставки создается в основном потоке пользовательского интерфейса, и у вас проблемы - и хорошо, что вы не используете InvokeRequired, потому что это еще больше спрячет ошибку.

Почему это имеет какое-либо отношение к новой форме входа? Ну, я подозреваю, что это очень важно, на самом деле - ваш код сломан или без формы входа. Однако ShowDialog запускает новый цикл сообщений, аналогичный Application.Run. Это также означает, что должен быть создан контекст синхронизации - то, что иначе могло бы произойти только на вашей линии Application.Run(MainForm.Instance). Ключевым моментом является то, что вам удалось значительно улучшить состояние гонки - между вызовом ShowSplashScreen и первым запуском заставки в больше нет времени, и результатом будет BOOM.

Не допускайте возврата метода ShowSplashScreen до тех пор, пока экземпляр не будет правильно создан, и все будет в порядке. Многопоточность hard - не пытайтесь угадать свой путь. Хорошей отправной точкой будет http://www.albahari.com/threading/ - убедитесь, что вы уделяете большое внимание правильной синхронизации и сигнализации.

+0

Спасибо Luaan!Я читал/посещал сайт albahari много раз, хотя есть много информации, чтобы принять, и да многопоточность трудна. – KDecker

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

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