none
Debug vs. Release Version RRS feed

  • Frage

  • Hallo,

    ich habe hier ein sehr kurioses Problem. Ich programmiere in VB.Net 2008 (.Net FW2). Meine Anwendung funktioniert tadelos wenn ich sie als Debug kompiliere. Sobald ich aber auf Release stelle funktioniert sie nicht mehr. USB Geräte werden von der Software nicht mehr erkannt, der Zugriff schlägt fehl, ... Unreproduzierbar.

    Ich habe keine Schalter drin die den Quelltext grundlegend verändern. Nur so etwas wie:

    ...
    Catch
    #IF DEBUG THEN
        Stop
    #ELSE
        Throw
    #END IF
    End Try

    Daher vermute ich dass es irgendwie an den compilerinternen Optimierungen liegt. Jetzt weiß ich aber nicht wo ich mit der Fehlersuche überhaupt ansetzen könnte. Als Debug Version läuft ja alles fehlerfrei...

    Dienstag, 17. Februar 2015 09:34

Antworten

  • Hallo Tommy,

    Frage 1: Läuft das Programm dann unter x86 (32-Bit) oder x64 (64-Bit). Im 2. Fall dürften Probleme vorprogrammiert sein.

    Punkt 1.: Denn nach der ersten Durchsicht sind einige Strukturen nicht ganz richtig. So u. a. hat DeviceInterfaceData) (SP_DEVICE_INTERFACE_DATA) einen ULONG_PTR als Reserved, was ein IntPtr sein sollte.[1]

    Auch Overlapped sollte mit IntPtr operieren, direkt aus dem .NET Framework "geklaut":

    Namespace System.Threading
    	<ComVisible(True)>
    	Public Structure NativeOverlapped
    		Public InternalLow As IntPtr
    		Public InternalHigh As IntPtr
    		Public OffsetLow As Integer
    		Public OffsetHigh As Integer
    		Public EventHandle As IntPtr
    	End Structure
    End Namespace
    

    (und das waren jetzt nur zwei die mir aufgefallen sind, weitere könnten versteckt sein.)

    Punkt 2: Der Code verwendet SetupDiEnumDeviceInterfaces ohne Fehlerprüfung. Die Rückgabe sagt nur, dass ein Fehler aufgetreten ist. Man sollte via Marshal.GetLastError prüfen, ob es ein "unkritisches" ERROR_NO_MORE_ITEMS ist und in jedem anderen Falle den Fehler protokollieren und/oder eine Win32Exception werfen (und später behandeln).

    Generell zur Fehlerfindung: Du solltest weiteren Trace Code einbauen (wie bereits von anderen empfohlen), um den Verlauf nachvollziehen zu können. Denn bei nativen Funktionen gibt es keine automatische Exception und ein nicht beachteter Error - was für alle DllImport mit SetLastError gilt - kann den Code Verlauf empfindlich stören.

    Ein wichtiger Unterschied beim Release Modus ist, dass der GC Speicher wesentlich früher freigibt - u. U. direkt nach der letzten Verwendung. Wohingegen bei der Debug Version (angekoppeltem Debugger)  i. a. gewartet wird, bis eine Funktion verlassen wurde, um eine Variablen Inspektion zu erlauben. Was unangenehme Nebeneffekte oft erst beim Release-Modus erscheinen lässt.

    Gruß Elmar

    [1] Anstatt (umständlich) Marshal.SizeOf(GetType(IntPtr)) liefert IntPtr.Size den richtigen Wert je nach Plattform.

    • Als Antwort markiert TommyB83 Mittwoch, 18. Februar 2015 08:05
    Dienstag, 17. Februar 2015 18:59
    Beantworter

