VB .NET: Advanced logging method

The following function adds aMessage to the applications DefaultLogWriter. The DefaultLogWriter is a nice little feature included in Visual Basic .NET which allows you to automatically create a log file in the "Documents and Settings" folder of your application to which you can add your log entries via WriteLine(aMessage).

The function below also adds some more information to the log entry itself. It adds the current date and time and also retrieves the calling method from stack, which can be useful for tracking down a bug (thanks go out to skybound.ca for his help here).

The output could look like this:

"23.08.2008 16:15:43 : CreateListViewItem : Added new item"

Public Shared Sub AddToLog(ByVal aMessage As String)
 
    If Not My.Settings.Logging Then Exit Sub
 
    Dim nle As String ' new list entry
    Dim acn As String ' A caller name

    ' if logging of stack is enabled put together some info
    ' note: i put this as an option to the application.settings because when adding many entries to the log you might want to speed up things a bit by disabling logging the stack info
    If My.Settings.LogStack Then
 
      Dim sf As System.Diagnostics.StackFrame
      ' the calling method is always frame 1 of the call stack (the current method is frame 0)
      sf = New System.Diagnostics.StackTrace().GetFrame(1)
      acn = sf.GetMethod.ReflectedType.Name & "." & sf.GetMethod.Name
 
    Else
      acn = ""
    End If
 
    ' setup log string
    nle = Now.ToShortDateString & " : " & Now.ToShortTimeString & " : " & acn & " : " & aMessage
 
    ' Add to log
    ' note: you could also add the nle string to a listbox or something else...
    My.Application.Log.DefaultFileLogWriter.WriteLine(nle)
    My.Application.Log.DefaultFileLogWriter.Flush()
  End Sub

VB .NET: Binary compare files

This little function compares two files byte for byte and returns true if they are equal.

Public Shared Function Compare(ByVal aFilename1 As String, ByVal aFilename2 As String) As Boolean
 
      Dim fs1 As New System.IO.FileStream(aFilename1, IO.FileMode.Open, IO.FileAccess.Read)
      Dim fs2 As New System.IO.FileStream(aFilename1, IO.FileMode.Open, IO.FileAccess.Read)
 
      If fs1.Length <> fs2.Length Then
        Return False
      End If
 
      For i As Integer = 0 To fs1.Length - 1 Step i + 1
        If fs1.ReadByte <> fs2.ReadByte Then
          Return False
        End If
      Next
 
      fs1.Close()
      fs2.Close()
 
      Return True
    End Function

VB .NET Felder / Properties einer Klasse dynamisch mit einem SQLDataReader befuellen

Manche kennen vielleicht das Problem, auf das man beim Befüllen von Feldern einer bestimmten Klasse durch einen SQLDataReader stößt. Und zwar muss man jedes mal, wenn man die Klasse um ein Feld oder eine Property erweitert, an der entsprechenden Stelle im Code auch dessen Wert zuweisen (zumindest, wenn man sich nicht vom Visual Studio Assistenten eine Klasse bzw. ein Dataset erstellen lässt).

Bisher habe ich sowas für kleinere Programme in etwa wie folgt gemacht:

Public Class Mitarbeiter
  ' Hier eine Beispiel-Klasse für einen Mitarbeiter, 
  ' den wir aus der Datenbank auslesen möchten.
  Public VORNAME As String
  Public NACHNAME As String
  Public ANREDE As String
  Public TELEFON As String
  Public TELEFAX As String
  Public MAIL As String
  Public ART As String
  Public STATUS As String
