AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

WM_COPYDATA mit arrays?

Ein Thema von nru · begonnen am 20. Mai 2010 · letzter Beitrag vom 21. Mai 2010
Antwort Antwort
Seite 1 von 2  1 2      
nru

Registriert seit: 30. Mai 2008
Ort: Bonn
40 Beiträge
 
Delphi 7 Enterprise
 
#1

WM_COPYDATA mit arrays?

  Alt 20. Mai 2010, 01:02
Hallo Community,

ich bräuchte mal einen Denkanschub in Richtung Datenaustausch zwischen Prozessen.

Ich habe
a.) hier gesucht
b.) mit WM_COPYDATA Messages experimentiert
c.) und mich auch an MMF's (Memory Mapped Files) versucht
War leider alles nicht sonderlich erfolgreich.


Was will ich überhaupt erreichen? Einen Datenaustausch zwischen zweier, von mir kontrollierten (und auch entwickelten) Apps, die ein array of Records austauschen sollen, deren Anzahl vorher nicht bekannt ist. App1 fragt per SendMessage an, App2 antwortet datenbankgestützt (und deshalb in variabler Länge) und das per COPYDATA Message und App1 reagiert darauf.

Wie kann ich das am besten angehen?

Messaging an sich ist für mich nicht so das Problem, mein aktuelles Problem ist das der variablen Länge.



Gruss
nru
  Mit Zitat antworten Zitat
Tryer

Registriert seit: 16. Aug 2003
200 Beiträge
 
#2

Re: WM_COPYDATA mit arrays?

  Alt 20. Mai 2010, 01:05
Entweder steht am Anfang der Daten immer die Länge, oder ist da nicht noch ein Param frei in dem du die Länge übertragen kannst?

Zitat:
cbData
DWORD
The size, in bytes, of the data pointed to by the lpData member.
Steckt in der CopyDataStruct.

Grüsse, Dirk
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#3

Re: WM_COPYDATA mit arrays?

  Alt 20. Mai 2010, 08:22
Und was MMFs angeht: Die habe ich zum Datenaustausch hier in diesem Projekt auch benutzt:
http://www.delphipraxis.net/internal....php?p=1071944
Dort habe ich die Größe per Parameter an eine neue Instanz übergeben, du kannst hier ja einfach Messages benutzen, das macht es noch einfacher. Der Quelltext ist auch relativ kurz.

Dies sind die relevanten Teile des Codes:
Delphi-Quellcode:
// ab Zeile 453:
    FMMFFileHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil,
      PAGE_READWRITE, 0, FileListStream.Size, MmfCommunicationName);
    // Handle will be closed in our destructor
    if FMMFFileHandle <> 0 then
    begin
      MMFPointer := MapViewOfFile(FMMFFileHandle, FILE_MAP_WRITE, 0, 0,
        FileListStream.Size);
      FileListStream.Position := 0;
      CopyMemory(MMFPointer, FileListStream.Memory, FileListStream.Size);
      UnmapViewOfFile(MMFPointer);
Ich erzeuge also eine neue MMF mit der Größe des Streams FileListStream. Dann setze ich den View entsprechend und kopiere die Daten hinein, fertig. Jetzt übergebe ich diese Größe (bei mir als Parameter) an den zweiten Prozess.

Dort öffne ich dann die MMF:
Delphi-Quellcode:
// ab Zeile 512:
var
  HandleCount: Integer;
  MMFPointer: Pointer;
  MMFFileHandle: THandle;
begin
  MMFFileHandle := OpenFileMapping(FILE_MAP_READ, True, MmfCommunicationName);
  if AStep2 then // only here we have to wait for the mutex to ensure the file mapping is open
    CreateMutex(nil, False, PChar(UpdateNotifyMutexName));
  if MMFFileHandle <> 0 then
  begin
    MMFPointer := MapViewOfFile(MMFFileHandle, FILE_MAP_READ, 0, 0, 0);
