因為ServiceObject的函數簽名格式是統一的,可以傳遞的參數只有一個AParams:OLEVariant,用戶端的多個參數需要使用數組傳遞。如:
var<br /> vParams:OLEVariant;<br /> vResult:integer;<br />begin<br /> ...<br /> vParams:=VarArrayCreate([0,1],varVariant);<br /> vParams[0]:=3;<br /> vParams[1]:=2;<br /> ...<br /> vResult:=BizService.Call('system.Test','Sum',vParams);
但是這種方式不太直觀,AParams內部的元素嚴重依賴順序,一旦寫好,很難改變。另一種方式是使用序列化的Object,如:
TMarshalSumParams=class<br />published<br /> A:integer;<br /> B:integer<br />end;
使用某種編碼方式,把這個元件序列化成一個stream,這樣就很容易傳遞到伺服器,伺服器的ServiceObject再把stream還原序列化TMarshalSumParams。
序列化在delphi中,有兩種方式可以利用:
1、使用TWriter和TReader。這兩個元件,是delphi用來讀寫其dfm檔案的,你在剪下/黏貼元件時,也是它們在工作。但是兩個元件唯寫元件published屬性,也沒有專門考慮來做用戶端伺服器的序列化,所以任何published不支援的資料類型,它都不處理,如動態數組,Variant也只支援byte數組。這裡需要修改。TWriter寫出的是二進位流,效率比文本高。
procedure TVariantWriter.WriteArray(const Value: Variant);<br />var<br /> i:integer;<br /> Lo,Hi:integer;<br />begin<br /> WriteValue(vaArray);<br /> Lo:=VarArrayLowBound(Value,1);<br /> Hi:=VarArrayHighBound(Value,1);<br /> Write(Lo,SizeOf(integer));<br /> Write(Hi,SizeOf(integer));<br /> for i:=Lo to Hi do WriteVariant(Value[i]);<br />end;</p><p>procedure TVariantWriter.WriteVariant(const Value: Variant);<br />var<br /> CustomType: TCustomVariantType;<br /> OuterStream, InnerStream: TMemoryStream;<br /> OuterWriter: TWriter;<br /> StreamSize: Integer;<br /> VarStreamer: IVarStreamable;<br /> LInt64: Int64;<br />begin<br /> if VarIsArray(Value) then<br /> begin<br /> if (VarType(Value) and varByte)=varByte then<br /> WriteByteArray(Value)<br /> else<br /> WriteArray(Value);<br /> end<br /> else<br /> case VarType(Value) and varTypeMask of<br /> varEmpty:<br /> WriteValue(vaNil);<br /> varNull:<br /> WriteValue(vaNull);<br /> varOleStr:<br /> WriteWideString(Value);<br /> varString:<br /> WriteString(Value);<br /> varByte, varShortInt, varWord, varSmallInt, varInteger:<br /> WriteInteger(Value);<br /> varSingle:<br /> WriteSingle(Value);<br /> varDouble:<br /> WriteFloat(Value);<br /> varCurrency:<br /> WriteCurrency(Value);<br /> varDate:<br /> WriteDate(Value);<br /> varBoolean:<br /> if Value then<br /> WriteValue(vaTrue)<br /> else<br /> WriteValue(vaFalse);<br /> varLongWord, varInt64:<br /> begin<br /> LInt64 := Value;<br /> WriteInteger(LInt64);<br /> end;<br /> else<br /> try<br /> if not FindCustomVariantType(TVarData(Value).VType, CustomType) or<br /> not Supports(Value, IVarStreamable, VarStreamer) then<br /> WriteString(Value)<br /> else<br /> begin<br /> InnerStream := nil;<br /> OuterStream := TMemoryStream.Create;<br /> try<br /> InnerStream := TMemoryStream.Create;<br /> OuterWriter := TWriter.Create(OuterStream, 1024);<br /> try<br /> VarStreamer.StreamOut(TVarData(Value), InnerStream);<br /> StreamSize := InnerStream.Size;</p><p> OuterWriter.WriteString(CustomType.ClassName);<br /> OuterWriter.Write(StreamSize, SizeOf(StreamSize));<br /> OuterWriter.Write(InnerStream.Memory^, StreamSize);<br /> finally<br /> OuterWriter.Free;<br /> end;<br /> StreamSize := OuterStream.Size;<br /> WriteValue(vaBinary);<br /> Write(StreamSize, SizeOf(StreamSize));<br /> Write(OuterStream.Memory^, StreamSize);<br /> finally<br /> InnerStream.Free;<br /> OuterStream.Free;<br /> end;<br /> end;<br /> except<br /> raise EWriteError.CreateRes(@SWriteError);<br /> end;<br /> end;<br />end;
2、使用SOAP對TRemotable元件的編碼方式,編碼成XML格式。同樣它對Variant只支援byte數組,如果你要支援別的variant類型,需要自己改造。需要說明的是,XML編碼效率比較低,資料量越大越明顯。
function TMarshalComponent._MarshalToXMLStream(<br /> AStream: TStream): WideString;<br />const<br /> Signature:WideChar=#$FEFF;<br />var<br /> vDoc:IXMLDocument;<br /> vConverter:IObjConverter;<br /> {$IFDEF Ver150}<br /> RefID:WideString;<br /> {$ELSE}<br /> RefID:InvString;<br /> {$ENDIF}<br />begin<br />//為何輸出的XML檔案沒有encoding?<br /> ActiveX.CoInitialize(nil);<br /> vDoc:=TXMLDocument.Create(nil);<br /> try<br /> (vDoc as IXMLDocumentAccess).DocumentObject.DOMVendor:=GetDOMVendor(sMSXML);<br /> vDoc.Active:=true;</p><p> vDoc.Version:='1.0';<br /> vDoc.StandAlone:='yes';<br /> vDoc.Encoding:='utf-8';</p><p> vDoc.DocumentElement:=vDoc.CreateNode(Self.ClassName);<br /> vConverter:=OPToSOAPDomConv.TOPToSoapDomConvert.Create(nil);<br /> try<br /> vConverter.ObjInstanceToSOAP(Self,vDoc.DocumentElement,vDoc.DocumentElement,<br /> Self.ClassName,'',[],RefID);<br />//用SaveToStream編碼有問題<br /> vDoc.SaveToXML(Result);<br />//需要增加簽名,否則IE打不開<br /> Result:=Signature+Result;<br /> if AStream<>nil then<br /> begin<br /> AStream.Size:=0;<br /> AStream.Write(Result[1],Length(Result)*2);<br /> end;<br /> vDoc.Active:=false;<br /> finally<br /> vConverter:=nil;<br /> end;<br /> finally<br /> vDoc:=nil;<br /> ActiveX.CoUninitialize;<br /> end;<br />end;
在Delphi2010的時候,支援了Json格式的序列化,它應該是比XML更高效的一種方式。
一旦序列化成stream,我們就可以使用任何壓縮方式在傳遞前壓縮,如zlib,leo,等。