End Class
Public Class MitarbeiterListe
  ' Dies ist die Liste aller Mitarbeiter aus der Datenbank, 
  ' die wir einlesen wollen.
  ' Beim Erstellen einer neuen Instanz der Klasse MitarbeiterListe, 
  ' wird die Datenbank geöffnet und für jede
  ' zurückgelieferte Zeile eine neue Instanz der Klasse Mitarbeiter 
  ' erstellt und ihr die Werte der verschiedenen Spalten zugewiesen.
  Inherits List(Of Mitarbeiter)
 
  Public Sub New()
    ' Als erstes versuchen wir die Verbindung zur Datenbank zu öffnen
    Try
 
      Using con As New SqlClient.SqlConnection(My.Settings.SQLConnectionString)
 
        Try
          con.Open()
        Catch ex As Exception
          MsgBox(ex.Message, MsgBoxStyle.Critical)
          Exit Sub
        End Try
 
        ' Dann erstellen wir den Command zur Auflistung unserer Mitarbeiter-Query
        Dim cmd As New SqlClient.SqlCommand("Select * From viewMitarbeiter", con)
 
        ' und führen die Abfrage aus
        Using dr As SqlClient.SqlDataReader = cmd.ExecuteReader
 
          ' Nun gehen durch alle zurückgelieferten Datensätze
          Do While dr.Read
 
            ' Neuen Mitarbeiter erstellen
            Dim itm As New Mitarbeiter
 
            ' Werte einzeln den jeweiligen Fields zuweisen
            with itm
              .Vorname = dr.Item("VORNAME")
              .Nachname = dr.Item("NACHNAME")
              .Telefon = dr.Item("TELEFAX")
              .Telefax = dr.Item("TELEFAX")
              ' usw. usf.
            end with
 
            ' Und zur Liste hinzufügen
            Me.Add(itm)
 
          Loop
 
        End Using
 
      End Using
 
    Catch ex As Exception
      MsgBox(ex.Message, MsgBoxStyle.Critical)
      Exit Sub
    End Try
  End Sub
 
End Class

Mittlerweile mache ich das wie folgt:

Public Class MitarbeiterListe
 
  Inherits List(Of Mitarbeiter)
 
  Public Sub New()
 
    ' Als erstes versuchen wir die Verbindung zur Datenbank zu öffnen
    Try
 
      Using con As New SqlClient.SqlConnection(My.Settings.SQLConnectionString)
 
        Try
          con.Open()
        Catch ex As Exception
          MsgBox(ex.Message, MsgBoxStyle.Critical)
          Exit Sub
        End Try
 
        ' Dann erstellen wir den Command zur Auflistung unserer Mitarbeiter-Query
        Dim cmd As New SqlClient.SqlCommand("Select * From viewMitarbeiter", con)
 
        ' und führen die Abfrage aus
        Using dr As SqlClient.SqlDataReader = cmd.ExecuteReader
 
          ' Nun gehen durch alle zurückgelieferten Datensätze
          Do While dr.Read
 
            ' Neuen Mitarbeiter erstellen
            Dim itm As New Mitarbeiter
 
            ' Werte dynamisch den Fields zuweisen
            DataFiller.SetFieldsFromDataReader(itm, dr)
 
            ' Und zur Liste hinzufügen
            Me.Add(itm)
 
          Loop
 
        End Using
 
      End Using
 
    Catch ex As Exception
      MsgBox(ex.Message, MsgBoxStyle.Critical)
      Exit Sub
    End Try
 
  End Sub
 
End Class

Und hier nun meine neue Klasse, die aus der Klasse aObj sämtliche Felder liest und ihr die Werte dynamisch aus dem übergebenen SQLDataReader zuweist.

Public Class DataFiller
 
  Public Shared Sub SetFieldsFromDataReader(ByVal aObj As Object, ByVal aDataReader As SqlClient.SqlDataReader)
    Dim fis As System.Reflection.FieldInfo() = aObj.GetType().GetFields()
 
    ' For each column in the datareader
    For i As Integer = 0 To aDataReader.FieldCount - 1
 
      ' Only assign if we have a value
      If Not aDataReader.IsDBNull(i) Then
 
        ' Check for Field-Name match for each field in our aObj Class
        For Each fi As System.Reflection.FieldInfo In fis
 
          If fi.Name = aDataReader.GetName(i) Then
 
            Try
              fi.SetValue(aObj, aDataReader.Item(i), Reflection.BindingFlags.SetField, Nothing, Nothing)
              Exit For
            Catch ex As Exception
              MsgBox(ex.Message, MsgBoxStyle.Critical)
            End Try
 
          End If
        Next
      End If
    Next
  End Sub
 