Hier öffne ich einfach mit Offset und Größe 0, so dass die gesamte MMF gemappt wird. Du musst die Größe also gar nicht angeben. Wenn du zum Beispiel am Anfang immer einen Integerwert mit der Größe usw. als Header speicherst, dann reicht das bereits zum Auslesen. Den bekommst du dann mit PInteger(Integer(MMFPointer))^ ausgelesen.

Das sieht bei mir in den folgenden Zeilen so aus um den Integer am Ende des Mappings auszulesen und dann einen Teil der Daten zu kopieren, aber das ist für dich nicht weiter wichtig:
Delphi-Quellcode:
    // Read update list, size is 4 byte less than in AListStreamSize, because we
    // have an integer with the count of handles at the end.
    HandleCount := PInteger(Integer(MMFPointer) + AListStreamSize - SizeOf(HandleCount))^;
    AFileListStream.Size := AListStreamSize - HandleCount * SizeOf(THandle) - SizeOf(HandleCount);
    CopyMemory(AFileListStream.Memory, MMFPointer, AFileListStream.Size);
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#4

Re: WM_COPYDATA mit arrays?

  Alt 20. Mai 2010, 09:33
Schick die Records doch einzeln in einer Schleife per WM_COPYDATA.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
nru

Registriert seit: 30. Mai 2008
Ort: Bonn
40 Beiträge
 
Delphi 7 Enterprise
 
#5

Re: WM_COPYDATA mit arrays?

  Alt 20. Mai 2010, 09:47
Zitat von Luckie:
Schick die Records doch einzeln in einer Schleife per WM_COPYDATA.
Daran hab ich ehrlich gesagt auch schon gedacht. Aber irgendwie beschleicht mich das Gefühl, dass das nicht der richtige Ansatz wäre.

Zuvor werd ich mich mit jaenicke's MMF-Ansatz auseinandersetzen.

Vielen Dank für Euren Input.
  Mit Zitat antworten Zitat
Benutzerbild von Luckie
Luckie

Registriert seit: 29. Mai 2002
37.621 Beiträge
 
Delphi 2006 Professional
 
#6

Re: WM_COPYDATA mit arrays?

  Alt 20. Mai 2010, 09:53
Und warum nicht? Damit kann man auch eine schönen Fortschrittsanzeige einbauen.
Michael
Ein Teil meines Codes würde euch verunsichern.
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#7

Re: WM_COPYDATA mit arrays?

  Alt 20. Mai 2010, 10:02
Zitat von nru:
Zitat von Luckie:
Schick die Records doch einzeln in einer Schleife per WM_COPYDATA.
Daran hab ich ehrlich gesagt auch schon gedacht. Aber irgendwie beschleicht mich das Gefühl, dass das nicht der richtige Ansatz wäre.
Richtig ist der Ansatz durchaus, aber es gibt auch Nachteile. Erstens ist es langsamer, da die Daten einzeln behandelt und geschickt werden müssen. Das spielt bei geringeren Datenmengen kaum eine Rolle, wenn es aber viele Datensätze sind schon.

Zudem ist es denke ich komplizierter die ganzen einzelnen Daten zu schicken und zu empfangen und das alles zu synchronisieren. Möglich ist aber auch das.

Zitat von Luckie:
Und warum nicht? Damit kann man auch eine schönen Fortschrittsanzeige einbauen.
Sicher richtig, aber mit dem MMF-Ansatz sollte erst gar keine nötig sein. Denn damit kann man eben gerade viele MiB in sehr kurzer Zeit kopieren, was einzeln sonst ewig dauern kann. Denn das Kopieren der Daten an sich ist gar nicht nötig, man kann ja direkt mit dem Pointer auf die MMF in beiden Programmen arbeiten.

Wenn man das Programm entsprechend baut, dann kann man auch direkt mit dem Speicherbereich arbeiten und braucht gar kein Array. Dadurch hat man dann die maximale Geschwindigkeit, da man sich auch das Kopieren in die MMF spart.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
nru

