在 Delphi XE6 中使用 Data Snap 传递 TStream

passing a TStream using Data Snap in Delphi XE6

我需要在 Delphi XE6 (TStream & TClientdataSet) 中使用 Data Snap 传递一些东西。让我们从 TStream 开始 - 也许我在这里学到的东西,我可以弄清楚 TClientDataSet.

这是我的尝试,但会引发错误:

Remote error: Access violaion at address 0040801C in module DSServer.exe

客户端演示 (DSClient.exe)

//RunReportObj is the real object I will be passing to the server method

Procedure TForm8.Button1Click(Sender: TObject);
var
  RunReportObj: TRunReportObject;
  S: TStream;
  FS: TFileStream;
begin
  RunReportObj:= TRunReportObject.Create;
  RunReportObj.ID:= '10101';
  RunReportObj.ReportName:= 'Test';
  RunReportObj.ExportType:= 'PDF';
  S:= TStream.Create;
   try
    S:= ClientModule1.ServerMethods1Client.GetReport(RunReportObj);
    S.Seek(0,soFromBeginning);
    FS:= TFileStream.Create(RunReportObj.ReportName + '.' + RunReportObj.ExportType, fmOpenWrite);;
    try
      FS.CopyFrom(S, S.Size);
    finally
      FS.Free;
    end;
   finally
    S.Free;
   end;
end;

ClientClassesUnit1.pas

function TServerMethods1Client.GetReport(RunReportObj: TRunReportObject): TStream;
begin
  if FGetReportCommand = nil then
  begin
    FGetReportCommand := FDBXConnection.CreateCommand;
    FGetReportCommand.CommandType := TDBXCommandTypes.DSServerMethod;
    FGetReportCommand.Text := 'TServerMethods1.GetReport';
    FGetReportCommand.Prepare;
  end;
  if not Assigned(RunReportObj) then
    FGetReportCommand.Parameters[0].Value.SetNull
  else
  begin
    FMarshal := TDBXClientCommand(FGetReportCommand.Parameters[0].ConnectionHandler).GetJSONMarshaler;
    try
      FGetReportCommand.Parameters[0].Value.SetJSONValue(FMarshal.Marshal(RunReportObj), True);
      if FInstanceOwner then
        RunReportObj.Free
    finally
      FreeAndNil(FMarshal)
    end
    end;
  FGetReportCommand.ExecuteUpdate;
  Result := FGetReportCommand.Parameters[1].Value.GetStream(FInstanceOwner);
end;

服务器演示 (DSServer.exe)

//not really doing anything with the RunReportObj yet,
// just trying to test whether or not I can pass a TStream back first

function TServerMethods1.GetReport(RunReportObj: TRunReportObject): TStream;
var
  Stream: TMemoryStream;
  Writer: TBinaryWriter;
  Bytes: TBytes;
begin
  result := TMemoryStream.Create;
  try
    Writer := TBinaryWriter.Create(result);
    try
      Writer.Write(TEncoding.UTF8.GetBytes('Hello World' + sLineBreak));
    finally
      Writer.Free;
    end;
  finally
    Stream.Free;
  end;
end;

我确定我做了一些愚蠢的事情:)

您必须注意谁负责释放使用 DataSnap 发送的对象。 TServerMethods1.GetReport() 不应释放 Result,因为它必须先发送给客户端。另一方面,只要 FInstanceOwner 为真(默认情况下为真),客户端不应释放从 TServerMethods1Client.GetReport() 获得的 TStream

第一个条件的满足更多是偶然的,尽管正如 David 指出的那样,您正在释放未初始化的局部变量 Stream。

目前无法对此进行实际测试,客户端的正确代码应如下所示:

Procedure TForm8.Button1Click(Sender: TObject);
var
  RunReportObj: TRunReportObject;
  S: TStream;
  FS: TFileStream;
begin
  RunReportObj:= TRunReportObject.Create;
  RunReportObj.ID:= '10101';
  RunReportObj.ReportName:= 'Test';
  RunReportObj.ExportType:= 'PDF';
  S:= ClientModule1.ServerMethods1Client.GetReport(RunReportObj);
  S.Seek(0,soFromBeginning);
  FS:= TFileStream.Create(RunReportObj.ReportName + '.' + RunReportObj.ExportType, fmOpenWrite);;
  try
    FS.CopyFrom(S, S.Size);
  finally
    FS.Free;
  end;
end;

对于服务器端:

function TServerMethods1.GetReport(RunReportObj: TRunReportObject): TStream;
var
  Writer: TBinaryWriter;
  Bytes: TBytes;
begin
  result := TMemoryStream.Create;
  Writer := TBinaryWriter.Create(result);
  try
    Writer.Write(TEncoding.UTF8.GetBytes('Hello World' + sLineBreak));
  finally
    Writer.Free;
  end;
end;