End Class

Was meine obige Methode SetFieldsFromDataReader beispielsweise noch nicht kann, ist mit Properties zu arbeiten. Sie arbeitet lediglich mit Feldern. Auch muss man dringend noch die ein oder andere Fehlerbehandlung integrieren. Und optimieren lässt sich auch noch einiges beim Durchlaufen der Schleife. Beispielsweise könnte man vor dem Aufruf von SetFieldsFromDataReader alle Fields der jeweiligen Klasse auslesen und mit übergeben. So würden dann nicht jedes Mal beim Aufruf der Methode immer wieder die Fields ermittelt...

Sollte jemand eine einfachere, oder bessere Lösung haben, würde ich mich sehr freuen, darüber zu hören.

Update am 01.12.2009:

Da ich immer häufiger auf die obigen Funktionen zugreife, habe ich meine Klasse DataFiller mittlerweile etwas ergänzt. Wer möchte, kann sich den Quellcode hier herunter laden.

VB .NET Properties / Felder einer Klasse mit Reflection ermitteln

Mit den folgenden Beispiel Methoden kann man von einer Instanz einer Klasse sämtliche Properties bzw. Felder mit Hilfe von System.Reflection auflisten lassen.

Public Shared Sub GetProperties(ByVal aObj As Object)
    Dim pis As System.Reflection.PropertyInfo() = aObj.GetType().GetProperties()
    For Each pi As System.Reflection.PropertyInfo In pis
      MsgBox(pi.Name)
    Next
  End Sub
 
  Public Shared Sub GetFields(ByVal aObj As Object)
    Dim fis As System.Reflection.FieldInfo() = aObj.GetType().GetFields()
    For Each fi As System.Reflection.FieldInfo In fis
      MsgBox(fi.Name)
    Next
  End Sub

Beispiel:

Public Class person
  public Name as String
  public Alter as Integer
End Class
public Sub ShowFields
  dim p as new Person
  GetFields(p)
End Sub

Ich arbeite gerade an einer Klasse, die dynamisch aus einem DataReader die Properties einer Klasse befüllt. Sobald das fertig ist, gibt es ein etwas komplexeres Beispiel für .NET Reflection.

Update am 01.12.2009:

Da ich immer häufiger auf die obigen Funktionen zugreife, habe ich meine Klasse DataFiller mittlerweile etwas ergänzt. Wer möchte, kann sich den Quellcode hier herunter laden.

VB .NET - Microsoft Word Dokument Variablen auslesen

Um aus einem Word-Dokument die Variablen auszulesen genügt folgender Code und das Einbinden des Verweises "Microsoft Office 9.0" (bzw. höher).

Public Shared Function GetVariables(ByVal aFilename As String) As List(Of String)
 
    Dim result As New List(Of String)
 
    If System.IO.File.Exists(aFilename) Then
 
      Dim wa As New Word.Application
      Dim wd As New Word.Document
 
      Try
        wd = wa.Documents.Open(aFilename)
 
        If wd.Variables.Count > 0 Then
          For Each var As Word.Variable In wd.Variables
            result.Add(var.Name & " = " & var.Value)
          Next
        Else
          result.Add("Keine Variablen in Dokument " & aFilename)
        End If
 
        wd.Close()
        wa.Quit()
 
      Catch ex As Exception
        result.Add(ex.Message)
      End Try
 
    Else
      result.Add("Datei nicht gefunden " & aFilename)
    End If
 
    Return result
  End Function

VB .NET: Dateien mit PDFCreator drucken

Mit den folgenden Funktionen kann man mit Visual Basic .NET unter Verwendung der PDFCreator Library (Verweis!) PDF-Dateien drucken.