Registriert seit: 30. Mai 2008
Ort: Bonn
40 Beiträge
 
Delphi 7 Enterprise
 
#8

Re: WM_COPYDATA mit arrays?

  Alt 20. Mai 2010, 23:39
Zitat von Luckie:
Und warum nicht?
Synchronisation, Handling und Geschwindigkeit waren/sind meine Bedenken.

Doch man kommt ja leider oft sehr schnell auf den Boden der Tatsachen zurück - so auch ich in diesem Fall. Denn meine MMF-Versuche waren leider alles andere als glücklich bzw. erfolgreich. Vielen Dank nochmal an jaenicke für die vielen sehr brauchbaren Code-Schnipsel.

Aber ich habe feststellen müssen, dass mir hier dann doch noch ein paar basics fehlen. Ich habs leider nicht hinbekommen, das dyn. Array of TFindTitel als Pointer in das MMF zu schreiben und anschl. daraus wieder was brauchbares zu casten. Das ist, was ich mit basics meine

Hättet ihr hierzu vielleicht auch noch ein paar Ideen? Das wär echt klasse.


Hier mal meine bisherigen Ansätze (Fragmente).

Sender sucht Infos in DB und verpackt die in ein array, welches dann in die MMF geschrieben werden soll.
Die Rückgabe von FindFilmTitel wird an dvbWriteMMF übergeben (ist hier nicht aufgeführt).

Delphi-Quellcode:

type
   TFindTitel = packed record
      Name: String[255];
      ID: Integer;
   end;
   PFindTitel = ^TFindTitel;

   TFindTitelArray = array of TFindTitel;



function FindFilmTitel( cSearch: String ): TFindTitelArray;
var
   i: Integer;
   x: TFindTitelArray;
begin

// weggelassener DB-Stuff
   SetLength( x, RecCount );
   i := 0;
   while not eof do begin
      x[i].Name := FieldByName('TITEL').AsString;
      x[i].ID := FieldByName('FNUM').AsInteger;
      Inc(i);
      Next;
   end;
   result := x;

end;

procedure dvbWriteMMFile(hwnd: THandle; const x: array of TFindTitel );
var
   FMMFFileHandle: THandle;
   MMFPointer: Pointer;
   memSize: Integer;
begin

   memSize := SizeOf(TFindTitel)*(High(x)+1);

   FMMFFileHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil,
      PAGE_READWRITE, 0, memSize, PChar( MmfCommunicationName ));

   if FMMFFileHandle <> 0 then begin
      MMFPointer := MapViewOfFile(FMMFFileHandle, FILE_MAP_WRITE, 0, 0, memSize);
      CopyMemory(MMFPointer, @x, memSize);
      UnmapViewOfFile(MMFPointer);
   end;

   // Benachrichtigung an wartenden Emfpänger senden (incl. Anzahl Datensätze!)
   // Empfänger geht dann hin und soll MMF lesen
   SendMessage( hwnd, CatchMsg, High(x)+1, 0 );

end;

Empfänger bekommt die Msg und weiss, dass nun aus dem MMF gelesen werden könnte ... und versucht das erfolglos über die folgende Funktion.

Delphi-Quellcode:
function dvbReadMMF( nCount: Integer ): TFindTitelArray;
var
   HandleCount: Integer;
   MMFPointer: Pointer;
   MMFFileHandle: THandle;
   x: TFindTitelArray;
   memSize: Integer;
begin

   MMFFileHandle := OpenFileMapping(FILE_MAP_READ, True, PChar( MmfCommunicationName) );

   if MMFFileHandle <> 0 then begin
      MMFPointer := MapViewOfFile(MMFFileHandle, FILE_MAP_READ, 0, 0, 0);

      SetLength( x, nCount );
      memSize := nCount * SizeOf(TFindTitel);
      CopyMemory(@x, MMFPointer, memSize);

      UnmapViewOfFile(MMFPointer);

   end;

   result := x;

