vb@rchiv
VB Classic
VB.NET
ADO.NET
VBA
C#

https://www.vbarchiv.net
Rubrik: Internet/Netzwerk   |   VB-Versionen: VB5, VB615.10.02
Mailslots - virtuelle EMail-Postfächer

Anhand eines kleinen Chat-Programms möchten wir Ihnen in diesem Workshop erklären, was Mailslots sind, wie man mit Visual Basic und Hilfe der entsprechenden Windows API-Funktionen solche sogenannten virtuellen Postfächer erstellen, Daten senden und auch Daten empfangen kann (Client-/Server).

Autor:  Michael ScholleBewertung:  Views:  21.160 

Anhand eines kleinen Chat-Programms möchten wir Ihnen in diesem Workshop erklären, was Mailslots sind, wie man mit Visual Basic und Hilfe der entsprechenden Windows API-Funktionen solche sogenannten virtuellen Postfächer erstellen, Daten senden und auch Daten empfangen kann (Client-/Server).

Einführung

Jetzt werden Sie sicherlich denken, was zum Henker sind den Mailslots? Eine verständliche Frage, hört man doch nicht viel darüber. Mailslots ( Postfach) kann man vergleichen mit virtuellen E-Mail Postfächern, die auf jedem Rechner unter Windows erstellt werden können. Selbst Windows nutzt diese Technik, um Daten zwischen verschieden Programmen im Netzwerk auszutauschen.

Was glauben Sie, woher ihr Rechner weiß, welche Rechner sich in einem Netzwerk befinden ? Ein jeder kennt doch bestimmt die Programme Winpopup oder das Kommando NetSend der CMD.exe, eben diese Programme nutzen Mailslots, um Daten auszutauschen. Ein Manko haben Mailslots allerdings: ist eine Nachricht erst mal verschickt, besteht keine Möglichkeit zu kontrollieren, ob diese Nachricht auch wirklich angekommen ist. Mailslots sollten also immer nur dann eingesetzt werden, wenn die versendeten Daten nicht unbedingt "lebenswichtig" sind. Weiterhin lassen sich Mailslots nur in einem Lan einsetzen, sind also nicht "internetfähig".

Die versendeten Daten werden über sogenannte "SMB-Datentelegramme" verschickt und zwar für jedes installierte Protokoll! Es gibt keine Möglichkeit, einen Mailslot an ein bestimmtes Protokoll zu binden. Hat ein Rechner z. B. das TCP/IP - und das IPX Protokoll installiert, werden die Telegramme über beide Protokolle gesendet. Wenn jetzt der Rechner auf der Gegenseite auch beide Protokolle installiert hat, empfängt er das Datentelegramm zwei Mal.

Trotz der Nachteile die diese Technik hat, sind Mailslots wunderbar dazu geeignet, Nachrichten zu broadcasten, sprich an alle Rechner im Netzwerk gleichzeitig zu senden. Das wird uns durch die Namensgebung eines Mailslots ermöglicht. Doch dazu später mehr.

Zum besseren Verständnis werden wir im Laufe dieses Workshops ein kleines und simples Chatprogramm erstellen, mit dem man dann experimentieren kann.

Namensgebung

Was für einen Namen hat nun so ein Mailslot ? Der Name eines Mailslots hat eine große Ähnlichkeit mit den ganz normalen Verzeichnisstrukturen einer Festplatte und folgt immer dem gleichen Schema. Die Eröffnung sieht immer so aus:

//./mailslot/

Diese Eröffnung, muss immer zu Beginn eines Mailslotnamens stehen, ist zu vergleichen mit dem C:\ eines Festplattenpfades. Das Schlüsselwort "mailslot" zeigt an, das es sich hierbei um einen Mailslot handelt. Danach kann dann die Struktur folgen:

meineslots/dieserslot

oder

meineslots/nocheinslot

oder

meineslots/slot

Diese Namen können frei gewählt werden und werden im Prinzip genauso behandelt wie Pfade auf einer Festplatte. Insgesamt könnte ein Mailslotname also so aussehen:

//./mailslot/meineslots/dieserslot

oder

//./mailslot/meineslots/nocheinslot

