Projekt Vorschau

Hier eine kleine Vorschau des Projekts an dem ich gerade arbeite.

Wie man hoffentlich sieht, handelt es sich um einen App Launcher, also so etwas wie Launchy. Wenn das Projekt fertig ist, soll das Programm aber mehr können, als nur Programme zu öffnen. Ich habe folgendes geplant:

  • Starten, Beenden (und Bearbeiten) von Programmen
  • Öffnen und Bearbeiten von Dateien
  • Arbeiten mit markierten Dateien, Ordnern und Text
  • Index Unterstützung, d. h. eigene Indexer können beliebige Objekte (wie Mails, Termine, usw.) indexieren, mit denen dann gearbeitet werden kann.
  • Plugin Unterstützung, d. h. eigene Aktionen für Objekte können geschrieben werden. Beispielsweise könnte ein Mail Plugin E-Mails an indexierte Kontakte versenden, ein Packer-Plugin könnte markierte Ordner zipppen und via FTP-Plugin hochladen
  • Skin Unterstützung
  • Einfache(ste) Bedienung

Das wichtigste an diesem Programm wird die einfache Bedienung sein (auch wenn ich das als letzten Punkt in der Liste aufgeführt habe). So soll es mit nur wenigen Tastendrücken möglich sein, beispielsweise in Windows markierte Dateien zu zippen, zu verschlüsseln und anschließend auf einen FTP Server zu laden.

Dabei werden Plugins miteinander verknüpft die unterschiedliche Objekte an das nächste vom Benutzer ausgewählte Plugin zurückliefern.

Gemäß obigem Beispiel:

Ausgewählte Dateien -> Plugin: ZIP -> Gezippte Dateien -> Plugin: Verschlüsselung -> Verschlüsselte Dateien -> Plugin: FTP -> Auswahl FTP-Verbindungsdaten -> Fertig.

Wer Quicksilver für den Mac kennt, wird sicherlich viele Gemeinsamkeiten entdecken, was daran liegt, dass Quicksilver für mich eine Art inspirierende Vorlage war (was es für Windows Alternativen zu Quicksilver gibt, hat Lifehacker übrigens in diesem Artikel zusammengefasst).

Bis das alles so funktioniert wie ich das gerne hätte, muss ich aber noch einiges an Arbeit in das Projekt stecken. Ein Schritt in diese Richtung ist allerdings bereits gemacht.

VB .NET - Displayname / Beschreibung einer Datei ermitteln

Ich wollte für ein Programm an dem ich gerade arbeite statt der Dateiendung die Beschreibung zu der Dateiendung anzeigen.

Windows selbst zeigt diese Beschreibung beispielsweise in der Statusleiste des Explorers, wenn man auf eine bekannte Datei klickt. Beispielsweise bei "*.MOV" Dateien wäre die Anzeige (je nachdem mit welchem Programm die Endung verknüpft ist) "VLC media file (.mov)" oder "Quicktime Movie".

Nunja, als ich noch mit Delphi programmierte hätte ich hierzu die Shell32 SHGetFileInfo Funktion verwendet, doch unter .NET binde ich ungerne DLLs ein, also habe ich mir eine Funktion gebastelt, die die Info aus der Registry ausliest.

''' <summary>
  ''' Retrieve the Description/Displayname for a FileExtension
  ''' </summary>
  ''' <param name="aExtension">The Extension with a leading dot</param>
  ''' <returns>The Description of an Extension</returns>
  ''' <remarks></remarks>
  Public Shared Function GetDisplayNameFromExtension(ByVal aExtension As String) As String
 
    If aExtension = String.Empty Or Not aExtension.StartsWith(".") Then Return String.Empty
 
    ' First the get the key for the extension
    ' Extension is for example ".MOV", Key is "VLC.mov"
    Dim key As Microsoft.Win32.RegistryKey = My.Computer.Registry.ClassesRoot.OpenSubKey(aExtension)
 
    If key IsNot Nothing Then
 
      ' Then we look in the sub key for the description
      Dim SubkeyName As String = key.GetValue(String.Empty)
      Dim SubKey As Microsoft.Win32.RegistryKey = My.Computer.Registry.ClassesRoot.OpenSubKey(SubkeyName)
      If SubKey IsNot Nothing Then
        Return SubKey.GetValue(String.Empty)
      End If
 
    End If
 
    Return String.Empty
  End Function