end;
Das resultierende TFindTitelArray bzw. die darin enthaltenen Records sollte dann vom Emfpänger weiterverarbeitet werden können. Geht aber nicht - Exception beim Zuweisen an Result.

Mit sowas
PFindTitelArray = ^TFindTitelArray;
und
CopyMemory(y, MMFPointer, memSize);
result := TFindTitelArray(y^);
hab ich dann auch ohne Erfolg experiementiert.


Ihr wisst sicher auch warum - ich leider noch nicht

Vielleicht doch lieber die "WM_COPYDATA-Je-Datensatz"-Variante?

Gruss
Norbert
  Mit Zitat antworten Zitat
alzaimar
(Moderator)

Registriert seit: 6. Mai 2005
Ort: Berlin
4.956 Beiträge
 
Delphi 2007 Enterprise
 
#9

Re: WM_COPYDATA mit arrays?

  Alt 21. Mai 2010, 05:38
Vielleicht anstatt
Delphi-Quellcode:
...
      CopyMemory(MMFPointer, @x, memSize);
...
      CopyMemory(MMFPointer, @x[0], memSize);
"Wenn ist das Nunstruck git und Slotermeyer? Ja! Beiherhund das Oder die Flipperwaldt gersput!"
(Monty Python "Joke Warefare")
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#10

Re: WM_COPYDATA mit arrays?

  Alt 21. Mai 2010, 09:15
Da hast du Recht, denn sonst wird nicht mit den Werten gearbeitet.

Hier mal aus dem Kopf kurz getippselt eine Variante, bei der die Größenangabe direkt am Anfang der MMF steht. So kann man auch andere Headerangaben mit übertragen. Das zusätzliche Array beim Empfang wird gar nicht gebraucht. Mit alzaimars Korrektur sollte es aber auch direkt schon klappen.
Delphi-Quellcode:
procedure dvbWriteMMFile(hwnd: THandle; const x: array of TFindTitel );
var
  FMMFFileHandle: THandle;
  MMFPointer: Pointer;
  memSize: Integer;
begin
  memSize := SizeOf(TFindTitel)*(High(x)+1);

  FMMFFileHandle := CreateFileMapping(INVALID_HANDLE_VALUE, nil,
    PAGE_READWRITE, 0, memSize + SizeOf(Integer), PChar( MmfCommunicationName ));

  if FMMFFileHandle <> 0 then
  begin
    MMFPointer := MapViewOfFile(FMMFFileHandle, FILE_MAP_WRITE, 0, 0, 0);
    PInteger(MMFPointer)^ := Length(x); // Anzahl der Datensätze an den Anfang schreiben
    Inc(PInteger(MMFPointer)); // Nach der Angabe der Anzahl weitermachen
    CopyMemory(MMFPointer, @x[0], memSize);
    UnmapViewOfFile(MMFPointer);
  end;

   // Benachrichtigung an wartenden Emfpänger senden
   // Empfänger geht dann hin und soll MMF lesen
   SendMessage( hwnd, CatchMsg, 0, 0 );
end;

function dvbReadMMF(): TFindTitelArray;
var
  MMFPointer: Pointer;
  MMFFileHandle: THandle;
  memSize: Integer;
begin
  MMFFileHandle := OpenFileMapping(FILE_MAP_READ, True, PChar( MmfCommunicationName) );

  if MMFFileHandle <> 0 then begin
    MMFPointer := MapViewOfFile(MMFFileHandle, FILE_MAP_READ, 0, 0, 0);

    SetLength(Result, PInteger(MMFPointer)^); // Anzahl der Datensätze auslesen
    memSize := PInteger(MMFPointer)^ * SizeOf(TFindTitel);
    Inc(PInteger(MMFPointer)); // Nach der Angabe der Anzahl weitermachen
    CopyMemory(@Result[0], MMFPointer, memSize);

    UnmapViewOfFile(MMFPointer);
  end;
end;
Ich denke mal so sollte es klappen.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es 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

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 05:13 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz