在Delphi XE6中使用数据管理单元传递TStream
我需要使用DelphiXe6中的数据管理单元传递一些东西(在Delphi XE6中使用数据管理单元传递TStream,delphi,datasnap,Delphi,Datasnap,我需要使用DelphiXe6中的数据管理单元传递一些东西(TStream&TClientdataSet)。让我们从一个TStream开始-也许我在这里学到的是,我可以理解TClientDataSet 这是我的尝试,但它抛出了一个错误: 远程错误:访问模块DSServer.exe中地址0040801C处的违规 //not really doing anything with the RunReportObj yet, // just trying to test whether or not I
TStream
&TClientdataSet
)。让我们从一个TStream
开始-也许我在这里学到的是,我可以理解TClientDataSet
这是我的尝试,但它抛出了一个错误:
远程错误:访问模块DSServer.exe中地址0040801C处的违规
//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;
客户端演示(DSClient.exe)
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)
我肯定我做了一些愚蠢的事情:)你必须小心谁负责释放与DataSnap一起发送的对象
TServerMethods1.GetReport()
不应释放结果,因为它必须先发送到客户端。另一方面,只要FInstanceOwner
为true(默认情况下为true),客户端就不应释放从TServerMethods1Client.GetReport()获取的TStream
第一个条件更多地是偶然满足的,尽管正如David指出的,您正在释放未初始化的局部变量流
在目前无法实际测试的情况下,客户端的正确代码应该如下所示:
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;
问问自己,为什么连续两行分配给S。你泄露了第一条信息流。同样好,因为您实例化了TStream,它是一个抽象类。您真的要销毁第二个流吗?当前错误在TServerMethods1.GetReport的最后一行。对未初始化的变量调用free。您是否禁用了编译器警告。为什么?或者你没有阅读它们?一个有经验的程序员可以看到所有这些基本错误。但有些错误更难。没有经验的程序员不会发现它们。在所有情况下都需要调试。您现在需要做的是学习如何调试。您需要能够识别引发错误的代码行。在您能够有效地调试之前,您会遇到困难。我发现在客户端,TServerMethods1Client.GetReport()
假设自己是RunReportObj
的所有者,尽管创建它的是Button1Click()
。如果在GetReport()
中发生任何错误,RunReportObj
将被泄漏,因为没有人会释放它Button1Click()
应该在使用完毕后将其释放,并且GetReport()
不应该调用RunReportObj.free
。@RemyLebeau,说得好!唉,TServerMethods1Client.GetReport
是自动创建的,下次任何编辑都可能消失。可能建议创建TServerMethods1Client
,并将InstanceOwner
设置为false
。但是,我不确定这会带来什么其他后果。在服务器端,GetReport()
应该在将结果返回到位置0之前,将其搜索回位置0,客户端不应该负责搜索它。另外,可以使用TStreamWriter
而不是TBinaryWriter
,或者使用TStringStream
而不是TMemoryStream
来简化GetReport()
。感谢大家的建设性批评、注释和回答。让我回顾一下你的建议和我的建议,我会做出相应的回应。
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;