So ein Mailslotname muss bei der Erstellung eines Mailslots angegeben werden und hat auch nur auf dem Rechner Gültigkeit, auf dem die Erstellung stattgefunden hat! Will man nun eine Nachricht versenden, muss man den Mailslotnamen des Zielrechners im Netzwerk kennen. In unserem Fall würde auf jedem Rechner der gleiche Mailslotname verwendet werden, das vereinfacht die Sache enorm.

Um nun Nachrichten zu versenden, muss man die Nachricht in den Mailslot des Fremden Rechners schreiben. Dazu muss der Netzwerkname des Zielrechners bekannt sein. Mal angenommen der Zielrechner heißt "Testrechner", dann müssten wir von unserem Rechner aus seinen Mailslotnamen so interpretieren:

//Testrechner/mailslot/meineslots/dieserslot

Anhand dieses Namens könnten wir Nachrichten an diesen Rechner schicken. Es gibt aber noch die Möglichkeit, eine Nachricht an alle im Netzwerk befindlichen Rechner zu schicken. Dann müsste der Mailslotname so aussehen:

//*/mailslot/meineslots/dieserslot

Hier dient uns das allseits bekannte Sternchen als Jokerzeichen. Wir bräuchten also nur eine Schreiboperation um eine Nachricht an alle erreichbaren Rechner im Netzwerk zu schicken. Einfacher geht's kaum noch... probiert das mal mit dem Winsock Es gibt ferner noch die Möglichkeit, Daten an eine bestimmte Netzwerkdomäne zu schicken. Dann müsste der Mailslotname so aussehen:

//Domänenname/mailslot/meineslots/dieserslot

Wir wissen jetzt also, das wir Nachrichten versenden können, indem wir Daten in den Mailslot des Zielrechners schreiben. Unseren Mailslot brauchen wir eigentlich nur, um Daten auch empfangen zu können.

Vorgehensweise

Was wir brauchen, ist also eine Anwendung, die gleichzeitig Client und Server ist. Da VB uns bei der Erstellung von Mailslots absolut nichts zur Verfügung stellt, bedienen wir uns der API's von Windows.

Für den Client brauchen wir:

WriteFileSimple()
CreateFile()
CreateFileNoSecurity()

Für den Server brauchen wir:

ReadFileSimple()
CreateMailslot()
GetMailslotInfo()
CloseHandle()

Aus den API-Funktionen lässt es sich schon erahnen, wir arbeiten mit richtigen Files! Diese werden aber nicht auf die Festplatte geschrieben, sondern in die Mailslots der Zielrechner. Von dort werden die Files dann wieder ausgelesen und der Inhalt angezeigt.

So, genug geredet, jetzt wird geproggt!

Das Programm

Öffnen Sie ein neues Projekt und fügen Sie der Form folgende Komponenten hinzu:

Ferner benötigen wir noch ein Timer-Control und die API-Deklarationen mit den dazugehörigen Konstanten. Fügen Sie nachfolgende Deklarationen in den Allgemein-Teil der Form.

Option Explicit
 
' öffentliche Programmvariablen
Private SlotHandle As Long
Private MySlot As String
 
' Konstanten für die CreateFile API's
Private Const OPEN_EXISTING = 3
Private Const GENERIC_READ = &H80000000
Private Const GENERIC_WRITE = &H40000000
Private Const GENERIC_EXECUTE = &H20000000
Private Const GENERIC_ALL = &H10000000
Private Const INVALID_HANDLE_VALUE = -1
Private Const FILE_SHARE_READ = &H1
Private Const FILE_SHARE_WRITE = &H2
Private Const FILE_ATTRIBUTE_NORMAL = &H80
 
' Konstanten für die CreateMailslot API's
Private Type SECURITY_ATTRIBUTES
  nLength As Long
  lpSecurityDescriptor As Long
  bInheritHandle As Long
End Type
 
Private tSA As SECURITY_ATTRIBUTES
 
' Client API's
Private Declare Function CreateFile Lib "kernel32" _
  Alias "CreateFileA" ( _
  ByVal lpFileName As String, _
  ByVal dwDesiredAccess As Long, _
  ByVal dwShareMode As Long, _
  lpSecurityAttributes As SECURITY_ATTRIBUTES, _
  ByVal dwCreationDisposition As Long, _
  ByVal dwFlagsAndAttributes As Long, _
  ByVal hTemplateFile As Long) As Long
 
