2017-01-19 7 views
0

Ich habe eine .NET-Anwendung entwickelt, die ein Druckgerät mit 120 Hz abfragen muss. Die Standard-.NET-Timer scheinen nur bis zu 60 Hz zu erreichen, daher entschied ich mich, die Win32-API CreateTimerQueueTimer über PInvoke zu verwenden. Es funktioniert gut, aber die Debugging-Erfahrung ist sehr schlecht, weil die Timer sogar ausgelöst werden, wenn ich das Programm durchtrete, während das Programm gehalten wird. Ich schrieb ein minimales Beispiel in C und in C# und das unerwünschte Verhalten tritt nur auf C# auf. Das C-Programm erstellt die Timer-Callback-Threads nicht, während der Debugger das Programm angehalten hat. Kann mir jemand sagen, was ich tun kann, um das gleiche Debugging-Verhalten in C# zu erreichen?Windows Timer Queue Timer läuft nach .NET Breakpoint

C-Code:

#include <stdio.h> 
#include <assert.h> 
#include <Windows.h> 

int counter = 0; 

VOID NTAPI callback(PVOID lpParameter, BOOLEAN TimerOrWaitFired) 
{ 
    printf("Just in time %i\n", counter++); 
} 

int main() 
{ 
    HANDLE timer; 
    BOOL success = CreateTimerQueueTimer(&timer, NULL, callback, NULL, 0, 1000, WT_EXECUTEDEFAULT); 
    assert(FALSE != success); // set breakpoint at this line and wait 10 seconds 
    Sleep(1000); 
    success = DeleteTimerQueueTimer(NULL, timer, NULL); // step to this line 
    assert(FALSE != success); 
    return 0; 
} 

C result

Equivalent C# -Code:

using System; 
using System.Runtime.InteropServices; 

class TimerQueue 
{ 
    delegate void WAITORTIMERCALLBACK(IntPtr lpParameter, bool TimerOrWaitFired); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    static extern bool CreateTimerQueueTimer(
     out IntPtr phNewTimer, 
     IntPtr TimerQueue, 
     WAITORTIMERCALLBACK Callback, 
     IntPtr Parameter, 
     uint DueTime, 
     uint Period, 
     uint Flags); 

    [DllImport("kernel32.dll", SetLastError = true)] 
    [return: MarshalAs(UnmanagedType.Bool)] 
    static extern bool DeleteTimerQueueTimer(
     IntPtr TimerQueue, 
     IntPtr Timer, 
     IntPtr CompletionEvent); 

    static int counter = 0; 

    static void Callback(IntPtr lpParameter, bool TimerOrWaitFired) 
    { 
     Console.WriteLine("Just in time {0}", counter++); 
    } 

    static void Main(string[] args) 
    { 
     WAITORTIMERCALLBACK callbackWrapper = Callback; 
     IntPtr timer; 
     bool success = CreateTimerQueueTimer(out timer, IntPtr.Zero, callbackWrapper, IntPtr.Zero, 0, 1000, 0); 
     System.Diagnostics.Debug.Assert(false != success); // set breakpoint at this line and wait 10 seconds 
     System.Threading.Thread.Sleep(1000); 
     success = DeleteTimerQueueTimer(IntPtr.Zero, timer, IntPtr.Zero); // step to this line 
     System.Diagnostics.Debug.Assert(false != success); 
    } 
} 

C# result

By the way, ich weiß, es ist eine Race-Bedingung, wenn das ungeschützte Verwendung Zählervariable aus mehreren Threads, das ' Es ist momentan nicht wichtig. Der Schlaf für eine Sekunde ist unabhängig vom Warten nach dem Erreichen des Haltepunkts gemeint und scheint notwendig zu sein, da die Rückrufe nicht sofort in den Prozess eingereiht werden, selbst wenn das Programm in einem Debugger gestartet wird, aber nur nach einer kurzen Verzögerung.

