|
Antwort |
Registriert seit: 22. Mär 2003 Ort: Finsterwalde 622 Beiträge Turbo Delphi für Win32 |
#1
Hallo,
ich habe TurboDelphi und bin daher in der Benutzung fremder Komponenten eingeschränkt. Ich habe bereits mittels entsprechender Tutorials (z.B. hier) ein Chat-Programm ausschließlich mit TServerSocket und TClientSocket nachgebastelt. Allerdings hat das keine Authentifizierung der Benutzer und bringt nur Broadcast. Letzteres ist kein Problem (mehr), weil die Clients gleich als erstes die Benutzernamen an den Server senden und ich damit eine Liste parallel zu Socket.Connections[I] verwalte. Ich weiß also, welcher Benutzer welchen Index I hat. Die Textnachrichten enthalten dann immer einen selbstgebauten "Header", der Absender, Empfänger, Zeit etc. enthält. Meine Idee ist es nun, bereits im Ereignis TServerSocket.OnClientConnect die Prüfung von Benutzernamen und Passwort mit reinzubasteln. Wenns nicht klappt, soll die Verbindung gleich wieder gekappt werden. Nun meine Fragen:
Alex Winzer
|
Zitat |
Registriert seit: 3. Jan 2007 Ort: Dresden 3.443 Beiträge Delphi 7 Enterprise |
#2
In OnclientConnect geht sowas gar nicht. Sondern du musst das Passwort anfordern, und es dann überprüfen. Dazu musst du dir ein entsprechendes Protokoll überlegen.
Bisher besteht dein Protokoll nur darin, dass der zuerst gesendete String immer der Name ist. Alles andere sind Nachrichten. Jetzt musst du dir halt ein bisschen mehr überlegen.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
|
Zitat |
Registriert seit: 22. Mär 2003 Ort: Finsterwalde 622 Beiträge Turbo Delphi für Win32 |
#3
Zitat von sirius:
... Dazu musst du dir ein entsprechendes Protokoll überlegen.
Bevor ich nun einen Haufen Zeit investiere: Ich habe schon damit angefangen, dem jeweiligen String als erstes Zeichen einen Integer-Wert mitzugeben. Vom Server kommend bedeutet z.B. eine Eins: "Schicke mir erstmal Deinen Benutzernamen ." Und vom Client kommend bedeutet es: "Hier hast Du ihn!" ...
SQL-Code:
Kann ich das so weiter führen oder gibt es dagegen irgend welche Bedenken?
Client -> Server (Verbindung)
Server -> Client #01 Client -> Server #01 + 'Benutzername' Server (Prüfung ob OK, falls nicht sofort trennen) Server -> Client #02 Client -> Server #02 + 'Passwort' Server (Prüfung ob OK, falls nicht sofort trennen) Gibt es bessere Ideen? Gruß, Alex
Alex Winzer
|
Zitat |
Registriert seit: 3. Jan 2007 Ort: Dresden 3.443 Beiträge Delphi 7 Enterprise |
#4
obligatorischer Hinweis Nummer 1:
TCP sichert nicht ab, dass ein Paket zusammen ankommt, es kann auch zerstückelt werden, oder gleich zwei hintereinander gesendete kommen als eins an. Du brauchst also entweder ein Ende-Zeichen je Nachricht, oder du hast ein Protokoll-Header, welcher an definierter Stelle, die Größe des Gesamtpaketes mitsendet. Natürlich musst du hinterher die Pakete wieder trennen. (zum Testen musst du mal Benutzernmane über IMHO 1000 Bytes versenden.) Ansonsten sieht dein Protokoll soweit ok aus. Ich würde aber bei falscher Benutzereingabe nicht gleich trennen, sondern erst eine Meldung schicken und dann trennen. Gegen DOS o.ä. -Attacken kannst du dich vielleicht auch wehren. Hier schaust du dir in OnConnect mal die RemotAddresse des Sockets an.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
|
Zitat |
Registriert seit: 22. Mär 2003 Ort: Finsterwalde 622 Beiträge Turbo Delphi für Win32 |
#5
Nochmals vielen Dank!
Zitat von sirius:
... oder du hast ein Protokoll-Header, welcher an definierter Stelle, die Größe des Gesamtpaketes mitsendet...
Zitat von sirius:
Gegen DOS o.ä. -Attacken kannst du dich vielleicht auch wehren. Hier schaust du dir in OnConnect mal die RemotAddresse des Sockets an.
Speichere ich in einem Zähler, wie oft es von einer bestimmten IP Anmeldeversuche gegeben hat. Und wenn es zuviel sind, trenne ich immer gleich. Oder wie? Dann sehe ich aber das Problem, dass ja später mal ein lieber Benutzer mit derselben IP kommen kann. Der käme dann nicht mehr rein. Gruß, Alex
Alex Winzer
|
Zitat |
Registriert seit: 3. Jan 2007 Ort: Dresden 3.443 Beiträge Delphi 7 Enterprise |
#6
Zitat von Schwedenbitter:
Ich meine mich aus Anlass meiner vorherigen Suche daran zu erinnern, dass hier mal irgendwo mal so ein Header vorgschlagen wurde.
ICh weis ja nicht, wo du noch hin willst, aber reicht es für dein obiges Beispiel nicht auch erstmal am Ende ein #0 zu schicken. Da weist du hinterher zumindest, wann Schluss ist (bei http wird auch nur eine Leerzeile am Ende gesendet).
Zitat:
Also rein Praktisch?
Speichere ich in einem Zähler, wie oft es von einer bestimmten IP Anmeldeversuche gegeben hat. Und wenn es zuviel sind, trenne ich immer gleich. Oder wie?
Zitat:
Dann sehe ich aber das Problem, dass ja später mal ein lieber Benutzer mit derselben IP kommen kann. Der käme dann nicht mehr rein.
Dieser Beitrag ist für Jugendliche unter 18 Jahren nicht geeignet.
|
Zitat |
uoeb7gp
(Gast)
n/a Beiträge |
#7
Hallo Schwedenbitter, eine Serverauthentifizierung kannst Du folgendermaßen implementieren.
1. Du definierst Dir einen Protokollheader, dieser Header ist logisch von den Daten getrennt.
Delphi-Quellcode:
Datenstruktur [Daten Header]
{GENERIC HEADER}
//-- Headeraufbau MsgID = Byte = 0..255; Msg-Length = DWORD //-- +-----------------------------------------------|----------------+ //-- | Transport Header | Daten Header | //-- +--------------------|--------------------------|----------------+ //-- | Byte-MsgID | 4Byte-DWORD Msg-Length | Daten | //-- +--------------------|--------------------------|----------------+ MSG_ID_LOGON : Byte = 0; MSG_ID_DATA : Byte = 1; MSG_ID_KEEP_ALLIVE : Byte = 2; {MSG_ID_LOGON} //-- Aufbau der Datenstruktur [Daten] für MSG_ID_LOGON //-- +--------------------+ //-- | TLogonRec | //-- +--------------------+
Delphi-Quellcode:
2. Im ClientSocket Connect sendest du die Daten an den Server!
type
TLogonSVRRec = packed record NTUser : array[0..31] of Char; Passwort : array[0..31] of Char; RechnerName : array[0..31] of Char; RechnerIP : array[0..31] of Char; GUID : array[0..63] of Char; UserFullName : array[0..128] of Char; AnyIntData1 : integer; AnyIntData2 : integer; ptrClientObj : pointer; //-- out end; PLogonSVRRec = ^TLogonSVRRec; TDataRec = packed record Socket : integer; ptrData : Pointer; cbSize : DWORD; Error : integer; ErrorMsg : PChar; ptrLogonSVRRec : PLogonSVRRec; end; PDataRec = ^TDataRec;
Delphi-Quellcode:
3. Die Procedure SendMsg generiert den 5 Byte Header und fügt die Daten hinzu.
procedure TClientObj.ClientSocketConnect(const Sender: TObject;
Socket: TCustomWinSocket); begin try FSocketState := stConnect; if Assigned(CBConnect) then begin CBConnect('Client-Socket Connected !'); end; if (ClientObj <> NIL) then SendMsg(MSG_ID_LOGON, @MainClass.LogonInfo, SizeOf(TLogonSVRRec), ClientSocket.Socket); except FSocketState:=stError; end; end;
Delphi-Quellcode:
4. Am server Extrahierst du den Header.
procedure TClientObj.SendMsg(MsgID: Byte; ptrData: Pointer; cbSize: Integer;
Socket: TCustomWinSocket); const HEADER_SIZE = sizeof(Byte) + sizeof(integer); var ErrorCode:integer; fdSet: TFDSet; Begin try FSendBuf.Seek(0, 0); FSendBuf.Write(MsgID, sizeof(Byte)); FSendBuf.Write(cbSize, sizeof(integer)); FSendBuf.Write(ptrData^, cbSize); Inc(cbSize, HEADER_SIZE); if socket.SendBuf(FSendBuf.memory^, cbSize, ErrorCode) <= 0 then begin if ErrorCode = WSAEWOULDBLOCK then begin FD_ZERO(fdSet); fdSet.fd_count := 1; fdSet.fd_array[0] := socket.SocketHandle; if (Select(0, nil, @fdSet, nil, nil) <> 1) or (socket = NIL) or (not socket.Connected) then EXIT; socket.SendBuf(FSendBuf.memory^, cbSize, ErrorCode); end; end; except FSocketState := stError; end; end;
Delphi-Quellcode:
5. Das erste Byte des Headers ist die MSG-ID, ist die Msg gültig, dann
procedure TServerObj.ServerSocketClientConnect(const Sender: TObject;
const Socket: TCustomWinSocket); var DataRec: TDataRec; begin try TCliCon.Create(FIntHash, FStringHash, Socket); except on e:exception do begin if Assigned(@CBClientError) then begin FillChar(DataRec, SizeOf(TDataRec), 0); DataRec.Socket := Socket.SocketHandle; DataRec.Error := wsagetlasterror; DataRec.ErrorMsg := PChar( 'EXCEPTION > TServerObj.ServerSocketClientConnect: ' + e.Message ); CBClientError(@DataRec); end; end; end; end; procedure TServerObj.ServerSocketClientRead(const Sender: TObject; const Socket: TCustomWinSocket); var cbRcv: integer; RcvBuf: array [0..WSOCK_READ_BUFFER_SIZE * 8] of Byte; RcvBufItem: TRcvBufItem; hMemData: hGlobal; hMemStruct: hGlobal; begin cbRcv := Socket.ReceiveBuf(RcvBuf[0], WSOCK_READ_BUFFER_SIZE * 8); if cbRcv <= 0 then EXIT; if Socket.SocketHandle <= 0 then EXIT; hMemData := GlobalAlloc(GMEM_FIXED, cbRcv); move(RcvBuf[0], Pointer(hMemData)^, cbRcv); hMemStruct := GlobalAlloc(GMEM_FIXED, SizeOf(TRcvBufItem)); RcvBufItem.ptrData := Pointer(hMemData); RcvBufItem.cbData := cbRcv; RcvBufItem.Socket := Socket.SocketHandle; RcvBufItem.hMemStruct := hMemStruct; RcvBufItem.hMemData := hMemData; move(RcvBufItem, Pointer(hMemStruct)^, SizeOf(TRcvBufItem)); if FRecvBuffer.Write(Pointer(hMemStruct), SizeOf(TRcvBufItem)) <> SizeOf(TRcvBufItem) then begin FRecvBuffer.SetBufSize((FRecvBuffer.BufSize + cbRcv) * 2); FRecvBuffer.Remove(SizeOf(TRcvBufItem)); if FRecvBuffer.Write(Pointer(hMemStruct), SizeOf(TRcvBufItem)) <> SizeOf(TRcvBufItem) then raise exception.Create('you should never see this'); end; SetEvent(FMsgDispatchEvent); end; solange aus dem Socket lesen, bis die 4Byte-DWORD Msg-Length erreicht ist.
Delphi-Quellcode:
6. Die Daten aufbereiten, validieren und gegebenenfalls ungültigen Client disconnecten etc. usw.
procedure TServerObj.Execute;
const HEADER_LENGTH = SizeOf(byte) + SizeOf(DWORD); var cbRcv: integer; cbWritten: integer; cbRest: integer; ptrByte: ^Byte; nLen: integer; nDataSize: integer; cb: integer; nMsgID: Byte; p: pointer; CliCon: TCliCon; RcvBuf: array [0..WSOCK_READ_BUFFER_SIZE * 8] of byte; RcvBufItem: TRcvBufItem; begin while not terminated do begin if FRecvBuffer.CountBytes > 0 then begin RcvBufItem := TRcvBufItem(FRecvBuffer.Peek(nLen)^); if FRecvBuffer.Remove(SizeOf(TRcvBufItem)) <> SizeOf(TRcvBufItem) then raise exception.Create('nLen <> FRecvBuffer.Remove(nLen)'); cbRcv := RcvBufItem.cbData; if cbRcv <= 0 then raise exception.create('cbRcv <= 0'); move(RcvBufItem.ptrData^, RcvBuf[0], cbRcv); CliCon := FIntHash.ValueOf(RcvBufItem.Socket); if CliCon = nil then begin if GlobalFree(RcvBufItem.hMemData) <> 0 then raise exception.create('GlobalFree(RcvBufItem.hMemData)) <> 0'); if GlobalFree(RcvBufItem.hMemStruct) <> 0 then raise exception.create('GlobalFree(RcvBufItem.hMemStruct) <> 0'); Continue; end; cbWritten := CliCon.FMemBuf.Write(@RcvBuf[0], cbRcv); cbRest := cbRcv - cbWritten; if cbRest <> 0 then begin CliCon.FMemBuf.SetBufSize((CliCon.FMemBuf.BufSize + cbRcv) * 2); if CliCon.FMemBuf.Write(@RcvBuf[cbWritten], cbRest) <> cbRest then raise exception.Create('you should never see this'); end; while true do begin nLen := HEADER_LENGTH; ptrByte := CliCon.FMemBuf.Peek(nLen); if ptrByte <> nil then begin move(ptrByte^, nMsgID, SizeOf(Byte)); inc(ptrByte, SizeOf(Byte)); move(ptrByte^, nDataSize, SizeOf(DWORD)); end else begin Break; end; if nMsgID > MSG_ID_KEEP_ALLIVE then begin CliCon.FMemBuf.Remove(CliCon.FMemBuf.BufSize); raise exception.Create('not (nMsgID in [MSG_ID_LOGON..MSG_ID_KEEP_ALIVE])'); end; if CliCon.FMemBuf.CountBytes >= nDataSize + HEADER_LENGTH then begin //-- Mindestens ein Datensatz ist vorhanden if CliCon.FMemBuf.Remove(HEADER_LENGTH) = 0 then raise exception.Create('Buffer.Remove(HEADER_LENGTH) = 0'); cb := nDataSize; p := CliCon.FMemBuf.Peek(cb); CliCon.ProcessMessage(nMsgID, p, nDataSize); if CliCon.FMemBuf.Remove(nDataSize) = 0 then raise exception.Create('Buffer.Remove(nDataSize) = 0'); end else //-- if CliCon.FMemBuf.CountBytes >= nDataSize + HEADER_LENGTH Break; end; //-- while true do begin if GlobalFree(RcvBufItem.hMemData) <> 0 then raise exception.create('GlobalFreeII(RcvBufItem.hMemData)) <> 0'); if GlobalFree(RcvBufItem.hMemStruct) <> 0 then raise exception.create('GlobalFreeII(RcvBufItem.hMemStruct) <> 0'); end else begin DoGarbageCollection; Waitforsingleobject(FMsgDispatchEvent, INFINITE); end; end; end;
Delphi-Quellcode:
procedure TCliCon.ProcessMessage(nMsgID: Byte; ptrData: Pointer; cbSize: DWORD);
var nKeepAlive: Byte; DataRec: TDataRec; begin try if nMsgID = MSG_ID_DATA then begin try if Assigned(@ServerObj.CBData) then begin FillChar(DataRec, SizeOf(TDataRec), 0); DataRec.Socket := FSocket.SocketHandle; DataRec.ptrData := ptrData; DataRec.cbSize := cbSize; DataRec.ptrLogonSVRRec := @FLogonRec; ServerObj.CBData(@DataRec); end; except on e:exception do begin if Assigned(@ServerObj.CBClientError) then begin FillChar(DataRec, SizeOf(TDataRec), 0); DataRec.Socket := FSocket.SocketHandle; DataRec.Error := WSAGetLastError; DataRec.ErrorMsg := PChar( 'EXCEPTION > $0228 [MSG_ID_DATA] : ' + e.Message ); ServerObj.CBClientError(@DataRec); end; try FSocket.Close except end; end; end; end else if nMsgID = MSG_ID_LOGON then begin try move(ptrData^, FLogonRec, SizeOf(TLogonSVRRec)); FLogonRec.ptrClientObj := FSocket; FStringHash.Add(FLogonRec.GUID, self); if Assigned(@ServerObj.CBLogon) then begin FillChar(DataRec, SizeOf(TDataRec), 0); DataRec.Socket := FSocket.SocketHandle; DataRec.ptrData := ptrData; DataRec.cbSize := cbSize; DataRec.ptrLogonSVRRec := @FLogonRec; ServerObj.CBLogon(@DataRec); end; except on e:exception do begin if Assigned(@ServerObj.CBClientError) then begin FillChar(DataRec, SizeOf(TDataRec), 0); DataRec.Socket := FSocket.SocketHandle; DataRec.Error := WSAGetLastError; DataRec.ErrorMsg := PChar( 'EXCEPTION > $0227 [MSG_ID_LOGON] : ' + e.Message ); ServerObj.CBClientError(@DataRec); end; try FSocket.Close except end; end; end; end else if nMsgID = MSG_ID_KEEP_ALLIVE then begin try //-- KeepAllive Retournieren SendMsg(MSG_ID_KEEP_ALLIVE, @nKeepAlive, SizeOf(Byte)); except on e:exception do begin if Assigned(@ServerObj.CBClientError) then begin FillChar(DataRec, SizeOf(TDataRec), 0); DataRec.Socket := FSocket.SocketHandle; DataRec.Error := WSAGetLastError; DataRec.ErrorMsg := PChar( 'EXCEPTION > $0228 [MSG_ID_KEEP_ALLIVE] : ' + e.Message ); ServerObj.CBClientError(@DataRec); end; try FSocket.Close except end; end; end; end; except on e:exception do begin if Assigned(@ServerObj.CBClientError) then begin FillChar(DataRec, SizeOf(TDataRec), 0); DataRec.Socket := FSocket.SocketHandle; DataRec.Error := WSAGetLastError; DataRec.ErrorMsg := PChar( 'EXCEPTION > TCliCon.ProcessMessage [GENERAL] : 1449 ' + e.Message ); ServerObj.CBClientError(@DataRec); end; end; end; end; Prinzipiell wars das schon! Beispielprogramm und Sourcen sind unter folgendem Thread verfügbar. http://www.delphipraxis.net/internal...457&highlight= Threaded-Buffered Async Socket (Client, Server Library incl. Source mit Demoprogramm) lg. |
Zitat |
Registriert seit: 22. Mär 2003 Ort: Finsterwalde 622 Beiträge Turbo Delphi für Win32 |
#8
1.
Also bzgl. des Header kann ich nur sagen: VOLLTREFFER! Das ware genau das, was mir bei meiner Suche schon über den Weg gelaufen war und ich hatte es nicht aufgeschrieben/gespeichert etc. pp. Den Quelltext habe ich mir angesehen. Da es ein vollständiges Programm ist, habe ich erhebliche Probleme, das zu verstehen. Ich habe aber Deine Hinweise zum Anlass genommen, selbst loszulegen und zu probieren. Also quasi learning by doing. 2. Vom Client aus gesehen sieht es beim Connect nun so aus:
Delphi-Quellcode:
Beim Klick auf den Button wird die Verbindung geöffnet.
Procedure TForm1.SendMsg(Msg_ID: Byte; SData: String; Socket: TCustomWinSocket);
Var S : String; Len : DWord; Begin S:='*****' + SData; Len:=Length(SData); Move(Msg_ID, S[1], 1); Move(Len, S[2], 4); If Socket.SendText(S) < (Len + 5) Then ShowMessage(SysErrorMessage(GetLastError)); End; procedure TForm1.Button1Click(Sender: TObject); begin Client.Host:='localhost'; Client.Open; end; Procedure TForm1.ClientConnect(Sender: TObject; Socket: TCustomWinSocket); Var S : String; Begin S:=Edit1.Text + #00 + Edit2.Text; SendMsg(USER_LOGON, S, Socket); End; End. -> Daraufhin kommt das Ereignis OnClientConnect. Hier werden Benutzername und Passwort aus den TEdit-Feldern ausgelesen und mittels SendMsg an den Server geschickt. Die '*****' könnten auch was anderes sein. Sie sind nur Platzhalter für die Msg_ID und die Länge. 3. Am Server sieht es dann so aus:
Delphi-Quellcode:
Im Grunde funktioniert das! Ich habe jetzt nur das Problem, wie ich die User verwalten kann. Denn ActiveConnections numeriert ja einfach durch. Damit kann ich es also nicht machen. Wie gesagt habe ich Probleme mit Deinem Beispielcode. Der ist mit Sicherheit sehr ausgeklügelt aufgeteilt (exen, dlls usw.). Aber ich komme damit nicht klar.
Procedure TForm1.ServerClientConnect(Sender: TObject; Socket: TCustomWinSocket);
Var Msg_ID : Byte; Len : DWord; I : Integer; S : String; LogonStr : String; Begin // Paket entpacken S:=Socket.ReceiveText; Move(S[1], Msg_ID, 1); Move(S[2], Len, 4); Delete(S, 1, 5); // Auf Logon-Daten prüfen If Msg_ID <> USER_LOGON Then Socket.Close // unbekanntes Protokoll Else Begin LogonStr:=SepString(S, #00) + ':' + S; I:=UList.Items.IndexOf(LogonStr); If I = -1 Then Socket.Close // unbekannter Benutzer Else Begin UList.Items[I]:='@' + UList.Items[I]; // Online-Markierung UList.Sorted:=True; // Online nach oben End; End; End; Merkst Du Dir die IP? Oder wie wird das gemacht? Gruß, Alex
Alex Winzer
|
Zitat |
uoeb7gp
(Gast)
n/a Beiträge |
#9
Hallo Schwedenbitter.
Also, die Clients kannst du so verwalten.
Delphi-Quellcode:
Diese Prozedure wird immer dann aufgerufen wenn sich ein Client zum Server verbindet.
procedure TServerObj.ServerSocketClientConnect(const Sender: TObject;
const Socket: TCustomWinSocket); Die Variable Socket identiffiziert den Client immer eindeutig. Auf Grund dieser Eindeutigkeit kannst du dir für jede Clientverbindung eine Datenstruktur (ClientObjekt, Record etc.) aufbauen. Dieses Clientobjekt speicherst du dir am besten in einer Ultra schnellen Hash Liste, da du bei jedem
Delphi-Quellcode:
wieder an Hand des Socket das zugehörige Client - Objekt suchen musst.
procedure TServerObj.ServerSocketClientRead(const Sender: TObject;
const Socket: TCustomWinSocket); Jedes Clientobjekt erhält ausserdem einen Empfangsbuffer, für die aus dem Socket gelesenen Daten. Diese Aufteilung ist notwendig, da bei massivem Netzwerkverkehr die Datenpakete fragmentiert werden, und diese Fragmente für jeden Client wieder zusammengesetzt werden müssen, solange bis die im Header angegebene Data-Size für eine vollständige Übertragung erreicht ist, erst dann können die Daten weiterverarbeitet werden. Hier ein Beispiel: 1. Client Connectet sich zum Server 2. Im ServerSocketClientConnect Event wird das Clientobjekt erzeugt. 3. ClientObjekt wird in einer Hashliste oder ähnlichen gespeichert. 4. Empfangen werden zB. 1020 Byte Daten (ServerSocketClientRead) 5. Hashliste wird mit der ID des sockets nach dem Clientobjekt durchsucht. 6. Die Empfangenen Daten werden in den Empfangsbuffer, des aus der Hashliste gesuchten, ClientObjektes geschrieben. Analyse des Headers erfolgt. Emfangsbuffer wird geprüft, ob die Datasize lt. Header Analyse schon vorhanden ist. Wenn Ja, dann DataSize aus dem Buffer Extrahieren und weiter bei 7. Wenn nein, alles von 1. bis Daten vollständig übertragen sind. ACHTUNG wenn der Client sehr schnell viele Messages hintereinander zum Server sendet, ist es möglich, dass der Empfangsbuffer auch mehrere vollständige und oder fragmentierte Datensätze enthält. Dies musst du bei der Bufferanalyse (Datenextraction) auch noch berücksichtigen. Hier musst du ordentlich Hirnschmalz hineinstecken!. 7. Message lt. ID im Header aufbereiten. PS.
Folgende Exportfunktionen (stdcall) sind vorhanden.
Disconnect Connect
StopServer StartServer Kommunikation erfolgt mit Windowsmessages und oder Callbacks. Hoffe dir damit geholfen zu haben. lg. |
Zitat |
Ansicht |
Linear-Darstellung |
Zur Hybrid-Darstellung wechseln |
Zur Baum-Darstellung wechseln |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
LinkBack URL |
About LinkBacks |