Die erste Funktion schickt dabei einfach alle gefundenen Dateien eines beliebigen Verzeichnisses an die Funktion "PrintPDFFile", welche letztendlich die PDF Datei erstellt.

Public Sub PrintDirectory(ByVal aSourceDirectory As String, ByVal aFileFilter As String)
 
    Dim lstFiles As System.Collections.ObjectModel.ReadOnlyCollection(Of String) = _
    My.Computer.FileSystem.GetFiles(aSourceDirectory, FileIO.SearchOption.SearchAllSubDirectories, aFileFilter)
 
    Dim fi As System.IO.FileInfo
    Dim fnnew As String
    Dim dirnew As String
 
    Dim pdfjob As New PDFCreator.clsPDFCreator
 
    If pdfjob.cStart("/NoProcessingAtStartup") = False Then
      MsgBox("Can't initialize PDFCreator.", vbCritical & vbOKOnly)
      Exit Sub
    End If
 
    For Each fn As String In lstFiles
      ' Create new filename
      fi = My.Computer.FileSystem.GetFileInfo(fn)
      fnnew = fi.Name
      fnnew = Replace(fnnew, fi.Extension, "") ' Remove old extension. Don't add new extension!
      dirnew = fi.Directory.FullName & "\"
 
      Me.PDFPrintFile(pdfjob, fi.FullName, fnnew, dirnew)
    Next
 
    pdfjob.cClose()
    pdfjob = Nothing
  End Sub
Public Sub PDFPrintFile(ByRef aPDFJob As PDFCreator.clsPDFCreator, ByVal aFilename As String, ByVal aOutputFilename As String, ByVal aOutputPath As String)
 
    ' Exit it output filename already exists
    If System.IO.File.Exists(aOutputPath & aOutputFilename) Then
      Exit Sub
    End If
 
    ' Create Outputdirectory if not existant
    If Not System.IO.Directory.Exists(aOutputPath) Then
      System.IO.Directory.CreateDirectory(aOutputPath)
    End If
 
    With aPDFJob
      .cOption("UseAutosave") = 1
      .cOption("UseAutosaveDirectory") = 1
      .cOption("AutosaveDirectory") = aOutputPath
      .cOption("AutosaveFilename") = aOutputFilename
      .cOption("AutosaveFormat") = 0    ' 0 = PDF
      .cClearCache()
    End With
 
    'Print the document to PDF
    aPDFJob.cPrintFile(aFilename)
 
    'Wait until the print job has entered the print queue
    Do Until aPDFJob.cCountOfPrintjobs = 1
      ' My.Application.DoEvents.DoEvents()
    Loop
 
    aPDFJob.cPrinterStop = False
 
    'Wait until PDF creator is finished then release the objects
    Do Until aPDFJob.cCountOfPrintjobs = 0
      ' My.Application.DoEvents()
    Loop
 
  End Sub

Aktualisierung vom 03.08.2009:

Wie ich heute leider mal wieder feststellen musste, funktioniert obiger Code bei einer großen Menge an umzuwandelnden Dateien nicht sonderlich.

Das unten aufgeführte "Modul" hat bei mir soeben 1400 Word-Dokumente erfolgreich in PDF umgenwandelt.

