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 Dieser Workshop wurde bereits 21.540 mal aufgerufen.
Anzeige
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. |
sevISDN 1.0 Überwachung aller eingehender Anrufe! Die DLL erkennt alle über die CAPI-Schnittstelle eingehenden Anrufe und teilt Ihnen sogar mit, aus welchem Ortsbereich der Anruf stammt. Weitere Highlights: Online-Rufident, Erkennung der Anrufbehandlung u.v.m. Tipp des Monats Januar 2025 Dieter Otter Zeilen einer MultiLine-TextBox ermitteln (VB.NET) Dieser Zipp zeigt, wie man die Zeilen einer MultiLine-TextBox exakt so ermitteln kann, wie diese auch in der TextBox dargestellt werden. sevGraph (VB/VBA) Grafische Auswertungen Präsentieren Sie Ihre Daten mit wenig Aufwand in grafischer Form. sevGraph unterstützt hierbei Balken-, Linien- und Stapel-Diagramme (Stacked Bars), sowie 2D- und 3D-Tortendiagramme und arbeitet vollständig datenbankunabhängig! |
|||||||||||||
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. |