Der Aufruf von DeleteTimerQueueTimer ist nicht wirklich notwendig, um mein Problem anzuzeigen, da es auftritt, bevor diese Zeile ausgeführt wird.

+0

'// ... 10 Sekunden warten' gefolgt von 'Sleep (1000);' ist inkohärent. Meinst du, 10 Sekunden zu schlafen, aber entschieden, nur 1 zu schlafen? Hast du versehentlich nur 1 Sekunde geschlafen? Haben Sie einen anderen als den von Ihnen verwendeten Code eingegeben? – IInspectable

+0

'die Timer werden sogar ausgelöst, wenn ich durch das Programm gehe. '- das und muss sein.und 'DeleteTimerQueueTimer' kann' FALSE' mit dem Fehler 'ERROR_IO_PENDING' zurückgeben – RbMm

+0

Sie durchlaufen das Programm in eigenen Threads, während dieser Zeit Timer-Ereignissignal im Pool-Arbeits-Thread, wenn Kontext zu diesem Thread - Ihre' Callback' aufgerufen . das ist absolut normal und muss sein. Natürlich Debug-Code Bused auf periodische Timer-Ereignisse kann nicht einfach – RbMm

Antwort

0

Sie sollten in der Lage sein, in die Debug-> Windows-> Threads Fenster und Freeze alle Threads, wenn Sie treten.

+0

Dies hat das Problem nicht gelöst. Hier ist das, was ich habe: 1) bewegen und schlug einen Haltepunkt auf der Create Linie 2) Frieren alle Themen mit Ausnahme des Haupt-Thread 3) Schritt eine Codezeile (Timer ist jetzt aktiv) 4) 10 Sekunden warten 5) Schritt zwei Zeilen Code, so dass der Schlaf (1000) ausgeführt wird Ergebnis: Eine Vielzahl von Threads wurden erstellt, wie zuvor. – Spacy

+0

Ok - Das liegt daran, dass Sie einen nicht verwalteten Thread über Win32 erstellen, damit der .NET-Debugger die Threads nicht einfrieren kann. Entschuldigung - kann keine Alternative anbieten. – PhillipH

-1

Da das C direkt auf Funktionen des Systems (kernel32) zugreifen kann, ist es im Vergleich zu C# direkter und weniger sicher. C# wird zur Laufzeit und alle Aufrufe an die Außenwelt (DLLs/COM) kompiliert, es muss Wrapper vom Betriebssystem (Windows) verwendet werden.

Wie PhillipH schrieb: Überprüfen Sie das Thread-Fenster während Haltepunkt. In C# -Code werden Sie sehen, dass es Threads wie RunParkingWindow und .NET SystemEvent gibt.

Im Vergleich mit C-Programm gibt es nur 1 Thread - der, den Sie gerade zum Debuggen gestoppt haben.

Das wird Ihnen klar sein Sie werden verstehen, dass der Breakpoint im Debug nur den Thread stoppt, auf dem Sie sich gerade befinden, aber die anderen Threads werden weiter laufen. Das bedeutet auch, dass die Wrappers provided by OS weiterhin alle Ereignisse abhören und sie nur in die Warteschlange stellen. Sobald Ihre App weiter ausgeführt wird, werden sie alle möglichen Möglichkeiten darauf werfen und die Callback-Methode erledigt den Rest.

Ich habe versucht, alle relevanten Artikel/Bilder mit Datenfluss zu finden, aber ich war nicht erfolgreich. Bitte beachten Sie den obigen Text nur als eine Meinung mehr als einen technischen Absatz.

+0

Auch wenn ich die RunParkingWindow- und .NET SystemEvent-Threads einfriere, befinden sich die Callbacks immer noch in der Warteschlange für meinen Prozess. Ich nehme an, dass dieses Problem nichts mit den Threads zu tun hat, die für .NET erforderlich sind. Ich vermute, dass es eine Art API gibt, die native C-Debugger aufrufen, um Windows-Timer anzuhalten, was der .NET-Laufzeit-Debugger nicht tut. – Spacy