Private Declare Function CreateFileNoSecurity Lib "kernel32" _
  Alias "CreateFileA" ( _
  ByVal lpFileName As String, _
  ByVal dwDesiredAccess As Long, _
  ByVal dwShareMode As Long, _
  ByVal Zero As Long, _
  ByVal dwCreationDisposition As Long, _
  ByVal dwFlagsAndAttributes As Long, _
  ByVal hTemplateFile As Long) As Long
 
Private Declare Function WriteFileSimple Lib "kernel32" _
  Alias "WriteFile" ( _
  ByVal hFile As Long, _
  ByVal lpBuffer As String, _
  ByVal nNumberOfBytesToWrite As Long, _
  lpNumberOfBytesWritten As Long, _
  ByVal Zero As Long) As Long
 
' Server API's
Private Declare Function ReadFileSimple Lib "kernel32" _
  Alias "ReadFile" ( _
  ByVal hFile As Long, _
  ByVal lpBuffer As String, _
  ByVal nNumberOfBytesToRead As Long, _
  lpNumberOfBytesRead As Long, _
  ByVal Zero As Long) As Long
 
Private Declare Function CreateMailslot Lib "kernel32" _
  Alias "CreateMailslotA" ( _
  ByVal lpName As String, _
  ByVal nMaxMessageSize As Long, _
  ByVal lReadTimeout As Long, _
  lpSecurityAttributes As SECURITY_ATTRIBUTES) As Long
 
Private Declare Function GetMailslotInfo Lib "kernel32" ( _
  ByVal hMailslot As Long, _
  lpMaxMessageSize As Long, _
  lpNextSize As Long, _
  lpMessageCount As Long, _
  lpReadTimeout As Long) As Long
 
Private Declare Function CloseHandle Lib "kernel32" _
  (ByVal hObject As Long) As Long

So die Deklarationen hätten wir, jetzt können wir uns an das eigentliche Programm machen

Der Server

Zunächst einmal machen wir uns daran, einen Mailslot zu öffnen. Dazu benötigen wir eine kleine Funktion, die ausgelöst wird, wenn der Anwender auf den Erstellen-Button klickt.

Private Sub Form_Load()
  ' bedarf wohl keiner Erklärung ;)
  Timer1.Interval = 999
  Timer1.Enabled = False
End Sub
Private Sub cmd_CreateSlot_Click()
  If txt_MySlot.Text = "" Then Exit Sub
  If Left$(txt_MySlot.Text, 13) <> "//./mailslot/" Then
    Exit Sub ' Name korrekt ?
  End If
 
  ' SlotName und MaxDateigröße übergeben
  Slothandle = MailSlotCreate(txt_MySlot.Text, 400)
 
  ' Falls erfolgreich geöffnet, zyklisch nach
  ' Dateien schauen
  If Slothandle > 0 Then Timer1.Enabled = True
End Sub

Hier wird erst kontrolliert, ob der Mailslotname gültig ist. Falls nicht, wird die Prozedur verlassen. Dann rufen wir unsere Funktion auf und übergeben den Namen und die maximal versendbare Dateigröße des neuen Mailslots. Wird die maximale Dateigröße mit 0 angegeben, so hat der Mailslot keine Dateigrößenbegrenzung. Wenn das Öffnen des Mailslots erfolgreich war, schalten wir unseren Timer ein um nun zyklisch nach neuen Dateien zu schauen.

' Mailslot erstellen
Private Function MailSlotCreate( _
  ByVal SlotName As String, _
  MsgSize As Long) As Long
 
  Dim Handle As Long
 
  If SlotName = "" Then
    ' kein Name angegeben?
    MailSlotCreate = -1: Exit Function
  End If
 
  ' Hier erhalten wir das Handle
  Handle = CreateMailslot(SlotName, MsgSize, 0, tSA)
 
  MailSlotCreate = Handle
End Function

Diese Funktion öffnet nun unseren Mailslot. Nach der erfolgreichen Übergabe der Parameter erhalten wir unser Handle, ansonsten wird -1 zurückgegeben (Fehler). Ein Fehler tritt auch auf, wenn schon ein Mailslot mit dem gleichen Namen existiert.

