CreateFileMapping between two processes lazarus / 34-Windows ---------------------------------------------------------------------- rpetges · 2018-06-23 10:58 Dear all, I want to use shared data between two Lazarus applications using the Windows FileMapping mechanism. Please have a look at the attached test project. In fact, I need to pass a variable string length from the caller to the receiver. The length can grow up to several KB of data. In the test application, a data structure has been defined and it contains a variable of type string. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i   TData = packed record    FUniqueID: DWORD;    FName: string;  end; The variable FName is initialized as follow (TForm1.SendClick) : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Data.FName := 'ABC-'; Data is correctly exchanged when clicking on the "Send" and then "Receive" button, even between TWO instances. Large strings of more than 800 characters also work without problems. Now, initialization code is changed to : Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i Data.FName := 'ABC-';Data.FName := Data.FName + 'DEF'; For ONE instance, it still works fine. When you launch TWO instances of the compiled application, and click "Send" in the first one and then "Receive" in the second instance, some garbage data is transmitted, but not the requested data. In the message dialog, the text for the label "Name" may be empty or the "Receiver" application may crash. I tried so many things and can't get it working. Maybe it's just a small adjustment. How can I get this working ? Many thanks for your help, Romain ---------------------------------------------------------------------- Martin_fr · 2018-06-23 13:02 "string" defaults to "ansistring" It may work if you use shortstring. But then the length is limited. Ansistring, is just a pointer to some memory (within the current process) where the actual text is. So you just copy this pointer. If you read within the same process, then it may work sometimes (kind of, it may leak mem). You need to copy the actual text data instead. ---------------------------------------------------------------------- rpetges · 2018-06-23 13:25 Shortstring works, but I'm too limited in size. Can you provide a practical example that works for dynamic text sizes ... I'm somehow lost  Many thanks ---------------------------------------------------------------------- ASerge · 2018-06-23 14:35 Quote from: Romain on June 23, 2018, 03:25:29 pmCan you provide a practical example that works for dynamic text sizes ... I'm somehow lost  Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program Project1;{$APPTYPE CONSOLE}{$MODE OBJFPC}{$LONGSTRINGS ON} uses SysUtils, uPageFileStream; const  CMapName = '{9D809F6B-FC10-4E4F-B352-4A7773762BAA}'; // Any unique name type  TData = record    FUniqueID: DWORD;    FName: string;  end; function Receive(out Data: TData): Boolean;var  Stream: TPageFileStream;begin  Stream := TPageFileStream.CreateForRead(CMapName);  try    Result := (pfsValid in Stream.States) and (Stream.Size >= SizeOf(Data));    if Result then    begin      Data.FUniqueID := Stream.ReadDWord;      Data.FName := Stream.ReadAnsiString;    end;  finally    Stream.Free;  end;end; var  Stream: TPageFileStream;  DataIn, DataOut: TData;begin  DataIn.FUniqueID := 15;  DataIn.FName := 'Some string';  // SizeOf(DataIn) + Length(DataIn.FName) is enough  Stream := TPageFileStream.Create(SizeOf(DataIn) + Length(DataIn.FName), CMapName);  try    Stream.WriteDWord(DataIn.FUniqueID);    Stream.WriteAnsiString(DataIn.FName);    // Do interprocess    if Receive(DataOut) then      Writeln('ID=', DataOut.FUniqueID, ', Name=', DataOut.FName)    else      Writeln(SysErrorMessage(GetLastOSError));  finally    Stream.Free;  end;  Readln;end. unit uPageFileStream are included. ---------------------------------------------------------------------- rpetges · 2018-06-23 15:06 Works great  Many thanks ASerge ! ---------------------------------------------------------------------- Remy Lebeau · 2018-06-25 23:30 Quote from: ASerge on June 23, 2018, 04:35:09 pmStream := TPageFileStream.Create(SizeOf(DataIn) + Length(DataIn.FName), CMapName); DataIn is a TData, which contains a string, which is a pointer.  So right off the bat, you are relying on SizeOf(Pointer) = 4 to match the 4-byte leading DWORD that WriteAnsiString() writes.  That is not the case if you compile for 64-bit instead of 32-bit.  OK granted, an 8-byte pointer will simply allocate more memory than needed, no harm done.  But you are also relying on the string containing characters that fit within 8 bits each when written by WriteAnsiString(). That is certainly the case when string=AnsiString, but not guaranteed when string=UnicodeString, such as in {$ModeSwitch UnicodeStrings}.  So again, you might end up over-allocating when the length of a Unicode string is greater than the length of the ANSI equivalent.  In fact, WriteAnsiString() and ReadAnsiString() are both coded to assume string=AnsiString, so they won't even work correctly when string=UnicodeString. Personally, I wouldn't suggest serializing strings using AnsiString at all. Better to use UTF-8 instead, eg: Code: [Select]function Receive(out Data: TData): Boolean; var   Stream: TPageFileStream;   TheSize: DWORD;   S: UTF8String; begin   Result := False;   Stream := TPageFileStream.CreateForRead(CMapName);   try     if (pfsValid in Stream.States) and (Stream.Size >= (SizeOf(DWord)*2)) then     begin       Data.FUniqueID := Stream.ReadDWord;       TheSize := Stream.ReadDWord;       if (TheSize > 0) and ((Stream.Size - Stream.Position) >= TheSize) then       begin         SetLength(S, TheSize);         Stream.ReadBuffer(Pointer(S)^, TheSize);         Data.FName := UTF8Decode(S);         Result := True;       end;     end;   finally     Stream.Free;   end; end;     function Send(const Data: TData): Boolean; var   Stream: TPageFileStream;   TheSize: DWORD;   S: UTF8String; begin   Result := False;   S := UTF8Encode(Data.FName);   TheSize := Length(S);   Stream := TPageFileStream.Create((SizeOf(DWord) * 2) + TheSize, CMapName);   try     if pfsValid in Stream.States then     begin       Stream.WriteDWord(DataIn.FUniqueID);       Stream.WriteDWord(TheSize);       Stream.WriteBuffer(Pointer(S)^, TheSize);     end;   finally     Stream.Free;   end; end; var   DataIn, DataOut: TData; begin   DataIn.FUniqueID := 15;   DataIn.FName := 'Some string';   if Send(DataIn) then   begin     // Do interprocess     if Receive(DataOut) then       Writeln('ID=', DataOut.FUniqueID, ', Name=', DataOut.FName)     else       Writeln(SysErrorMessage(GetLastOSError));   end else       Writeln(SysErrorMessage(GetLastOSError));   Readln; end. ---------------------------------------------------------------------- marcov · 2018-06-26 11:09 (FPC 3.x based Lazaruses change the default 1-byte encoding to utf8)