Autor Beitrag
patmann2001
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 201

Windows 7 Prof.
Delphi XE2
BeitragVerfasst: Do 21.09.06 12:16 
Hallo Leute,

ich hoffe das die Frage hier richtig ist, ansonsten liebe Moderatoren verschiebt das bitte dahin wo es hingehört. Danke.

Ich habe da mal eine Komponente gemacht. Sieht so aus:
ausblenden volle Höhe Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
UNIT MMF;

{Die Routinen zum schreiben und lesen von MMF Files habe ich entnommen von
http://www.michael-puff.de/Developer/Delphi/Sonstiges/MMF.html
Vielen Dank an Michael Puff.
}


INTERFACE

USES
 Windows, Messages, SysUtils, Classes, Dialogs;

TYPE
 TDATARecord = RECORD
  Zeit1: TTimeStamp;
  Owner: STRING[255];
  ID: Integer;
  Value: STRING[255];
  Zeit2: TTimeStamp;
 END;
 PDATARecord = ^TDATARecord;
TYPE
 TMMF = CLASS(TComponent)
 private
    { Private-Deklarationen }
  FMapHandle: HWND;
  FMMFName: STRING;
  FOnCreate: TNotifyEvent;
  FOnDestroy: TNotifyEvent;
  FOwner: STRING;
  FUNCTION CreateMMFile(FileName: STRING; Size: Integer): HWND;
  FUNCTION OpenMMFile(FileName: STRING): HWND;
  FUNCTION MapMMFile(FileHandle: HWND; readonly: Boolean = true): Pointer;
  FUNCTION DestroyMMFile(FileHandle: HWND): Boolean;
  PROCEDURE WriteToMMF(Value: TDATARecord);
  FUNCTION ReadFromMMF: TDATARecord;
 protected
    { Protected-Deklarationen }
 public
    { Public-Deklarationen }
  CONSTRUCTOR Create(AOwner: TComponent); override;
  DESTRUCTOR Destroy; override;
  FUNCTION MMFCreate: Boolean;
 published
    { Published-Deklarationen }
  PROPERTY MMFName: STRING read FMMFName write FMMFName;
  PROPERTY MMFData: TDataRecord read ReadFromMMF write WriteToMMF;
  PROPERTY Tag;
  PROPERTY OnCreate: TNotifyEvent read FOnCreate write FOnCreate;
  PROPERTY OnDestroy: TNotifyEvent read FOnDestroy write FOnDestroy;
 END;
 
PROCEDURE Register;

IMPLEMENTATION

PROCEDURE Register;
BEGIN
 RegisterComponents('MyComponents', [TMMF]);
END;

CONSTRUCTOR TMMF.Create(AOwner: TComponent);
BEGIN
 INHERITED;
 IF Assigned(FOnCreate) THEN FOnCreate(self);
END;

DESTRUCTOR TMMF.Destroy;
BEGIN
 IF Assigned(FOnDestroy) THEN FOnDestroy(self);
 INHERITED;
END;

FUNCTION TMMF.CreateMMFile(FileName: STRING; Size: Integer): HWND;
BEGIN
 Result := CreateFileMapping(
  INVALID_HANDLE_VALUE, // Keine Quelldatei
  NIL, // Keine Sicherheitsattribute
  PAGE_READWRITE, // Voller Zugriff
  0, // Minimalgröße
  Size + 4, // Maximalgröße
  PChar(FileName)); // Name des MMF-Objekts
END;

FUNCTION TMMF.OpenMMFile(FileName: STRING): HWND;
BEGIN
 Result := OpenFileMapping(
  FILE_MAP_READ, // Lesezugriff
  True, // Handle vererben
  PChar(Filename)); // Name des MMF-Objekts
END;

FUNCTION TMMF.MapMMFile(FileHandle: HWND; readonly: Boolean = true): Pointer;
VAR
 Access: LongWord;
BEGIN
 Result := NIL;
 IF ReadOnly THEN Access := FILE_MAP_READ ELSE Access := FILE_MAP_WRITE;
 IF (FileHandle <> 0) THEN
  BEGIN
    // MMF-Objekt mappen
    // Dadurch haben wir erst Zugriff auf die Daten
   Result := MapViewOfFile(
    FileHandle, // Handle des MMF-Objekts
    Access, // Zugriff
    0, // Kein Start-Offset
    0, // Kein Start-Offset
    0); // Gesamte Größe mappen
  END;
END;