Beispielaufruf:

Msgbox(GetDisplayNameFromExtension(".zip"))

VB .NET - Mit Lotus Notes E-Mails und Dateianhängen arbeiten

Mit den folgenden Klassen ist es möglich, aus einer Domäne heraus, eine beliebige Lotus Notes-Datenbank zu öffnen und alle E-Mails darin aufzulisten. Es werden dabei alle Dateianhänge aus den E-Mails in eine Liste gefügt.

Wie man E-Mails über einen Lotus Domino Server versendet, habe ich hier beschrieben.

Ein Beispielaufruf findet sich am Ende dieses Artikels.

Public Class NotesMail
 
  Public Subject As String
  Public From As String
  Public Body As String
  Public AttachmentFilenames As New List(Of Domino.NotesEmbeddedObject)
 
End Class
 
Public Class NotesMailCollection
  Inherits List(Of NotesMail)
End Class
 
Public Class MailAccount
  Public Host As String
  Public DatabaseFilename As String
  Public Password As String
End Class
 
Public Class NotesTools
 
  Public Shared Function GetMails(ByVal aMailAccount As MailAccount, ByVal MoveMails As Boolean) As NotesMailCollection
    Try
      Return _GetMails(aMailAccount, MoveMails)
    Catch ex As Exception
      MsgBox(ex.Message, MsgBoxStyle.Critical)
      Return Nothing
    End Try
  End Function
 
  Private Shared Function _GetMails(ByVal aMailAccount As MailAccount, ByVal MoveMails As Boolean) As NotesMailCollection
 
    ' Die Session starten
    Dim Session As New Domino.NotesSession
    Session.Initialize(aMailAccount.Password)
 
    ' Datenbank öffnen
    Dim MailDB As Domino.NotesDatabase
    Dim MailServer As String = Session.GetEnvironmentString("MailServer", True)
    ' Dim DatabaseFilename As String = Session.GetEnvironmentString("MailFile", True)

    MailDB = Session.GetDatabase(MailServer, aMailAccount.DatabaseFilename)
 
    If Not MailDB.IsOpen Then
      MailDB.Open()
    End If
 
    Dim result As New NotesMailCollection
 
    Dim n_View As Domino.NotesView
    Dim n_ViewNav As Domino.NotesViewNavigator
    Dim n_ViewEntry As Domino.NotesViewEntry
    Dim n_Document As Domino.NotesDocument
 
    n_View = MailDB.GetView("($Inbox)")
    n_ViewNav = n_View.CreateViewNav
    n_ViewEntry = n_ViewNav.GetFirstDocument
 
    ' For all mails
    Do While Not (n_ViewEntry Is Nothing)
 
      n_Document = n_ViewEntry.Document
      Dim oAttachment As Domino.NotesEmbeddedObject
 
      If n_Document.HasEmbedded Then
 
        ' Add data to our object
        Dim nm As New NotesMail
        With nm
          .Body = n_Document.GetItemValue("Body")(0).ToString
          .Subject = n_Document.GetItemValue("Subject")(0).ToString
          .From = n_Document.GetItemValue("From")(0).ToString
        End With
 
        Dim fileNames As Object() = Session.Evaluate("@AttachmentNames", n_Document)
        For Each fileName As String In fileNames
          oAttachment = n_Document.GetAttachment(fileName)
          nm.AttachmentFilenames.Add(oAttachment)
        Next
 
        ' Add object to our list
        result.Add(nm)
 
        ' Remove from list
        If MoveMails Then
          ' n_Document.PutInFolder("Importiert " & Now.ToShortDateString, True)
          ' n_Document.RemoveFromFolder("($Inbox)")
        End If
 
      End If
 
      ' Next Mail
      n_ViewEntry = n_ViewNav.GetNextDocument(n_ViewEntry)
    Loop
 
    Return result
 
  End Function
 
End Class
public Sub Test
 
    ' Account Einstellungen 
    Dim acc As New MailAccount
    With acc
      .Host = "DominoServer"
      .DatabaseFilename = "mail\postfach.nsf"
      .Password = "meingeheimespasswort"
    End With
 
    ' GO! GO! GO! Alle Mails auflisten
    Dim lstMails As NotesMailCollection = NotesTools.GetMails(acc, False)
 
    For Each itm As NotesMail In lstMails
      ' Alle Anhänge in Verzeichnis abspeichern
      For Each att As Domino.NotesEmbeddedObject In itm.AttachmentFilenames
        strFilename = att.Name
        strDirectory = "C:\tmp"
        My.Computer.FileSystem.CreateDirectory(strDirectory)
        strFullFilename = strDirectory & "\" & strFilename
        att.ExtractFile(strFullFilename)
      Next
 
    Next
end sub

Dupe Files Beispiel Tasks verfügbar

Für die im neuen Dupe Files eingeführte Tasks-Funktion (welche kurz gesagt der Automatisierung oder Skript-Steuerung von Dupe Files dient) habe ich nun vier Beispiel Tasks hochgeladen.

Um diese Tasks in Dupe Files zu testen, bitte das Archiv aus dem Download-Bereich von Dupe Files herunterladen und in das Dupe Files Verzeichnis entpacken. Dabei müsste der Entpacker automatisch einen Unterordner namens "Tasks" erstellen.

(Sollen die Tasks in einem anderen Verzeichnis abgelegt werden, so kann man dieses über den Menüpunkt "Optionen / Erweitert / Task Verzeichnis auswählen..." festlegen.)

Anschließend startet man Dupe Files und wählt über das Menü den Punkt "Optionen / Erweitert / Task Manager" aus.

Fortan gibt es im Menü unter "Datei" einen neuen Eintrag namens "Tasks", welcher alle Task-Dateien aus dem Task-Verzeichnis auflistet. Mit einem Klick auf einen solchen Eintrag wird die jeweilige Task-Datei ausgeführt.

VB .NET Programm zum Autostart in der Registrierung hinzufügen

Wer hätte das gedacht... unter .NET ist auch das mittlerweile ein Klacks...

Die folgende Prozedur fügt die aktuelle Application im Bereich CurrentUser in die Registrierung als Autostart Eintrag hinzu.

Der erste Wert "My.Application.Info.Productname" ist dabei der Name des Eintrags und dessen Wert ist der komplette Dateiname des Programms, den man via "System.Reflection.Assembly.GetEntryAssembly.Location" ermitteln kann.

Die Anführungszeichen vorne und hinten sind notwendig, wenn der Pfad zum Programm Leerzeichen enthält. Ansonsten würde Windows nur das Verzeichnis statt das Programm an sich öffnen.