Public Module modPrintPDF
 
  Public Sub Main()
 
    Dim strDir As String = "C:\Word\Serienbriefe\Einzelbriefe"
    PrintDirectoryPDF(strDir, "*.doc", strDir)
 
  End Sub
 
  Public Function PrintDirectoryPDF(ByVal aInputDirectory As String, ByVal aFilter As String, ByVal aOutputDirectory As String)
 
    Dim lstFiles As System.Collections.ObjectModel.ReadOnlyCollection(Of String) = _
    My.Computer.FileSystem.GetFiles(aInputDirectory, FileIO.SearchOption.SearchAllSubDirectories, aFilter)
 
    Dim pdfc As New PDFCreator.clsPDFCreator
    pdfc.cStart(, True)
 
    For Each fn As String In lstFiles
 
      ' Generate New Filename
      Dim fi As New IO.FileInfo(fn)
      Dim strNewFilename As String = fi.Name
      strNewFilename = Replace(strNewFilename, fi.Extension, ".pdf")
 
      ' Print File
      PrintPDF(pdfc, fn, aOutputDirectory, strNewFilename)
    Next
 
    Return True
 
  End Function
 
  Public Function PrintPDF(ByRef aPDFCreator As PDFCreator.clsPDFCreator, ByVal aPDFFile As String, ByVal aOutputPath As String, ByVal aOutputFilename As String) As Integer
 
    With aPDFCreator
      .cOption("UseAutosave") = 1
      .cOption("UseAutosaveDirectory") = 1
      .cOption("AutosaveFormat") = 0 ' 0 = PDF
      .cOption("AutosaveDirectory") = aOutputPath
      .cOption("AutosaveFilename") = aOutputFilename
      .cClearCache()
      .cPrintFile(aPDFFile)
    End With
 
  End Function
 
End Module

VB .NET - Strings mit Google uebersetzen

Das ganze habe ich aus dem Internet von Piyush Sha's Blog und noch etwas abgeändert, da der Code bei mir in der Praxis leider nicht funktioniert hat.

Erst die Sprach-Kürzel als Enum:

Public Enum eLocales
  ar
  bg
  hr
  cs
  da
  nl
  en
  fi
  fr
  de
  l
  hi
  ja
  ko
  no
  pl
  pt
  ro
  ru
  es
  sv
End Enum

Und hier die eigentliche Funktion die für die Übersetzung zuständig ist:

''' <summary>
  ''' Translates a text using the Google-API
  ''' </summary>
  ''' <param name="TextToTranslate"></param>
  ''' <param name="lngInput">Input Language</param>
  ''' <param name="lngOutput">Output Language</param>
  ''' <returns>The translated text</returns>
  ''' <remarks></remarks>
  Public Function TranslateText(ByVal TextToTranslate As String, ByVal lngInput As String, ByVal lngOutput As String) As String
    Dim result As String
 
    Try
      Dim url As String = [String].Format("http://www.google.com/translate_t?hl=en&ie=UTF8&text={0}&langpair={1}|{2}", TextToTranslate, lngInput, lngOutput)
      Dim webClient As New Net.WebClient()
      webClient.Encoding = System.Text.Encoding.Default
 
      result = webClient.DownloadString(url)
 
      Dim match As String = "id=result_box"
      Dim i As Integer = result.IndexOf(match) + 20
      Dim f As Integer = result.IndexOf(match) + 500
 
      result = Mid(result, i, f)
      result = Mid(result, result.IndexOf(">") + 2, Len(result))
      result = Mid(result, 1, result.IndexOf("</div>"))
 
      result = MakeHTMLValid(result)
    Catch ex As Exception
      result = String.Empty
    End Try
 
    Return result
 
  End Function

Update vom 28.02.2009:

Hier noch die kleine Hilfsfunktion um HTML-Zeichen einigermaßen valide zu machen. Ich weiß, dass das keine wirklich professionelle Lösung ist, daher habe ich den Code ursprünglich auch nicht gepostet. Da aber Nachfragen kamen, anbei als Ergänzung:

''' <summary>
  ''' Format HTML Code a bit
  ''' </summary>
  ''' <param name="aString">The text to format</param>
  ''' <returns></returns>
  ''' <remarks></remarks>
  Public Shared Function MakeHTMLValid(ByVal aString As String) As String
 
    Dim result As String = aString
 
    ' Replace Entities
    result = result.Replace("Ö", "Ö")
    result = result.Replace("ö", "ö")
    result = result.Replace("Ä", "Ä")
    result = result.Replace("ä", "ä")
    result = result.Replace("Ü", "Ü")
    result = result.Replace("ü", "ü")
    result = result.Replace("ß", "ß")
 
    result = result.Replace("€", "€")
 
    Return result
 
  End Function
← Zurück  1  3 4 5 6 Weiter →