FUNCTION TMMF.DestroyMMFile(FileHandle: HWND): Boolean;
BEGIN
 Result := false;
 TRY
  Result := CloseHandle(FileHandle);
 EXCEPT
  ON E: Exception DO
   BEGIN
    MessageDlg('Fehler beim schließen eines Objektes mit dem HWND = ' + IntToStr(FileHandle) + #10
     + #13 + E.Message, mtError, [mbok], 0);
   END;
 END;
END;

PROCEDURE TMMF.WriteToMMF(Value: TDATARecord);
VAR
 Mem, Data: PDATARecord;
 jetzt: TTimeStamp;
BEGIN
 IF FMMFName = '' THEN Exit;
 jetzt := DateTimeToTimeStamp(now);
 Data := GetMemory(sizeof(TDATARecord));
 TRY
  IF Assigned(Data) THEN
   BEGIN
    Data^ := Value;
   END;

  // MMF-Objekt erzeugen
  FMapHandle := CreateMMFile(MMFName, sizeof(Value));
  Mem := MapMMFile(FMapHandle, false);
  IF Assigned(Mem) THEN
   BEGIN
      // String-Länge in MMF schreiben
  // PCardinal(Data)^ := sizeof(Value);
      // String in MMF schreiben
    Data.Zeit2 := jetzt;
    CopyMemory(Mem, Pointer(Data), sizeof(TDATARecord));
    Data.Zeit1 := jetzt;
    CopyMemory(Mem, Pointer(Data), sizeof(TDATARecord));
      // Mapping schließen
    UnmapViewOfFile(Mem);
   END;
 FINALLY
  FreeMemory(Data);
 END;
END;

FUNCTION TMMF.ReadFromMMF: TDATARecord;
VAR
 Data: PDATARecord;
BEGIN
  // MMF-Objekt öffnen
 Result.ID := -1;
 Result.Quest := '';
 Result.Value := '';
 IF FMMFName = '' THEN Exit;
 FMapHandle := OpenMMFile(MMFName);
 Data := MapMMFile(FMapHandle);
 IF Assigned(Data) THEN
  BEGIN
   IF (Data^.Zeit1.Time = Data^.Zeit2.Time) AND (Data^.Zeit1.Date = Data^.Zeit2.Date) THEN
    Result := Data^;
   UnmapViewOfFile(Data);
  END;
END;

FUNCTION TMMF.MMFCreate: Boolean;
BEGIN
 Result := false;
 IF FMMFName = '' THEN Exit;
 FMapHandle := CreateMMFile(MMFName, sizeof(TDataRecord));
 IF FMapHandle = 0 THEN Exit;
 FOwner := ReadFOwner;
 Result := true;
END;

END.


Wie man gut sehen kann schreibe ich immer diesen TDataRecord in die MMF. Nun währe es schön, wenn ich einen Teil dieses Record ausserhalb meiner Komponente, z.B. im Hauptprogramm oder in einer DLL deklarieren könnte.
Das könnte dann vielleicht so aussehen:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
 TDATARecord = RECORD
  Zeit1: TTimeStamp;
  Owner: STRING[255];
  ID: Integer;
  Value: TIrgendeinandererRecord; External ''; {Also das dieser TIrgendeinandererRecord in einer anderen Datei festgelegt wird und eben nicht in der Komponente selbst, würde das die Kompo sehr felxiebel gestallten.}
  Zeit2: TTimeStamp;


Ich möchte also den eigentlichen Datenteil felxibel gestalten und nur einen Rumpf liefern. Leider kann man Records nicht overloaden oder overriden.
Vielleicht hat jemand hier eine Idea.

Ich hoffe das Problem wird verstanden.

Vielen Dank im Vorraus

Patmann
Narses
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Administrator
Beiträge: 10185
Erhaltene Danke: 1261

W11x64
TP3 .. D7pro .. D10.2CE
BeitragVerfasst: Do 21.09.06 13:43 
Moin!

user profile iconpatmann2001 hat folgendes geschrieben:
Ich möchte also den eigentlichen Datenteil felxibel gestalten und nur einen Rumpf liefern. Leider kann man Records nicht overloaden oder overriden.

Leider geht das nicht, records sind nun mal einfach statische Datenstrukturen. :|

Ich denke allerdings, das Luckie der Einfachheit halber ein record verwendet hat, um das Beispiel nicht ausufern zu lassen; im Allgemeinen halte ich das aber (besonders für eine Komponente) nicht für gut, hier mit records zu arbeiten.

Schau mal in diesen FAQ-Beitrag, da geht´s zwar um Netzwerkdatenübertragung, aber das Grundproblem ist das gleiche: records sind nicht flexibel.

Fazit: mit records wird das nix; für IPC entweder von den MMFs weg zu einer TCP-Verbindung wechseln und dann ein Protokoll einsetzen, oder z.B. eine Datenvorbereiterklasse schreiben, die die Daten z.B. in einem dynamischen String sammelt und dann serialisiert in das MMF übergeben.

cu
Narses

_________________
There are 10 types of people - those who understand binary and those who don´t.
Reinhard Kern
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 591
Erhaltene Danke: 14



BeitragVerfasst: Do 21.09.06 14:24 
user profile iconpatmann2001 hat folgendes geschrieben:
Hallo Leute,
....
Ich möchte also den eigentlichen Datenteil felxibel gestalten und nur einen Rumpf liefern. Leider kann man Records nicht overloaden oder overriden.
Vielleicht hat jemand hier eine Idea.

Ich hoffe das Problem wird verstanden.

Vielen Dank im Vorraus

Patmann


Hallo,

man könnte statt eines Records ein Objekt nehmen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
 TDATARecord = class
  public
   Zeit1: TTimeStamp;
   Owner: STRING[255];
   ID: Integer;
   Value: STRING[255];
   Zeit2: TTimeStamp;
 END;


aber dann muss die gesamte Software umgeschrieben werden, weil es kein SizeOf gibt bzw. das liefert nur die Grösse einer Adresse, und weil eine Zuweisung nur die Adresse kopiert, nicht den Inhalt, und und und ...

ABER dann kann man Grundroutinen für diese Klasse schreiben, die auch für erweiterte Klassen gelten, z.B.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
 TDATARecordWithString = class (TDATARecord)
  public
   Text: ShortString;
 END;



Gruss Reinhard