Entwickler-Ecke

Dateizugriff - Filesharing zwischen C++ und Delphi


MisterCP - Mi 29.09.10 10:43
Titel: Filesharing zwischen C++ und Delphi
Hallo,

ich hoffe hier jemanden u finden, der sich sowohl mit C als auch mit Delphi auskennt.

Folgende Konstellation von Programmen führt bei mir zu einem Problem
Anwendung A: C-Programm, schreibt zyklisch Daten in eine Datei
Anwendung B: C-Programm vom gleichen Programmierer, liest Daten aus der oben geschriebenen Datei von Anwendung A ohne das es zu irgendeiner Zeit Probleme beim schreibenden Zugriff gibt
Anwendung C: Delphi-Programm (das ist meins), erzeugt in Anwendung A Probleme beim schreiben.

Ich habe auf meiner (Delphi-)Seite folgenden Code verwendet:


Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
            try
                //File öffnen
                if FileExists(FileName) = true then begin
                    hFile := TFileStream.Create(FileName, fmOpenRead, fmShareDenyNone) //keine Beschränkung des Zugriffs
                    //hFile := TFileStream.Create(FileName, fmOpenRead, fmShareDenyRead) //andere Applikationen haben Schreibrecht,
                                                                                       //aber kein Leserecht
                    //hFile := TFileStream.Create(FileName, fmOpenRead, fmShareCompat)   //Sharing is compatible with the way FCBs are opened
                    end{erster if-Zweig}
                else begin
                    result := eCantOpenFile;
                end;{if}
            except
                //Fehlerbehandlung
                result := eCantOpenFile;
            end;{try..except}


Egal welche form der Zugriffsbeschränkung ich verwende, Anwendung A behauptet, nicht mehr in den File schreiben zu dürfen (geht erst wieder, wenn ich ihn geschlossen habe)

Frage: kann ich auf meiner (Delphi-)Seite noch irgendetwas anders parametrieren, damit Anwendung A weiterhin schreiben kann?
Ich weiß leider nicht was Anwendung A an Berechtigungen abfragt, da ich von diesem Programm keine Source habe, aber da Anwendung B das gleiche macht wie ich und es da zu keinen Kollisionen kommt muß es ja irgend eine Form des Sharings bei C geben, die ich event. auch unter Delphi aufrufen kann.

Ich bedanke mich jetzt schon für eure konstuktiven Vorschläge ganz herzlich.

ch. Peterson


jaenicke - Mi 29.09.10 10:56

Hallo und :welcome:

Schau einfach mit dem Process Monitor nach wie auf die Dateien zugegriffen wird.


Flamefire - Mi 29.09.10 12:21

Oder mit Ollydbg (o.ä.) den Aufruf von Program B auf FileOpen kontrollieren.

Was ich aber für wahrscheinlicher halte: Programm A & B verwenden eine Semaphor zur Synchronisierung. D.h. Es hat GENAU 1 Programm Zugriffsrechte.


Delete - Mi 29.09.10 14:57

Hier ist ein funktionierendes Beispiel - CORETEMP ist in C++ programmiert und schreibt OHNE Probleme Daten
in das SHARED MEMORY.
Ausgelesen werden die Daten 1 oder 2x/sec von dem DELPHI-Programm.
ReadCTInfo wird NUR beim Programmstart aufgerufen, danach müssen die Daten nur noch timergesteuert ausgelesen werden,
denn das 'CoreTempMappingObject' bleibt bis zum Programmende erhalten.

http://www.delphi-forum.de/viewtopic.php?t=83627&highlight=coretemp


MisterCP - Mi 29.09.10 15:54

Erst mal vielen Dank für die schnellen Antworten.

@hathor: Deine Antwort macht mir Angst :-) Ich bin leider kein Profi und das sieht unheimlich kompliziert aus.

Ich hatte eher an eine Fehlerursache in der Definition des Funktionsaufrufs gedacht, so in der Art, daß Delphi irgend eine API anders interpretiert als das C-Programm (MFC).

Das liegt aus dem Grunde nahe, da mir jemand ein Beispielprogramm in C geschrieben hat, was eben nur die Datei öffnet und offen hält und Programm A kann munter weiter rein schreiben.

Ich kann nur leider nicht mein ganzes Projekt nach C portieren, da ich C nicht mal richtig lesen geschweige denn schreiben kann.