Public Shared Sub AddToAutorun()
    Dim key As Microsoft.Win32.RegistryKey = My.Computer.Registry.CurrentUser.CreateSubKey("Software\Microsoft\Windows\CurrentVersion\Run")
    key.SetValue(My.Application.Info.ProductName, """" & System.Reflection.Assembly.GetEntryAssembly.Location & """")
End Sub

VB .NET Prozess starten und auf sein Ende warten

Public Sub Execute(ByVal aFilename As String)
    Dim p As New Process
    p.StartInfo.FileName = aFilename
    p.StartInfo.UseShellExecute = True
    p.Start()
    p.WaitForExit()
  End Sub

Beispielaufrufe:

Execute("C:\test.doc")
Execute("C:\test.pdf")

HowTo: VB .NET Enums mit Beschreibungen versehen

Dieses HowTo beschreibt, wie man Enums mit Beschreibungen versehen kann und diese Beschreibungen dann beispielsweise in einer Combobox oder Listbox anzeigt.

Beispiel vorher:

So sehen die Einträge bisher aus. Für den Endanwender ziemlich unpraktisch.

Beispiel nachher:

So sollen die Einträge später angezeigt werden.

Auf gehts!

Unser Beispiel enum:

Public Enum eFileListActionType
  eflatNone = 0
  eflatCompress = 1
  eflatDeleteForever = 2
  eflatRecyclebin = 3
  eflatReplaceWithTextfile = 4
  eflatReplaceWithLink = 5
  eflatReplaceWithZeroByteFile = 6
  eflatMoveToDirectory = 7
End Enum

Um nun alle Namen diesse Enums beispielsweise in einer Combobox anzuzeigen, kann man bekanntermaßen folgendes machen:

Me.cbFileActionType.Items.AddRange([Enum].GetNames(GetType(eFileListActionType)))

Dann hätte man die Combobox mit den Werten eflatNone, eflatCompress usw. usf. gefüllt. Also praktisch so wie im ersten Bild oben. Dass das für den Endanwender nicht unbedingt ideal ist, sollte klar sein. Was wir also benötigen ist eine Möglichkeit den enums eine Beschreibung zu verpassen und diese Beschreibung stattdessen in die Combobox zu schreiben.

Zum Glück gibt es Reflection und das Attribut Description, das uns hierbei aus der Patsche hilft.

Also, wie geht das jetzt?

Zuerst müssen wir unseren enum um das Attribut Description ergänzen. Das sieht dann wie folgt aus:

Imports System.ComponentModel ' Wichtig!

Public Enum eFileListActionType
  <Description("Keine")> _
  eflatNone = 0
  <Description("Komprimieren")> _
  eflatCompress = 1
  <Description("Für immer löschen")> _
  eflatDeleteForever = 2
  <Description("In Papierkorb verschieben")> _
  eflatRecyclebin = 3
  <Description("Durch Textdatei mit Link ersetzen")> _
  eflatReplaceWithTextfile = 4
  <Description("Durch Dateilink ersetzen")> _
  eflatReplaceWithLink = 5
  <Description("Durch 0-Byte Datei ersetzen")> _
  eflatReplaceWithZeroByteFile = 6
  <Description("In Verzeichnis verschieben")> _
  eflatMoveToDirectory = 7
End Enum

Wie man sieht wird einfach der Tag vor einen Eintrag gesetzt.

Zum Auslesen dieser Werte müssen wir nun Reflection bemühen:

Die erste Funktion gibt uns dabei eine Beschreibung für einen Enum-Wert zurück.

Public Shared Function GetEnumDescription(ByVal EnumConstant As [Enum]) As String
    Dim fi As Reflection.FieldInfo = EnumConstant.GetType().GetField(EnumConstant.ToString())
    Dim attr() As System.ComponentModel.DescriptionAttribute = DirectCast(fi.GetCustomAttributes(GetType(System.ComponentModel.DescriptionAttribute), False), System.ComponentModel.DescriptionAttribute())
    If attr.Length > 0 Then
      Return attr(0).Description
    Else
      Return EnumConstant.ToString()
    End If
  End Function

Für folgenden Befehl

Dim str as String = GetEnumDescription(eflatReplaceWithTextfile)
Debug.Print(str)

bekämen wir also das als Ausgabe:

Durch Textdatei mit Link ersetzen

Um das ganze noch etwas zu optimieren habe ich noch eine Hilfs-Funktion geschrieben, die alle Werte eines Enums übersetzt (also die Description einliest) und diese in ein String Array schreibt, das man dann einfach an die Combobox übergeben kann.

Public Shared Function GetEnumDescriptions(ByVal EnumConstant As Type) As String()
    Dim res(-1) As String
    For Each ec As [Enum] In [Enum].GetValues(EnumConstant)
      ReDim Preserve res(UBound(res) + 1)
      res(UBound(res)) = GetEnumDescription(ec)
    Next
    Return res
  End Function

Beispielanwendung:

Me.cbFileActionType.Items.AddRange(GetEnumDescriptions(GetType(eFileListActionType)))

Und damit hätten wir alle Beschreibungen eines Enums sauber in einer Combobox oder Listbox untegebracht.