Private Sub Timer1_Timer()
  Dim MsgText As String
  Dim MSize As Long
  Dim MsgCount As Long
  Dim SlotInfo As Long
  Dim BytesRead As Long
  Dim BufferSize As Long
 
  ' GetMailslotInfo gibt uns Auskunft über den Mailslot
  ' MSize   = Maximale Dateigröße
  ' Buffersize = Größe der nächsten Datei im Mailslot
  ' MsgCount  = Anzahl der Dateien im Mailslot
 
  SlotInfo = GetMailslotInfo(SlotHandle, MSize, _
    BufferSize, MsgCount, 0)
 
  ' Dateien da?
  If MsgCount > 0 Then
    ' Wenn ja, Funktion zum Lesen und anzeigen
    MsgText = MailSlotRead(BufferSize)
    txt_RecMsg.Text = MsgText
  End If
End Sub

Diese Prozedur wird ab jetzt jede Sekunde nach unseren Mailslot schauen. Sollten Dateien eingetroffen sein, werden sie unverzüglich ausgelesen und der Inhalt im Textfeld angezeigt. Dabei ist uns die API GetMailslotInfo sehr hilfreich. Sie sagt uns wie viele Dateien in unseren Mailslot liegen und wie groß sie sind. Beides Informationen, die wir für unsere nächste Funktion benötigen: dem Auslesen der Datei-Inhalte.

Auslesen der Daten des Mailslots

Public Function MailSlotRead(BufferSize As Long) As String
  Dim TextPuffer As String
  Dim RC As Long
  Dim BytesRead As Long
 
  ' Puffer mit benötigter Anzahl Nullen füllen
  TextPuffer = String(BufferSize, 0)
 
  ' Datei auslesen:
  ' BytesRead sagt uns, wie viele Bytes gelesen wurden
  RC = ReadFileSimple(SlotHandle, TextPuffer, _
    Len(TextPuffer) + 1, BytesRead, 0)
 
  ' Datei-Inhalt übergeben
  MailSlotRead = TextPuffer
End Function

Die Funktion MailslotRead kümmert sich um das Auslesen der Dateien aus unserem Mailslot. Dafür müssen wir einzig den Wert übergeben, der uns sagt wie groß die nächste auszulesende Datei ist. Die API Funktion ReadFileSimple erwartet von uns lediglich einen Puffer, wo sie den Inhalt der Datei hineinschreiben kann. Diesen Puffer füllen wir mit entsprechend vielen Nullen auf, um die passende Größe zu bekommen. Alles andere haben wir schon. War ReadFileSimple erfolgreich, können wir die Variable BytesRead abfragen um festzustellen, wie viele Bytes gelesen wurden. Das ist sicherlich hilfreich bei eventuellen Fehlern, wir verzichten aber heute darauf.

Private Sub cmd_CloseProg_Click()
  ' bedarf wohl keiner Erklärung ;)
  Unload Me
End Sub
Private Sub Form_QueryUnload(Cancel As Integer, _
  UnloadMode As Integer)
 
  ' Das ist ein wichtiger Programmteil!
  ' Sollte das Programm beendet werden, ohne den
  ' Mailslot zu schließen, hilft nur noch ein An- und
  ' Abmelden des Users oder ein Neustart des Rechners,
  ' um wieder auf den Mailslot zugreifen zu können.
 
  CloseHandle (SlotHandle)
End Sub

Kommen wir zum Ende des Servers, hier wird nur noch sichergestellt, dass der Mailslot bei der Programmbeendigung auch wieder geschlossen wird. Das ist wichtig weil, wir sonst nicht mehr darauf zugreifen können! Der Mailslot wird nur dann vom System geschlossen, wenn das System neu startet oder der User sich neu anmeldet. Wird das Programm unvorhergesehen beendet, kann man den Mailslot nur noch wie gerade beschrieben zum Leben erwecken.

Damit hätten wir unseren Server eigentlich schon fertig, was jetzt noch fehlt ist der Client.

Der Client

Die Aufgabe des Clients besteht einzig und allein darin, Dateien in den Mailslot des Zielrechners zu schreiben.

Private Sub cmd_SendMsg_Click()
  Dim Result As Boolean
  Dim ErrMsg As String
 
  ' Daten da?
  If txt_SendSlot.Text = "" Then Exit Sub
  If txt_SendMsg.Text = "" Then Exit Sub
 
  ' Funktion zum Senden des Textes aufrufen
  Result = MailSlotSend(txt_SendMsg.Text, _
    txt_SendSlot.Text)
 
  ' wenn das Resultat True ist, hat alles geklappt
  If Result = False Then
    ErrMsg = "Es konnte nicht in den Mailslot geschrieben werden!"
    MsgBox ErrMsg, vbCritical
  End If