Kann man die Klasse TFilestream auch direkt nochmal aus irgend einer Windows-Dll einbinden? (und macht das Sinn?)


Flamefire - Mi 29.09.10 19:03

Dann zeig dochmal dein C Programm, was funktioniert...


jaenicke - Mi 29.09.10 19:32

user profile iconMisterCP hat folgendes geschrieben Zum zitierten Posting springen:
Kann man die Klasse TFilestream auch direkt nochmal aus irgend einer Windows-Dll einbinden? (und macht das Sinn?)
Die Klasse ist ein Wrapper um die API-Funktionen. Zum Beispiel um CreateFile um die Datei zu öffnen bzw. zu erstellen.

Aber was soll denn das ganze Herumgerate? Nimm doch einfach wie ich schon sagte den Process Monitor, dann siehst du in 5 Minuten was Sache ist... :roll:


MisterCP - Mi 29.09.10 20:36

Die Lösung ist gefunden !!!
Die schlechte Nachricht ist, das müßte theoretisch tausende User betreffen, die mit Delphi 5.0 arbeiten

Es handelt sich anscheinend tatsächlich um einen Fehler in der Unit "Classes"

Im Construktor wurde meiner Meinung nach beim


Quelltext
1:
inherited Create(FileOpen(FileName, Mode));                    


das "or Rights" vergessen. Ich meine es sollte so aussehen:


Quelltext
1:
inherited Create(FileOpen(FileName, Mode or Rights));                    


Um aber da nicht dauerhaft dran rumzufuschen, (falls es das doch nicht war) hab ich mir die Klasse "TFileStreamC" mal zusammenkopiert.
Zumindest klappt es nun mit dem gemeinsamen Zugriff auf die Datei.

Trotzdem bleibt die Frage: Hat Delphi wirklich solche Fehler?

Trotzdem noch mal vielen Dank und schönen Abend noch.


BenBE - Mi 29.09.10 20:55

Dann versuch mal die Nutzung von THandleStream, statt TFilestream. Wie die Werte im Constructor sein müssen, weißt Du ja jetzt ;-)

