2010-04-12 15 views
7

Nie robię zbyt wiele programowania GUI systemu Windows, więc może to być powszechna wiedza dla osób bardziej zaznajomionych z WinForm niż ja. Niestety nie udało mi się znaleźć żadnych zasobów, aby wyjaśnić problem, napotkany dziś podczas debugowania.Control.EndInvoke resetuje stos wywołań dla wyjątku

Jeśli wywołasz EndInvoke na delegata async. Otrzymamy wyjątek wygenerowany podczas wykonywania metody ponownego wygenerowania. Stos wywołań będzie odzwierciedlał oryginalne źródło wyjątku.

Jeśli jednak zrobimy coś podobnego na Windows.Forms.Control, implementacja Control.EndInvoke resetuje stos wywołań. Można to zaobserwować za pomocą prostego testu lub patrząc na kod w Reflectorze. Odpowiedni fragment kodu z EndInvoke jest tutaj:

if (entry.exception != null) 
{ 
    throw entry.exception; 
} 

Rozumiem, że Begin/EndInvoke Kontroli i asynchronicznych delegatów są różne, ale spodziewałem się podobnego zachowania na Control.EndInvoke.

Czy istnieje jakikolwiek powód, dla którego Control nie robi tego, co jest asynchroniczne, jakie delegaci robią, aby zachować oryginalny stos połączeń?

Odpowiedz

1

Nie znam prawdziwego powodu, ale mogę się domyślić, że delegaci async są podobni do RPC, podczas gdy delegaci kontroli mogą opierać się na wysyłaniu wiadomości Win32. Różne technologie, więc wpływ tej funkcji może nie być taki sam. Delegat Async korzystałby ze wszystkich kodów zdalnego dostępu, dla których programista napisał kod w celu przeniesienia stosu wywołań wyjątków między różnymi procesami lub komputerami, podczas gdy delegaci kontroli będą symulować RPC z PostMessage w ramach tego samego procesu. Różny zespół, inny kod.

1

Należy również zauważyć, że Control.EndInvoke jest jednym z kilku zarządzanych EndInvokes w ramach (dzięki czemu można zobaczyć kod w Reflectorze). Prawdopodobnie powinni mieć niezainfekowanego pomocnika, który rzuca z oryginalnym stosem w miejscu.

W rzeczywistości, myślę, że jest tylko udało EndInvoke, ale istnieją inne zarządzanych End* procedury z parametrem IAsyncResult. Nie sprawdziłem ich wszystkich, ale wygląda na to, że wszystkie te, które sprawdziłem, rzucają wyjątek lub efektywnie korzystają z rozwiązania przekierowania Stephena Clearego, aby korzystać z .NET 4 w wersji GetWaiter.GetResult, która ma kilka zarządzanych i niezarządzanych szantażystów, aby spróbować uzyskać stos został przywrócony dla wyjątków.

1

Nie jestem pewien, dlaczego sterowania nie to (prawdopodobnie po prostu przeoczenie) zrobić, ale można obejść w .NET 4.0, planując zadanie do postaci ui:

private BackgroundWorker bgw; 
    private TaskFactory uiTaskFactory; 

    private void Form1_Load(object sender, EventArgs e) 
    { 
     this.uiTaskFactory = new TaskFactory(TaskScheduler.FromCurrentSynchronizationContext()); 
     this.bgw = new BackgroundWorker(); 
     this.bgw.DoWork += bgw_DoWork; 
     this.bgw.RunWorkerAsync(); 
    } 

    void bgw_DoWork(object sender, DoWorkEventArgs e) 
    { 
     var task = this.uiTaskFactory.StartNew(this.OuterTaskFunction); 
     try 
     { 
      task.Wait(); 
     } 
     catch (Exception ex) 
     { 
      // Note: Full stack trace preserved 
      MessageBox.Show(ex.InnerException.ToString()); 
     } 
    } 

    void OuterTaskFunction() 
    { 
     this.InnerTaskFunction(); 
    } 

    void InnerTaskFunction() 
    { 
     throw new InvalidOperationException("Blah."); 
    } 
-3

I nie przeczytałem 100% wiadomości, więc nie jestem pewien, czy to pomaga, czy mówię tylko oczywiste rzeczy, , ale gdy zostanie uchwycony wyjątek i napiszesz

"throw iAmAnCaughtExceptionInstance;"

wywołanie stosu nie zostaną zapisane, należy po prostu napisać

„rzucać”;

a następnie stos wywołań jest zapisany

+1

wiem, ale ponieważ nie jestem jedynym, który realizowany Windows.Forms.Control to z niewielką pomocą. –

Powiązane problemy