Alle Antworten

  • An diesem Codeteil kann man sehen, dass du den Fehler, wenn er auftritt, einfach weiter wirfst. Wo wird er aufgefangen, was passiert damit?

    Stürzt die Software ab, oder hängt sie sich auf, oder ignoriert sie einfach alles?


    © 2015 Thomas Roskop

    Germany // Deutschland

    Dienstag, 17. Februar 2015 09:39
  • Hi,

    das war jetzt nur ein Beispiel. Das Stop wird nie erreicht, außer man "rupft" in einem extrem ungünstigen Moment das USB Gerät ab. Ist mir bisher von über 100 Versuchen nur 1x "geglückt". Andere Compilerweichen gibt es nicht.

    Bestandteil der Software sind USB HID Geräte. Mal geht die Auflistung dieser nicht korrekt (Geräte werden ausgelassen), mal geht das Verbinden nicht. Aber wie gesagt, selber Quelltext, als Debug kompiliert geht alles.

    Ansonsten werden Fehler abgefangen. Im Worst Case kommt ein Dialog mit ex.ToString. Kommt aber auch in der Release Version nicht vor. Ich _vermute_ dass es an meiner USB HID Implementation liegen muss, aber warum läuft es fehlerfrei auf x86 und x64 als Debug, aber nicht als Release?

    Dienstag, 17. Februar 2015 09:55
  • Normalerweise dürfte es keinen unterschied zwischen beiden geben. Die Debug-Version ist halt nur aufgeblähter, weil sie debug-infos enthält, aber sonst komplett gleich ist.

    Du meintest, dass das aufzählen/enumerieren fehl schlägt. Könntest du dazu mal den Quellcode [Auszugsweise/Beispielsweise] hier hineinkopieren, vor allem mit der #if DEBUG weiche, um den echten unterschied zu zeigen.

    Mehr wüsste ich jetzt auf der Stelle auch nicht :(

    Vielleicht kann, mit dem Code, jemand noch irgendetwas finden.


    © 2015 Thomas Roskop

    Germany // Deutschland

    Dienstag, 17. Februar 2015 10:36
  • Bei der Release Version werden schon noch ein paar mehr Optimierungen gemacht, als nur die Debug-Infos zu entfernen. Die  Änderungen betreffen aber im allgemeinen die Ausführungsgeschwindigkeit. Ein klassischer Fehler sind da Race-Conditions.  

    Ein Ansatz wahre an den passenden Stellen die wichtigen Informationen zu Loggen, um einzugrenzen was da nicht klappt. (Wenn es eine Race-Condition ist kann überigens das Loggen dazu führen, das sie nicht auftritt.)

    Wie Thomas schon sagt, Auszüge des Quellcodes währen auch nicht schlecht.  

    MFG

    Björn

    • Als Antwort vorgeschlagen Thomas Roskop Dienstag, 17. Februar 2015 13:13
    • Nicht als Antwort vorgeschlagen Thomas Roskop Dienstag, 17. Februar 2015 13:13
    Dienstag, 17. Februar 2015 13:12
  • Hm... Den kompletten Code zu posten wäre zu viel, und darf ich auch nicht (teile sind kommerziell).
    Das ist jetzt nur der USB HID Teil. Leider schon komplex genug, hätte man eigentlich mit im Framework integrieren können...

    In diesem Sinne: Vorsicht, Rattenschwanz :)

    #If DEBUG sind dort aber keine drin. Nur in dem Rest der Anwendung.

    EventArgs.vb:

    Imports System.Windows.Forms
    
    Namespace USB
    
    	''' <summary>Event arguments for an USB HID data event</summary>
    	<ComVisible(False)> Friend NotInheritable Class DataEventArgs
    		Inherits EventArgs
    
    		''' <summary>The recieved or sent data</summary>
    		Friend ReadOnly Data As Byte()
    
    		''' <summary>Creates a new instance of this class</summary>
    		''' <param name="Data">The recieved or sent data</param>
    		Friend Sub New(ByVal Data As Byte())
    
    			Me.Data = Data
    
    		End Sub
    
    	End Class
    
    End Namespace

    HidException.vb:

    Imports System.Runtime.Serialization
    
    Namespace USB
    
    	<Serializable(), ComVisible(False)> Friend Class HidException
    		Inherits ApplicationException
    
    		Protected Sub New(ByVal info As SerializationInfo, ByVal context As StreamingContext)
    			MyBase.New(info, context)
    		End Sub
    
    		Friend Sub New(ByVal message As String)
    			MyBase.New(message)
    		End Sub
    		'Friend Shared Function GenerateError(ByVal message As String) As HidException
    		'	Return New HidException("Msg: " & message)
    		'End Function
    		Friend Shared Function GenerateWithWinError(ByVal message As String) As HidException
    			Return New HidException("Msg: " & message & " WinEr: " & Marshal.GetLastWin32Error.ToString("X8", ICul))
    		End Function
    
    	End Class
    
    End Namespace

    HidManager.vb:

    #Const EnableRead = True
    #Const EnableWrite = True
    
    Imports System.Windows.Forms
    
    Namespace USB
    
    	''' <summary>Manages an USB HID connection</summary>
    	<ComVisible(False)> Friend NotInheritable Class HidManager
    		Inherits NativeWindow ' Because we need WndProc to recieve messages
    		Implements IDisposable
    
    #Region "  Events  "
    
    		''' <summary>Raised if any device on the usb bus is attached</summary>
    		Friend Event DeviceAttached As EventHandler
    
    		''' <summary>Raised if any device on the usb bus is removed</summary>
    		Friend Event DeviceRemoved As EventHandler
    
    		''' <summary>Raised if the specified device is attached</summary>
    		Friend Event Connected As EventHandler
    
    		''' <summary>Raised if the specified device is removed</summary>
    		Friend Event Disconnected As EventHandler
    
    #If EnableRead Then
    
    		''' <summary>Raised if data was recieved from a specific device</summary>
    		Friend Event DataRecieved As EventHandler(Of DataEventArgs)
    
    #End If
    #If EnableWrite Then
    
    		''' <summary>Raised if data was sent to the specific device</summary>
    		Friend Event DataSend As EventHandler(Of DataEventArgs)
    
    #End If
    
    #End Region
    #Region "  Variables  "
    
    		''' <summary>The provided VendorID</summary>
    		Friend ReadOnly VendorID As UInt32
    
    		''' <summary>The provided ProductID</summary>
    		Friend ReadOnly ProductID As UInt32
    
    		''' <summary>If set to true the device will be automatically connected if detected or after a call to CheckForDevice. If false you need to call Connect manually.</summary>
    		Friend AutoConnect As Boolean = True
    
    		' Private stuff
    		Private Shared HidGuid As Guid = NativeMethods.HIDGuid ' Cache (for speedup)
    		Private _IsConnected As Boolean	' True if connected, False otherwise
    		Private DevicePath As String ' The path to the usb device
    		Private FileHandle As IntPtr = NativeTypes.InvalidHandleValue ' The (native) handle to the device
    		Private InputReportLength As Integer ' The input buffer size
    		Private OutputReportLength As Integer ' The output buffer size
    		Private FileStream As IO.FileStream	' The managed FileStream to the device
    
    #End Region
    #Region "  Form stuff & properties  "
    
    		''' <summary>Creates a new instance of this class.</summary>
    		''' <param name="VendorID">The vendor identifier from the USB device.</param>
    		''' <param name="ProductID">The product identifier from the USB device.</param>
    		Friend Sub New(ByVal VendorID As UInt32, ByVal ProductID As UInt32)
    
    			' Copy identifiers
    			Me.VendorID = VendorID
    			Me.ProductID = ProductID
    
    			' Create our handle and thus starting to recieve messages
    			Call CreateHandle(New CreateParams)
    
    		End Sub
    
    		''' <summary>Releases used resources.</summary>
    		Friend Sub Dispose() Implements IDisposable.Dispose
    
    			Call Disconnect()
    			MyBase.ReleaseHandle()
    
    			GC.SuppressFinalize(Me)
    
    		End Sub
    
    		''' <summary>Called if the window handle is created.</summary>
    		''' <param name="cp">The create parameters.</param>
    		''' <remarks>Called by system. Do not call this by yourself.</remarks>
    		''' <exception cref="HidException">Thrown if we can't register for USB events.</exception>
    		Public Overrides Sub CreateHandle(ByVal cp As CreateParams)
    
    			MyBase.CreateHandle(cp)
    
    			Dim UsbHandle As IntPtr = NativeMethods.RegisterForUsbEvents(MyBase.Handle, HidGuid)
    			If UsbHandle = IntPtr.Zero OrElse UsbHandle = NativeTypes.InvalidHandleValue Then Throw New HidException("Can't register for USB events!")
    			Call CheckForDevice()
    
    		End Sub
    
    		''' <summary>Called if the window handle will be destroyed.</summary>
    		''' <remarks>Called by system. Do not call this by yourself.</remarks>
    		Public Overrides Sub DestroyHandle()
    
    			Call NativeMethods.UnregisterForUsbEvents(MyBase.Handle)
    
    			MyBase.DestroyHandle()
    
    		End Sub
    
    		''' <summary>The native window recieved a message.</summary>
    		''' <param name="m">As you may guessed: It's the message.</param>
    		''' <remarks>Called by system. Don't call it by yourself or I kill your cat!</remarks>
    		<System.Diagnostics.DebuggerStepThrough()> Protected Overrides Sub WndProc(ByRef m As Message)
    
    			MyBase.WndProc(m)
    
    			If m.Msg = NativeTypes.WM_DEVICECHANGE Then	' Device was added or removed
    				Select Case m.WParam.ToInt32
    					Case NativeTypes.DEVICE_ARRIVAL				' Added
    						RaiseEvent DeviceAttached(Me, EventArgs.Empty)
    						Call CheckForDevice()
    					Case NativeTypes.DEVICE_REMOVECOMPLETE	' Removed
    						RaiseEvent DeviceRemoved(Me, EventArgs.Empty)
    						Call CheckForDevice()
    				End Select
    			End If
    
    		End Sub
    
    		''' <summary>Returns True if the device is present, otherwise False</summary>
    		Friend ReadOnly Property DevicePresent() As Boolean
    			Get
    				Return Not String.IsNullOrEmpty(DevicePath)
    			End Get
    		End Property
    
    		''' <summary>Returns True if we are connected to a device, otherwise False</summary>
    		Friend ReadOnly Property IsConnected() As Boolean
    			Get
    				Return _IsConnected
    			End Get
    		End Property
    
    #End Region
    #Region "  Accessors  "
    
    		''' <summary>Called if a device was attached or removed. Checks if it is our device.</summary>
    		''' <returns>Returns TriState.True if device was found, TriState.False if no device was found, TriState.UseDefault if a device was found but access is denied (already in use).</returns>
    		Friend Function CheckForDevice() As TriState
    
    			' Get the search string
    			Dim Search As String = String.Format(ICul, "vid_{0:x4}&pid_{1:x4}", VendorID, ProductID)
    
    			' Get device interfaces
    			Dim InfoSet As IntPtr = NativeMethods.SetupDiGetClassDevs( _
    			  HidGuid, _
    			  Nothing, _
    			  IntPtr.Zero, _
    			  NativeTypes.DIGCF_DEVICEINTERFACE Or NativeTypes.DIGCF_PRESENT _
    			)
    
    			Try
    
    				' Loop through all interfaces
    				Dim FoundAny As Boolean = False
    				Dim i As UInt32 = 0UI
    				Dim DeviceInterface As New NativeTypes.DeviceInterfaceData(True)
    				Do While NativeMethods.SetupDiEnumDeviceInterfaces(InfoSet, IntPtr.Zero, HidGuid, i, DeviceInterface)
    
    					' Get device path
    					Dim Details As New NativeTypes.DeviceInterfaceDetailData(True)
    					Dim Size As UInt32 = 0UI
    					If Not NativeMethods.SetupDiGetDeviceInterfaceDetail(InfoSet, DeviceInterface, IntPtr.Zero, 0UI, Size, IntPtr.Zero) Then
    						If NativeMethods.SetupDiGetDeviceInterfaceDetail(InfoSet, DeviceInterface, Details, Size, Size, IntPtr.Zero) Then
    
    							' Check if this is our device
    							If Details.DevicePath.IndexOf(Search, StringComparison.OrdinalIgnoreCase) >= 0 Then
    
    								FoundAny = True
    
    								' Yup, it is. Save path, raise event, connect if autoconnect is true and return true
    								DevicePath = Details.DevicePath
    								If AutoConnect Then Call Connect()
    								Return TriState.True
    
    							End If
    						End If
    
    					End If
    					i += 1UI ' Go to next device
    				Loop
    
    				' Device not found. Set connected to false (just to be sure) and return
    				_IsConnected = False
    				Return If(FoundAny, TriState.UseDefault, TriState.False)
    
    			Catch ex As Exception
    				' The "Ooops" part
    				Throw HidException.GenerateWithWinError(ex.ToString)
    			Finally
    				' In every case release the unmanaged resources
    				NativeMethods.SetupDiDestroyDeviceInfoList(InfoSet)
    			End Try
    
    		End Function
    
    		''' <summary>Connects to the specified device.</summary>
    		''' <remarks>You must call CheckForDevice first!</remarks>
    		Friend Sub Connect()
    
    			' Check if we are already connected. If yes exit.
    			If _IsConnected Then Exit Sub
    
    			' Connect to the device
    			FileHandle = NativeMethods.CreateFile( _
    			  DevicePath, _
    			  NativeTypes.GENERIC_READ Or NativeTypes.GENERIC_WRITE, _
    			  0, _
    			  IntPtr.Zero, _
    			  NativeTypes.OPEN_EXISTING, NativeTypes.FILE_FLAG_OVERLAPPED, _
    			  IntPtr.Zero _
    			)
    			If (FileHandle = Nothing) OrElse (FileHandle = NativeTypes.InvalidHandleValue) Then
    				' File open failed
    				Call Disconnect()
    				Throw HidException.GenerateWithWinError("Failed to create device file. Did you call CheckForDevice first?")
    			End If
    
    			' Connected. Get the device data
    			Dim ptrData As IntPtr
    			If Not NativeMethods.HidD_GetPreparsedData(FileHandle, ptrData) Then
    				' GetPreparsedData failed
    				Call Disconnect()
    				Throw HidException.GenerateWithWinError("GetPreparsedData failed")
    			End If
    
    			Try
    
    				' Get capabilities
    				Dim Caps As New NativeTypes.HidCaps
    				Call NativeMethods.HidP_GetCaps(ptrData, Caps)
    				InputReportLength = Caps.InputReportByteLength
    				OutputReportLength = Caps.OutputReportByteLength
    
    				' Create filestream
    				FileStream = New IO.FileStream( _
    				  New Microsoft.Win32.SafeHandles.SafeFileHandle(FileHandle, False), _
    				  IO.FileAccess.Read Or IO.FileAccess.Write, _
    				  InputReportLength, _
    				  True _
    				)
    
    				' Wohoo, we got it :)
    				_IsConnected = True
    				RaiseEvent Connected(Me, EventArgs.Empty)
    
    #If EnableRead Then
    				' Start async reading
    				Call BeginAsyncRead()
    #End If
    
    			Catch ex As Exception
    				' The "Ooops" thing
    				Throw HidException.GenerateWithWinError("Failed to get the detailed data from the hid.")
    			Finally
    				' In any case clean up unmanaged memory
    				Call NativeMethods.HidD_FreePreparsedData(ptrData)
    			End Try
    
    		End Sub
    
    		''' <summary>Disconnects from the specified device.</summary>
    		Friend Sub Disconnect()
    
    			' Check if we are connected. If not exit.
    			If Not _IsConnected Then Exit Sub
    
    			' Close the unmanaged "file"
    			If (FileHandle <> Nothing) AndAlso (FileHandle <> NativeTypes.InvalidHandleValue) Then
    				NativeMethods.CloseHandle(FileHandle)
    				FileHandle = NativeTypes.InvalidHandleValue
    			End If
    
    			' Close the filestream
    			Try
    				If FileStream IsNot Nothing Then
    					FileStream.Close()
    					FileStream.Dispose()
    					FileStream = Nothing
    				End If
    			Catch ex As IO.IOException
    				' Shutting down, ignore
    			Catch
    				Throw
    			End Try
    
    			' Reset connected state and raise event
    			_IsConnected = False
    			DevicePath = String.Empty
    			RaiseEvent Disconnected(Me, EventArgs.Empty)
    
    		End Sub
    
    #If EnableRead Then
    
    		'''<summary>Strats an asynchronous read which completes when data is read or when the device is disconnected. Uses a callback.</summary>
    		Private Sub BeginAsyncRead()
    
    			Dim Buffer(InputReportLength - 1) As Byte
    			FileStream.BeginRead(Buffer, 0, InputReportLength, New AsyncCallback(AddressOf ReadCompleted), Buffer)
    
    		End Sub
    
    		''' <summary>Callback for BeginAsyncRead. Take care with this as it will be called on the background thread from the async read</summary>
    		''' <param name="iResult">Async result parameter</param>
    		Private Sub ReadCompleted(ByVal iResult As IAsyncResult)
    
    			' Get the data
    			Dim Buffer As Byte() = DirectCast(iResult.AsyncState, Byte())
    
    			Try
    
    				' End the read process if still connected
    				If _IsConnected AndAlso FileStream IsNot Nothing Then
    
    					Try
    
    						' End reading
    						FileStream.EndRead(iResult)
    
    						' Got new data, raise it's event
    						RaiseEvent DataRecieved(Me, New DataEventArgs(Buffer))
    
    					Catch ex As OperationCanceledException
    
    						' We lost the connection
    						Call Disconnect()
    
    					Finally
    
    						' Restart reading if still connected
    						If _IsConnected Then BeginAsyncRead()
    
    					End Try
    
    				End If
    
    			Catch ex As IO.IOException
    
    				' Device was removed. Handle it.
    				Call Disconnect()
    
    			End Try
    
    		End Sub
    
    #End If
    #If EnableWrite Then
    
    		''' <summary>Writes data to the HID device.</summary>
    		''' <param name="Data">The data to write</param>
    		Friend Sub Write(ByVal Data As Byte())
    
    			' Check if we are still connected
    			If Not _IsConnected Then Throw New IO.IOException("Not connected!")
    
    			Try
    
    				' Send the data and flush the cache
    				If Not NativeMethods.HidD_SetFeature(FileStream.SafeFileHandle, Data, Data.Length) Then _
    				  Throw New System.ComponentModel.Win32Exception
    
    			Catch ex As IO.IOException
    
    				' Device was removed. Handle it.
    				Call Disconnect()
    
    			Catch ex As Exception
    
    				' Something else went wrong
    				Throw New Exception("Problem with USB connection :(", ex)
    
    			End Try
    
    		End Sub
    
    #End If
    
    #End Region
    
    	End Class
    
    End Namespace

    Native.vb:

    Imports System.Runtime.InteropServices
    Imports Microsoft.Win32.SafeHandles
    Imports DMM.USB.NativeMethods
    Imports DMM.USB.NativeTypes
    
    Namespace USB
    
    	''' <summary>Class for native calls (P/Invoke)</summary>
    	''' <remarks>Don't touch it. Really. Don't touch this. YOU don't need it. It's only used within USB namespace.</remarks>
    	<ComVisible(False)> Friend NotInheritable Class NativeMethods
    
    		' Deny CreateInstance for this shared class
    		Private Sub New()
    		End Sub
    
    		' kernel32.dll
    		<DllImport("kernel32.dll", SetLastError:=True)> _
    		Friend Shared Function CloseHandle(ByVal hObject As IntPtr) As Integer
    		End Function
    		<DllImport("kernel32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
    		Friend Shared Function CreateFile( _
    		  <MarshalAs(UnmanagedType.LPTStr)> ByVal strName As String, _
    		  ByVal nAccess As UInt32, _
    		  ByVal nShareMode As UInt32, _
    		  ByVal lpSecurity As IntPtr, _
    		  ByVal nCreationFlags As UInt32, _
    		  ByVal nAttributes As UInt32, _
    		  ByVal lpTemplate As IntPtr _
    		) As IntPtr
    		End Function
    
    		' hid.dll
    		<DllImport("hid.dll", SetLastError:=True)> _
    		Friend Shared Function HidD_FreePreparsedData(ByRef pData As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    		End Function
    		<DllImport("hid.dll", SetLastError:=True)> _
    		Friend Shared Sub HidD_GetHidGuid(<Out()> ByRef gHid As Guid)
    		End Sub
    		<DllImport("hid.dll", SetLastError:=True)> _
    		Friend Shared Function HidD_GetPreparsedData(ByVal hFile As IntPtr, <Out()> ByRef lpData As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    		End Function
    		<DllImport("hid.dll", SetLastError:=True)> _
    		Friend Shared Function HidP_GetCaps(ByVal lpData As IntPtr, <Out()> ByRef oCaps As HidCaps) As Integer
    		End Function
    		<DllImport("hid.dll", SetLastError:=True)> _
    		Friend Shared Function HidD_SetFeature(ByVal HidDeviceObject As SafeFileHandle, ByVal lpReportBuffer() As Byte, ByVal ReportBufferLength As Int32) As <MarshalAs(UnmanagedType.Bool)> Boolean
    		End Function
    
    		' user32.dll
    		<DllImport("user32.dll", SetLastError:=True, CharSet:=CharSet.Unicode)> _
    		Friend Shared Function RegisterDeviceNotification(ByVal hwnd As IntPtr, ByVal oInterface As DeviceBroadcastInterface, ByVal nFlags As UInt32) As IntPtr
    		End Function
    		<DllImport("user32.dll", SetLastError:=True)> _
    		Friend Shared Function UnregisterDeviceNotification(ByVal hHandle As IntPtr) As <MarshalAs(UnmanagedType.Bool)> Boolean
    		End Function
    
    		' setupapi.dll
    		<DllImport("setupapi.dll", SetLastError:=True)> _
    		Friend Shared Function SetupDiDestroyDeviceInfoList(ByVal lpInfoSet As IntPtr) As Integer
    		End Function
    		<DllImport("setupapi.dll", SetLastError:=True)> _
    		Friend Shared Function SetupDiEnumDeviceInterfaces( _
    		  ByVal lpDeviceInfoSet As IntPtr, _
    		  ByVal nDeviceInfoData As IntPtr, _
    		  ByRef gClass As Guid, _
    		  ByVal nIndex As UInt32, _
    		  ByRef oInterfaceData As DeviceInterfaceData _
    		) As <MarshalAs(UnmanagedType.Bool)> Boolean
    		End Function
    		<DllImport("setupapi.dll", SetLastError:=True)> _
    		Friend Shared Function SetupDiGetClassDevs( _
    		  ByRef gClass As Guid, _
    		  <MarshalAs(UnmanagedType.LPWStr)> ByVal strEnumerator As String, _
    		  ByVal hParent As IntPtr, _
    		  ByVal nFlags As UInt32 _
    		) As IntPtr
    		End Function
    		<DllImport("setupapi.dll", SetLastError:=True)> _
    		Friend Shared Function SetupDiGetDeviceInterfaceDetail( _
    		  ByVal lpDeviceInfoSet As IntPtr, _
    		  ByRef oInterfaceData As DeviceInterfaceData, _
    		  ByRef oDetailData As DeviceInterfaceDetailData, _
    		  ByVal nDeviceInterfaceDetailDataSize As UInt32, _
    		  ByRef nRequiredSize As UInt32, _
    		  ByVal lpDeviceInfoData As IntPtr _
    		) As <MarshalAs(UnmanagedType.Bool)> Boolean
    		End Function
    		<DllImport("setupapi.dll", SetLastError:=True)> _
    		Friend Shared Function SetupDiGetDeviceInterfaceDetail( _
    		  ByVal lpDeviceInfoSet As IntPtr, _
    		  ByRef oInterfaceData As DeviceInterfaceData, _
    		  ByVal lpDeviceInterfaceDetailData As IntPtr, _
    		  ByVal nDeviceInterfaceDetailDataSize As UInt32, _
    		  ByRef nRequiredSize As UInt32, _
    		  ByVal lpDeviceInfoData As IntPtr _
    		) As <MarshalAs(UnmanagedType.Bool)> Boolean
    		End Function
    
    
    
    		' Santas little helper
    		Friend Shared Function RegisterForUsbEvents(ByVal hWnd As IntPtr, ByVal gClass As Guid) As IntPtr
    
    			Dim Data As New DeviceBroadcastInterface
    			With Data
    				.Size = Marshal.SizeOf(Data)
    				.ClassGuid = gClass
    				.DeviceType = 5
    				.Reserved = 0
    			End With
    			Return RegisterDeviceNotification(hWnd, Data, 0)
    
    		End Function
    		Friend Shared Function UnregisterForUsbEvents(ByVal hHandle As IntPtr) As Boolean
    			Return UnregisterDeviceNotification(hHandle)
    		End Function
    		Friend Shared ReadOnly Property HIDGuid() As Guid
    			Get
    				Dim guid As Guid
    				HidD_GetHidGuid(guid)
    				Return guid
    			End Get
    		End Property
    
    	End Class
    
    
    
    	''' <summary>Class for native types (P/Invoke)</summary>
    	''' <remarks>Don't touch it. Really. Don't touch this. YOU don't need it. It's only used within USB namespace.</remarks>
    	<ComVisible(False)> Friend NotInheritable Class NativeTypes
    
    		' Deny CreateInstance for this shared class
    		Private Sub New()
    		End Sub
    
    
    
    		' Constants (more or less)
    		Friend Const DEVICE_NOTIFY_WINDOW_HANDLE As Integer = 0
    		Friend Const DEVICE_ARRIVAL As Integer = &H8000
    		Friend Const DEVICE_REMOVECOMPLETE As Integer = &H8004
    		Friend Const DEVTYP_DEVICEINTERFACE As Integer = 5
    		Friend Const DIGCF_PRESENT As Integer = 2
    		Friend Const DIGCF_DEVICEINTERFACE As Integer = &H10
    		Friend Const ERROR_IO_PENDING As UInt32 = &H3E5UI
    		Friend Const FILE_FLAG_OVERLAPPED As UInt32 = &H40000000UI
    		Friend Const FILE_SHARE_READ As UInt32 = 1UI
    		Friend Const FILE_SHARE_WRITE As UInt32 = 2UI
    		Friend Const GENERIC_WRITE As UInt32 = &H40000000UI
    		Friend Const GENERIC_READ As UInt32 = &H80000000UI
    		Friend Const INFINITE As UInt32 = UInt32.MaxValue
    		Friend Const OPEN_EXISTING As UInt32 = 3UI
    		Friend Const OPEN_ALWAYS As UInt32 = 4UI
    		Friend Const PURGE_TXABORT As UInt32 = 1UI
    		Friend Const PURGE_RXABORT As UInt32 = 2UI
    		Friend Const PURGE_TXCLEAR As UInt32 = 4UI
    		Friend Const PURGE_RXCLEAR As UInt32 = 8UI
    		Friend Const WM_DEVICECHANGE As Integer = &H219
    		Friend Shared ReadOnly InvalidHandleValue As IntPtr = New IntPtr(-1)
    		Friend Shared ReadOnly NullHandle As IntPtr = IntPtr.Zero
    
    
    
    		' Structures
    		<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Unicode, Pack:=1)> _
    		Friend Class DeviceBroadcastInterface
    			Friend Size As Integer
    			Friend DeviceType As Integer
    			Friend Reserved As Integer
    			Friend ClassGuid As Guid
    			<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=&HFF)> Friend Name As String
    		End Class
    		<StructLayout(LayoutKind.Sequential, Pack:=1)> _
    		Friend Structure DeviceInterfaceData
    			Friend Size As Integer
    			Friend InterfaceClassGuid As Guid
    			Friend Flags As Integer
    			Friend Reserved As Integer
    			Friend Sub New(ByVal SetSize As Boolean)
    				If SetSize Then Size = 24 + Marshal.SizeOf(GetType(IntPtr)) ' 28 on x86, 32 on x64
    			End Sub
    		End Structure
    		<StructLayout(LayoutKind.Sequential, Pack:=1)> _
    		Friend Structure DeviceInterfaceDetailData
    			Friend Size As Integer
    			<MarshalAs(UnmanagedType.ByValTStr, SizeConst:=&H100)> Friend DevicePath As String
    			Friend Sub New(ByVal SetSize As Boolean)
    				If SetSize Then
    					Size = Marshal.SizeOf(GetType(IntPtr))
    					If Size = 4 Then Size = 5 ' 4 + padding byte for x86, 8 for x64
    				End If
    			End Sub
    		End Structure
    		<StructLayout(LayoutKind.Sequential, Pack:=1)> _
    		Friend Structure HidCaps
    			Friend Usage As Short
    			Friend UsagePage As Short
    			Friend InputReportByteLength As Short
    			Friend OutputReportByteLength As Short
    			Friend FeatureReportByteLength As Short
    			<MarshalAs(UnmanagedType.ByValArray, SizeConst:=&H11)> Friend Reserved As Short()
    			Friend NumberLinkCollectionNodes As Short
    			Friend NumberInputButtonCaps As Short
    			Friend NumberInputValueCaps As Short
    			Friend NumberInputDataIndices As Short
    			Friend NumberOutputButtonCaps As Short
    			Friend NumberOutputValueCaps As Short
    			Friend NumberOutputDataIndices As Short
    			Friend NumberFeatureButtonCaps As Short
    			Friend NumberFeatureValueCaps As Short
    			Friend NumberFeatureDataIndices As Short
    		End Structure
    		<StructLayout(LayoutKind.Sequential, Pack:=1)> _
    		Friend Structure Overlapped
    			Friend Internal As UInt32
    			Friend InternalHigh As UInt32
    			Friend Offset As UInt32
    			Friend OffsetHigh As UInt32
    			Friend [Event] As IntPtr
    		End Structure
    
    	End Class
    
    End Namespace
    Dienstag, 17. Februar 2015 13:32
  • Ohh, das ist mal ein leckerer happen Code :D

    Eigentlich hätte auch der Teil mit dem #IF DEBUG gereicht. Also so ein Code:

    #IF DEBUG THEN
       ' DEBUG
    #ELSE
       ' RELEASE
    #END IF 'Heißt das bei VB so?


    © 2015 Thomas Roskop

    Germany // Deutschland


    Dienstag, 17. Februar 2015 14:10
  • Jupp, heißt so :)

    Und es muss irgendwas mit USB zu tun haben, da auch als Release alles geht, außer ich greife auf den o.g. Code zu.

    Also kann es mit #If DEBUG nichts zu tun haben. Eigentlich. Imho. Aber wer weiß was da schief schießt. 

    Dienstag, 17. Februar 2015 14:25
  • Meine Vermutung ist, das dass async Read die Probleme verursacht, wenn du den FileHandel in der Connect Methode neu setzt.  

    Ohne Loggen wirst du aber Probleme haben es herauszubekommen. Einen Log Eintrag in einem Catch zu machen, wo ich die Exeption nicht neu schweiße ist Eigendlich meistens sehr Hilfreich. Und wenn du einen Logger mit verschiedenen Log Level hast, kannst du ihn bei bedarf auch beim Kunden aktivieren.

    MFG

    Björn   

    Dienstag, 17. Februar 2015 14:57
  • Hallo Tommy,

    Frage 1: Läuft das Programm dann unter x86 (32-Bit) oder x64 (64-Bit). Im 2. Fall dürften Probleme vorprogrammiert sein.

    Punkt 1.: Denn nach der ersten Durchsicht sind einige Strukturen nicht ganz richtig. So u. a. hat DeviceInterfaceData) (SP_DEVICE_INTERFACE_DATA) einen ULONG_PTR als Reserved, was ein IntPtr sein sollte.[1]

    Auch Overlapped sollte mit IntPtr operieren, direkt aus dem .NET Framework "geklaut":

    Namespace System.Threading
    	<ComVisible(True)>
    	Public Structure NativeOverlapped
    		Public InternalLow As IntPtr
    		Public InternalHigh As IntPtr
    		Public OffsetLow As Integer
    		Public OffsetHigh As Integer
    		Public EventHandle As IntPtr
    	End Structure
    End Namespace
    

    (und das waren jetzt nur zwei die mir aufgefallen sind, weitere könnten versteckt sein.)

    Punkt 2: Der Code verwendet SetupDiEnumDeviceInterfaces ohne Fehlerprüfung. Die Rückgabe sagt nur, dass ein Fehler aufgetreten ist. Man sollte via Marshal.GetLastError prüfen, ob es ein "unkritisches" ERROR_NO_MORE_ITEMS ist und in jedem anderen Falle den Fehler protokollieren und/oder eine Win32Exception werfen (und später behandeln).

    Generell zur Fehlerfindung: Du solltest weiteren Trace Code einbauen (wie bereits von anderen empfohlen), um den Verlauf nachvollziehen zu können. Denn bei nativen Funktionen gibt es keine automatische Exception und ein nicht beachteter Error - was für alle DllImport mit SetLastError gilt - kann den Code Verlauf empfindlich stören.

    Ein wichtiger Unterschied beim Release Modus ist, dass der GC Speicher wesentlich früher freigibt - u. U. direkt nach der letzten Verwendung. Wohingegen bei der Debug Version (angekoppeltem Debugger)  i. a. gewartet wird, bis eine Funktion verlassen wurde, um eine Variablen Inspektion zu erlauben. Was unangenehme Nebeneffekte oft erst beim Release-Modus erscheinen lässt.

    Gruß Elmar

    [1] Anstatt (umständlich) Marshal.SizeOf(GetType(IntPtr)) liefert IntPtr.Size den richtigen Wert je nach Plattform.

    • Als Antwort markiert TommyB83 Mittwoch, 18. Februar 2015 08:05
    Dienstag, 17. Februar 2015 18:59
    Beantworter
  • Super, scheinbar war das wirklich der Fehler.

    Vielen Dank, auch für die ausführliche Erklärung :)
    Mittwoch, 18. Februar 2015 08:05