Code zu Anwendungen in .NET
- Einbetten von Win32-Ressourcendateien in Visual-Basic-Projekten
- Erkennen laufender Instanzen einer Anwendung
- Unterscheiden zwischen Kunde und Entwicklungsumgebung
- Ermitteln des Pfades der Anwendungsdatei
Einbetten von Win32-Ressourcendateien in Visual-Basic-Projekten
Während Visual Studio 2005 für C#-Projekte das Einbetten von Win32-Ressourcendateien über die Projekteigenschaften unterstützt (siehe Embedding multiple icons into .NET Executables), ist dies für Visual-Basic-Projekte nicht vorgesehen. Win32-Ressourcendateien können verwendet werden, um beispielsweise mehrere Symbole in das Programm einzubetten. Das im nachstehenden Listing wiedergegebene Makro SelectWin32Resource
schafft Abhilfe, indem es dem Benutzer ermöglicht, dem gerade geöffneten Projekt eine Win32-Ressourcendatei zuzuordnen. Win32-Ressourcendateien können mit Visual Studio erstellt und bearbeitet und Projekten hinzugefügt werden. Sie tragen die Dateinamenserweiterung .res
. Beim Kompilieren mit dem Compiler vbc
kann die Ressourcendatei im Parameter /win32resource
(-win32resource
) angegeben werden.
Option Strict On
Option Explicit On
Imports System
Imports EnvDTE
Imports System.Windows.Forms
Public Module Macros
Friend Class DTEMainWindow
Implements IWin32Window
Public ReadOnly Property Handle() As IntPtr _
Implements IWin32Window.Handle
Get
Return New IntPtr(DTE.MainWindow.HWnd)
End Get
End Property
End Class
Public Sub SelectWin32Resource()
If DTE.ActiveDocument Is Nothing Then
MsgBox( _
"In order to assign a Win32 resource file to a " & _
"project, at least one file belonging to the project " & _
"must be opened.", _
MsgBoxStyle.Exclamation _
)
Return
End If
Dim CurrentProject As Project = _
DTE.ActiveDocument.ProjectItem.ContainingProject
Dim Win32ResourceProperty As [Property] = _
CurrentProject.Properties.Item("Win32ResourceFile")
Dim CurrentValue As String = _
DirectCast(Win32ResourceProperty.Value, String)
Dim Prompt As String
If Len(CurrentValue) = 0 Then
Prompt = _
"Currently there is no Win32 resource file associated " & _
"with the project. Do you want to assign one to the " & _
"project?"
Else
Prompt = _
"Currently the Win32 resource file " & _
ControlChars.Quote & CurrentValue & ControlChars.Quote & _
" is associated with the project. Do you want to " & _
"assign another one to the project?"
End If
If MsgBox(Prompt, MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
Using FileBrowser As New OpenFileDialog()
FileBrowser.Filter = "Win32 resource files (*.res)|*.res"
Dim VSMainWindow As New DTEMainWindow()
If _
FileBrowser.ShowDialog(VSMainWindow) = DialogResult.OK _
Then
Win32ResourceProperty.Value = FileBrowser.FileName
End If
End Using
End If
End Sub
End Module
SelectWin32Resource
zum Zuweisen einer Win32-Ressourcendatei zu einem Projekt.Erkennen laufender Instanzen einer Anwendung
Einige Anwendungen dürfen nur ein Mal zur gleichen Zeit ausgeführt werden. In Classic Visual Basic konnte die Eigenschaft App.PrevInstance
verwendet werden, um zu überprüfen, ob die Anwendung bereits ausgeführt wird. Im .NET Framework findet sich jedoch keinen adäquater Ersatz für diese Eigenschaft. Die folgende Funktionsprozedur PrevInstance
ersetzt die aus Classic Visual Basic bekannte Möglichkeit. Damit das Beispiel funktioniert, muß der Namensraum System.Diagnostics
importiert werden:
''' <summary>
''' Ermittelt, ob bereits eine Instanz des Programms geöffnet ist und gibt
''' <c>Process</c>-Objekt zurück, das die erste gefundene andere Instanz
''' der Anwendung repräsentiert.
''' </summary>
''' <returns>
''' Gibt ein <c>Process</c>-Objekt zurück, das die erste gefundene andere
''' Instanz der Anwendung repräsentiert.
''' </returns>
Public Shared Function PrevInstance() As Process
Dim c As Process = Process.GetCurrentProcess()
' Durchlaufen aller Prozesse mit gleichem Namen.
For Each p As Process In Process.GetProcessesByName(c.ProcessName)
' Aktuellen Prozeß nicht beachten.
If p.Id <> c.Id Then
' Es kann mehrere Prozesse gleichen Namens geben, die von
' unterschiedlichen Programmen stammen.
If p.MainModule.FileName = c.MainModule.FileName Then
' Prozeß der ersten gefundenen anderen Instanz
' zurückgeben.
Return p
End If
End If
Next p
' Keine andere Instanz gefunden.
Return Nothing
End Function
Nachteilig an der oben angegebenen Lösung ist, daß Prozeßnamen auf einem System nicht notwendigerweise eindeutig sein müssen. Um dieses Problem zu umgehen, wird zusätzlich zum Prozeßnamen auch der Ort verglichen, an dem die Anwendung abgelegt ist. Wird ein und die selbe Anwendung jedoch mehrfach aus unterschiedlichen Verzeichnissen gestartet, können die Instanzen einander nicht erkennen.
Weiters gilt es zu entscheiden, ob mehrere Benutzer gleichzeitig die Anwendung ausführen dürfen. Zu diesem Zweck bietet sich die Verwendung eines Mutex, das ist ein Mittel zu Synchronisation von Prozessen, an. Im zweiten Parameter des Konstruktors der Klasse Mutex
wird der Name des Mutex übergeben. Dieser Name sollte eindeutig sein, weshalb sich die Verwendung eines GUIDs als gute Wahl erweist.
Der Präfix Local\
gibt an, daß der Mutex nur für den aktuellen Benutzer gilt. Damit kann gesteuert werden, ob mehrere Benutzer in der Lage sein sollen, jeweils eine Instanz der Anwendung auszuführen, oder auf dem gesamten System nur eine Instanz existieren darf. Damit folgender Code funktioniert, ist es erforderlich, den Namensraum System.Threading
zu importieren:
Public Module Program
Private m_Mutex As Mutex
Public Sub Main()
Dim Owned As Boolean
m_Mutex = New Mutex(True, "Local\11C92606-65D9-4df2-9AEA-B6A4DA91BCE2", Owned)
If Owned Then
Application.Run(New MainForm())
m_Mutex.ReleaseMutex()
Else
MsgBox("Anwendung wird bereits ausgeführt!")
End If
End Sub
End Module
Unterscheiden zwischen Kunde und Entwicklungsumgebung
Während des Entwickelns von Anwendungen häuft sich meist eine Menge an Code an, der nur bei verschiedenen Testdurchläufen kompiliert und ausgeführt werden soll. Wie zwischen Ausführung im Debugmodus und dem Releasemodus unterschieden werden kann, wird im folgenden Listing gezeigt. Damit das Beispiel funktioniert, muß die Konstante DEBUG
definiert sein. Dies erreicht man in der Entwicklungsumgebung von Visual Studio .NET durch Aktivieren der Option DEBUG
-Konstante definieren unter Konfigurationseigenschaften → Erstellen:
#If DEBUG Then
Console.WriteLine("Debugmodus.")
#Else
Console.WriteLine("Releasemodus.")
#End If
Alternativ kann man überprüfen, ob ein Debugger vorhanden ist. Wenn dies der Fall ist, dann wird es sich um den Entwicklungsrechner handeln, andernfalls um die Umgebung des Kunden. Auskunft über das Vorhandensein eines Debuggers gibt System.Diagnostics.Debugger.IsAttached
. Beim Entwickeln von Steuerelementen kommt es vor, daß sich diese in der Entwurfsansicht der Entwicklungsumgebung anders verhalten sollen, als im fertigen Produkt, wenn sie auf einem Formular plaziert werden. Diese Unterscheidung kann man anhand von Me.DesignMode
durchführen.
Ermitteln des Pfades der Anwendungsdatei
Unter Classic Visual Basic war das Ermitteln des Anwendungspfades, also des Verzeichnisses, in dem sich die ausgeführte Datei befindet, sehr einfach mittels App.Path
möglich. In .NET ist dies nicht so einfach. Vielfach wird auf Application.StartupPath
aus dem Namensraum System.Windows.Forms
zurückgegriffen. Diese Methode kann aber beim Start von Anwendungen über Dateiverknüpfungen unter älteren Windows-Versionen angeblich falsche Werte zurückgeben. Außerdem zahlt es sich bei Konsolenanwendungen bzw. Klassenbibliotheken nicht aus, die doch recht große Datei System.Windows.Forms.dll
zu laden.
Eine einfache Lösung, die auch in Klassenbibliotheken den gewünschten Pfad zurückgibt, findet sich über Reflection. Dabei wird der Pfad der ausführenden Assembly ermittelt und zurückgegeben. Damit folgender Code funktioniert, müssen die Namensräume System.IO
und System.Reflection
importiert werden. Um den Pfad der ausgeführten Assembly zu ermitteln, reicht es aus, GetEntryAssembly
durch GetExecutingAssembly
zu ersetzen:
Private ReadOnly Property ApplicationPath() As String
Get
Return _
Path.GetDirectoryName([Assembly].GetEntryAssembly().Location)
End Get
End Property