End Sub

Die obige Prozedur löst der Anwender aus, wenn er auf Senden klickt. Dort wird nur kontrolliert, ob die Daten in den Textfeldern vorhanden sind. Wenn dem so ist wird die Funktion MailSlotSend aufgerufen und eben diese Daten übergeben. Hat alles geklappt erhalten wir True als Resultat zurück und wir müssen keine Fehler auswerten.

Public Function MailSlotSend(ByVal Message As String, _
  ByVal TargetMailSlot As String) As Boolean
 
  Dim TMSlot As Long
  Dim Res As Long
  Dim Res1 As Long
  Dim BytesWritten As Long
 
  ' Daten übergeben ?
  If TargetMailSlot = "" Or Message = "" Then
    MailSlotSend = False: Exit Function
  End If
 
  ' Datei im Mailslot des Zielrechners öffnen
  TMSlot = CreateFileNoSecurity(TargetMailSlot, _
    GENERIC_WRITE, FILE_SHARE_READ, 0, _
    OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0)
 
  ' und füllen
  Res = WriteFileSimple(TMSlot, Message, Len(Message), _
    BytesWritten, 0)
 
  ' und schließen
  Res1 = CloseHandle(TMSlot)
 
  ' BytesWritten gibt an, wie viele Bytes
  ' geschrieben wurden
  MailSlotSend = (BytesWritten > 0)
End Function

Die letzte Funktion schreibt die Daten in den Mailslot des Zielrechners. Zuerst einmal werden die übergebenen Daten geprüft. Sind diese da, wird mit Hilfe der API CreateFileNoSecurity eine neue Datei im Mailslot des Zielrechners geöffnet. Dort wird auch zum ersten Mal der Mailslotname des Zielrechners verwendet. Ist die Datei geöffnet, müssen wir sie noch füllen. Dazu dient die API WriteFileSimple. Dort übergeben wir das Handle der gerade neu geöffneten Datei, den Text der Nachricht und die Länge der Nachricht. Haben wir das erfolgreich hinter uns gebracht, müssen wir nur noch die Datei schließen damit unsere Nachricht auf Reisen geht. Das erfolgt durch das Schließen des Handles; jetzt wird auch die Datei geschlossen. Anschließend werten wir noch die Variable BytesWritten aus, um zu überprüfen, ob auch wirklich Daten in unsere Datei geschrieben wurden. Ist das nicht der Fall kann das verschiedene Ursachen haben. Die Datei konnte z. B. nicht erstellt werden, weil der Mailslotname des Zielrechners nicht existierte oder die Netzwerkverbindung nicht bestanden hatte.

Damit wäre unser kleines Beispielprogramm eigentlich fertig. Kompilieren Sie es und installieren Sie es nach Möglichkeit auf 2 oder mehr verschiedenen Rechnern im Netzwerk. Starten Sie die Programme und geben in allen Servern den gleichen Mailslotnamen an und probieren Sie ein bisschen herum.

Es reicht aber auch nur ein Rechner, dann müssen Sie im Server und im Client den gleichen Mailslotnamen verwenden.

Sie haben nun quasi die Urform eines Chatprogramms auf der Basis von Mailslots. Mit ein bisschen Phantasie lässt sich einiges draus machen FEEL FREE



Anzeige

Kauftipp Unser Dauerbrenner!Diesen und auch alle anderen Workshops finden Sie auch auf unserer aktuellen vb@rchiv  Vol.6
(einschl. Beispielprojekt!)

Ein absolutes Muss - Geballtes Wissen aus mehr als 8 Jahren vb@rchiv!
- nahezu alle Tipps & Tricks und Workshops mit Beispielprojekten
- Symbol-Galerie mit mehr als 3.200 Icons im modernen Look
Weitere Infos - 4 Entwickler-Vollversionen (u.a. sevFTP für .NET), Online-Update-Funktion u.v.m.
 
 
Copyright ©2000-2024 vb@rchiv Dieter OtterAlle Rechte vorbehalten.


Microsoft, Windows und Visual Basic sind entweder eingetragene Marken oder Marken der Microsoft Corporation in den USA und/oder anderen Ländern. Weitere auf dieser Homepage aufgeführten Produkt- und Firmennamen können geschützte Marken ihrer jeweiligen Inhaber sein.