Ggf. auch anhand der Compiler-Version abfragen (Omorphia.version.inc [https://omorphia.svn.sourceforge.net/svnroot/omorphia/trunk/omorphia/source/library/Omorphia.version.inc] dürfte Dir dabei helfen), ob Du das über nen TFilestream oder über nen THandleStream machst.


jaenicke - Mi 29.09.10 21:20

user profile iconMisterCP hat folgendes geschrieben Zum zitierten Posting springen:
das "or Rights" vergessen. Ich meine es sollte so aussehen:


Quelltext
1:
inherited Create(FileOpen(FileName, Mode or Rights));                    
Die Rechte spielen beim Öffnen der Datei keine Rolle, deshalb ist das schon richtig so.

Wenn eine Datei erzeugt wird, müssen die Rechte aber gesetzt werden, also bei FileCreate.

Und jetzt sehe ich auch deinen Fehler... So ist es richtig:

Delphi-Quelltext
1:
hFile := TFileStream.Create(FileName, fmOpenRead or fmShareDenyNone)                    
Du hast da ein Komma gesetzt...


MisterCP - Mi 29.09.10 22:46

nun, daß ICH einen fehler gemacht hätte war ja auch meine Vermutung, aber die Schnittstelle zum Construktor in der "Classes" lauet ja:


Quelltext
1:
constructor TFileStream.Create(const FileName: string; Mode: Word; Rights: Cardinal);                    


Blieb mir also gar nichts anderes übrig, als das Komma zu setzen, oder?
Nur eben, das innerhalb des Construktors die Rights dann nicht verarbeitet wurden.
Und wenn mich nicht alles täuscht hatte ich das Filehandling auch bei Herrn Doberenz abgeschrieben (wobei Abschreiben ja keine Tippfehler ausschließt)

Was ich da jetzt aber nicht verstehe ist

Zitat:
Die Rechte spielen beim Öffnen der Datei keine Rolle, deshalb ist das schon richtig so.

Wenn eine Datei erzeugt wird, müssen die Rechte aber gesetzt werden, also bei FileCreate.


Erzeugen tut die Datei ja Anwendung A und A schließt sie dann wieder und öffnet, schreibt was neues und schließt...
Und wenn dann jemand anderes mit drauf zugreift sollte man schon schauen, ob man grad zugreifen darf oder lieber nicht.
Nur eben, daß mein Delphi-Programm beim öffnen nicht mitgeteilt hat, das es ihm egal ist ob noch jemand anderes gleichzeitig schreibt.


jaenicke - Mi 29.09.10 23:38

Das sind keine Rechte, das sind alles Zugriffsmodi und gehören daher verodert in den entsprechenden Parameter.

Rechte regeln die Zugriffsrechte eines Benutzers auf eine Datei. Die Modi, die du meinst, regeln den parallelen Zugriff, sind aber keine Rechte.


Flamefire - Do 30.09.10 12:09

Hast du dir mal den ganzen Construktor angeguckt:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
  if Mode = fmCreate then
  begin
    inherited Create(FileCreate(AFileName, Rights));
    if FHandle = INVALID_HANDLE_VALUE then
      raise EFCreateError.CreateResFmt(@SFCreateErrorEx, [ExpandFileName(AFileName), SysErrorMessage(GetLastError)]);
  end
  else
  begin
    inherited Create(FileOpen(AFileName, Mode));
    if FHandle = INVALID_HANDLE_VALUE then
      raise EFOpenError.CreateResFmt(@SFOpenErrorEx, [ExpandFileName(AFileName), SysErrorMessage(GetLastError)]);
  end;


Die Rechte werden beim Erstellen verwendet (Datei existiert potenziell nicht, bzw wird neu erstellt), die Modi beim öffnen einer vorhandenen Datei.
Und: Die Rechte bei windows sind eh egal, weil ignoriert:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
function FileCreate(const FileName: string; Rights: Integer): Integer;
{$IFDEF MSWINDOWS}
begin
  Result := FileCreate(FileName);
end;
{$ENDIF}
{$IFDEF LINUX}
begin
  Result := Integer(open(PChar(FileName), O_RDWR or O_CREAT or O_TRUNC, Rights));
end;
{$ENDIF}


Martok - Do 30.09.10 18:26

user profile iconFlamefire hat folgendes geschrieben Zum zitierten Posting springen:
Und: Die Rechte bei windows sind eh egal, weil ignoriert:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
function FileCreate(const FileName: string; Rights: Integer): Integer;
{$IFDEF MSWINDOWS}
begin
  Result := FileCreate(FileName);
end;
{$ENDIF}
{$IFDEF LINUX}
begin
  Result := Integer(open(PChar(FileName), O_RDWR or O_CREAT or O_TRUNC, Rights));
end;
{$ENDIF}

Aber nur, weil Delphi da mal wieder nur die Hälfte wrappt.

Mit anderen Worten: Borland hat beim File-IO maximal verkackt. Was sie schreiben wollten, wäre wohl eher sowas anstatt separater Open/Create-Funktionen:


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:
function FOpen(FileName: string; Mode: LongWord): Integer;
const
  AccessMode: array[0..2of LongWord = (
    GENERIC_READ,
    GENERIC_WRITE,
    GENERIC_READ or GENERIC_WRITE);
  ShareMode: array[0..4of LongWord = (
    0,
    0,
    FILE_SHARE_READ,
    FILE_SHARE_WRITE,
    FILE_SHARE_READ or FILE_SHARE_WRITE);
  OpenMode: Cardinal;
begin
  Result := -1;
  if Mode and fmCreate>0 then
    OpenMode:= CREATE_ALWAYS
  else
    OpenMode:= OPEN_EXISTING;
  if ((Mode and 3) <= fmOpenReadWrite) and
    ((Mode and $F0) <= fmShareDenyNone) then
    Result := Integer(CreateFile(PChar(FileName), AccessMode[Mode and 3],
      ShareMode[(Mode and $F0shr 4], nil, OpenMode,
      FILE_ATTRIBUTE_NORMAL, 0));
end;


Wenn du statt TFileStream THandleStream nutzt, kannst du ja das Handle direkt selber übergeben.


BenBE - Fr 01.10.10 12:00

@Martok: Bei Signed-Typen sollte man bei Bitoperationen auf <> 0 prüfen. Auch wenn das hier ftaucht. Ganz davon abgsehen, dass JZ/JNZ weniger Aufwand für die CPU als ein JG/JNLE macht.

Und für die Create-File-Anweisung war wohl auch kein Platz mehr da?