当前位置: 首页 >> 程序设计 >> 远程调用技术代码追踪(webservice)
 

远程调用技术代码追踪(webservice)

作者:      来源:http://blog.csdn.net/wu_yanan2003     发表时间:2007-02-27     浏览次数:      字号:    

 
procedure TSOAPDomConv.ConvertSoapToNativeData(DataP: Pointer; TypeInfo: PTypeInfo;
 Context: TDataContext; RootNode, Node: IXMLNode; Translate, ByRef: Boolean;
 NumIndirect: Integer);
var
 TypeUri, TypeName: InvString;
 IsNull: Boolean;
 Obj: TObject;
 P: Pointer;
 I: Integer;
 ID: InvString;
begin
 Node := GetDataNode(RootNode, Node, ID);
 IsNull := NodeIsNull(Node);
 if TypeInfo.Kind = tkVariant then
 begin
    if NumIndirect > 1 then
      DataP := Pointer(PInteger(DataP)^);
    if IsNull then
    begin
      Variant(PVarData(DataP)^) := NULL;
    end else
      ConvertSoapToVariant(Node, DataP);
 end else
 if TypeInfo.Kind = tkDynArray then
 begin
    P := DataP;
    for I := 0 to NumIndirect - 2 do
      P := Pointer(PInteger(P)^);
    P := ConvertSoapToNativeArray(P, TypeInfo, RootNode, Node);
    if NumIndirect = 1 then
      PInteger(DataP)^ := Integer(P)
    else if NumIndirect = 2 then
    begin
      DataP := Pointer(PInteger(DataP)^);
      PInteger(DataP)^ := Integer(P);
    end;
 end else
 if TypeInfo.Kind = tkClass then
 begin
    Obj := ConvertSOAPToObject(RootNode, Node, GetTypeData(TypeInfo).ClassType, TypeURI, TypeName, DataP, NumIndirect);
    if NumIndirect = 1 then
      PTObject(DataP)^ := Obj
   else if NumIndirect = 2 then
    begin
      DataP := Pointer(PInteger(DataP)^);
      PTObject(DataP)^ := Obj;
    end;
 end else
 begin
    if Translate then
    begin
      if NumIndirect > 1 then
        DataP := Pointer(PInteger(DataP)^);
      if not TypeTranslator.CastSoapToNative(TypeInfo, GetNodeAsText(Node), DataP, IsNull) then
        raise ESOAPDomConvertError.CreateFmt(STypeMismatchInParam, [node.nodeName]);
    end;
 end;
end;
 
作为整型数据,处理方式为:
if not TypeTranslator.CastSoapToNative(TypeInfo, GetNodeAsText(Node), DataP, IsNull) then
 
function TTypeTranslator.CastSoapToNative(Info: PTypeInfo; const SoapData: WideString; NatData: Pointer; IsNull: Boolean): Boolean;
var
 ParamTypeData: PTypeData;
begin
 DecimalSeparator := '.';
 Result := True;
 if IsNull and (Info.Kind = tkVariant) then
 begin
    Variant(PVarData(NatData)^) := NULL;
    Exit;
 end;
 ParamTypeData := GetTypeData(Info);
 case Info^.Kind of
    tkInteger:
      case ParamTypeData^.OrdType of
        otSByte,
        otUByte:
          PByte(NatData)^ := StrToInt(Trim(SoapData));
        otSWord,
        otUWord:
          PSmallInt(NatData)^ := StrToInt(Trim(SoapData));
        otSLong,
        otULong:
          PInteger(NatData)^ := StrToInt(Trim(SoapData));
      end;
    tkFloat:
      case ParamTypeData^.FloatType of
        ftSingle:
          PSingle(NatData)^ := StrToFloatEx(Trim(SoapData));
        ftDouble:
        begin
          if Info = TypeInfo(TDateTime) then
            PDateTime(NatData)^ := XMLTimeToDateTime(Trim(SoapData))
          else
            PDouble(NatData)^ := StrToFloatEx(Trim(SoapData));
        end;
 
        ftComp:
          PComp(NatData)^ := StrToFloatEx(Trim(SoapData));
        ftCurr:
          PCurrency(NatData)^ := StrToFloatEx(Trim(SoapData));
        ftExtended:
          PExtended(NatData)^ := StrToFloatEx(Trim(SoapData));
      end;
    tkWString:
      PWideString(NatData)^ := SoapData;
    tkString:
      PShortString(NatData)^ := SoapData;
    tkLString:
      PString(NatData)^ := SoapData;
    tkChar:
      if SoapData <> '' then
        PChar(NatData)^ := Char(SoapData[1]);
    tkWChar:
      if SoapData <> '' then
        PWideChar(NatData)^ := WideChar(SoapData[1]);
    tkInt64:
      PInt64(NatData)^ := StrToInt64(Trim(SoapData));
 
    tkEnumeration:
      { NOTE: Here we assume enums to be byte-size; make sure (specially for C++)
              that enums have generated with the proper size }
      PByte(NatData)^ := GetEnumValueEx(Info, Trim(SoapData));
    tkClass:
      ;
    tkSet,
    tkMethod,
 
    tkArray,
    tkRecord,
    tkInterface,
 
    tkDynArray:
      raise ETypeTransException.CreateFmt(SUnexpectedDataType, [ KindNameArray[Info.Kind]] );
    tkVariant:
      CastSoapToVariant(Info, SoapData, NatData);
 end;
end;
 
PWideString(NatData)^ := SoapData;
通过把值赋给了相应的指针地址:
 
另外在看一下传对象时的情况:
Obj := ConvertSOAPToObject(RootNode, Node, GetTypeData(TypeInfo).ClassType, TypeURI, TypeName, DataP, NumIndirect);
 
 
if Assigned(Obj) and LegalRef then
    begin
      if (NodeClass <> nil) and (NodeClass <> Obj.ClassType) then
        Obj := NodeClass.Create;
    end else
    begin
    if (NodeClass <> nil) and NodeClass.InheritsFrom(AClass) then
      Obj := TRemotableClass(NodeClass).Create
    else
      Obj := TRemotableClass(AClass).Create;
    end;
Result := Obj;
 
可以理解,经过双边注册过之后,才可以传递对象。
 
现在研究一下服务器端的代码:
先大概简单介绍一下WEB服务器应用程序的工作模式:
 这里的WEB服务器就是IIS。
 
也就是说WEB服务器会把客户的HTTP请求消息,传递给CGI程序。然后由CGI进行处理:
 
CGIApp单元中的:
procedure InitApplication;
begin
 Application := TCGIApplication.Create(nil);
end;
//创建一个CGI的应用程序
 
constructor TWebApplication.Create(AOwner: TComponent);
begin
 WebReq.WebRequestHandlerProc := WebRequestHandler;
 inherited Create(AOwner);
 
 Classes.ApplicationHandleException := HandleException;
 if IsLibrary then
 begin
    IsMultiThread := True;
    OldDllProc := DLLProc;
    DLLProc := DLLExitProc;
 end
 else
    AddExitProc(DoneVCLApplication);
end;
 
constructor TWebRequestHandler.Create(AOwner: TComponent);
begin
 inherited Create(AOwner);
 FCriticalSection := TCriticalSection.Create;
 FActiveWebModules := TList.Create;
 FInactiveWebModules := TList.Create;
 FWebModuleFactories := TWebModuleFactoryList.Create;
 FMaxConnections := 32;
 FCacheConnections := True;
end;
 
procedure TCGIApplication.Run;
var
 HTTPRequest: TCGIRequest;
 HTTPResponse: TCGIResponse;
begin
 inherited Run;
 if IsConsole then
 begin
    Rewrite(Output);
    Reset(Input);
 end;
 try
    HTTPRequest := NewRequest;
    try
      HTTPResponse := NewResponse(HTTPRequest);
      try
        HandleRequest(HTTPRequest, HTTPResponse);
      finally
        HTTPResponse.Free;
      end;
    finally
      HTTPRequest.Free;
    end;
 except
    HandleServerException(ExceptObject, FOutputFileName);
 end;
end;
HTTPResponse := NewResponse(HTTPRequest);
调用:
function TCGIApplication.GetFactory: TCGIFactory;
begin
 if not Assigned(FFactory) then
    FFactory := TCGIFactory.Create;
 Result := FFactory;
end;
 
 
function TCGIFactory.NewRequest: TCGIRequest;
    Result := TCGIRequest.Create   
。。。
end;
//创建TCGIRequest
HTTPResponse := NewResponse(HTTPRequest);
Result := TCGIResponse.Create(CGIRequest)
HandleRequest(HTTPRequest, HTTPResponse);调用
 
现在看看是怎么响应客户端的:
 
function TWebRequestHandler.HandleRequest(Request: TWebRequest;
 Response: TWebResponse): Boolean;
var
 I: Integer;
 WebModules: TWebModuleList;
 WebModule: TComponent;
 WebAppServices: IWebAppServices;
 GetWebAppServices: IGetWebAppServices;
begin
 Result := False;
 WebModules := ActivateWebModules;
继续:
function TWebRequestHandler.ActivateWebModules: TWebModuleList;
begin
………………
FWebModuleFactories.AddFactory(TDefaultWebModuleFactory.Create(WebModuleClass));
把TWebModule1加入工厂中,并创建TwebModuleList对象。
 
        if FWebModuleFactories.ItemCount > 0 then
        begin
          Result := TWebModuleList.Create(FWebModuleFactories);
………………..
 
继续:
 if Assigned(WebModules) then
 try
WebModules.AutoCreateModules;
 
procedure TWebModuleList.AutoCreateModules
….... AddModule(Factory.GetModule);
 
调用:TWebModule1.create并加入TwebModuleList中。
function TDefaultWebModuleFactory.GetModule: TComponent;
begin
 Result := FComponentClass.Create(nil);
end;
 
constructor TWebModule.Create(AOwner: TComponent);调用
constructor TCustomWebDispatcher.Create(AOwner: TComponent);
 
之后又创建了THTTPSoapDispatcher,创建是在Treader类中创建的,有兴趣的朋友就追踪一下吧,这里实在是太麻烦。我也追了很久才发现。就懒得贴上来了。内容太多。
继续创建了TWSDLHTMLPublish
 
在回到TWebRequestHandler.HandleRequest函数中:
。。。
Result := WebAppServices.HandleRequest;
 
最后调用了:
function TCustomWebDispatcher.HandleRequest(
 Request: TWebRequest; Response: TWebResponse): Boolean;
begin
 FRequest := Request;
 FResponse := Response;
 Result := DispatchAction(Request, Response);
end;
注意HandleRequest函数,这里是关键部分:
 
function TCustomWebDispatcher.DispatchAction(Request: TWebRequest;
 Response: TWebResponse): Boolean;
…………………
while not Result and (I < FDispatchList.Count) do
 begin
    if Supports(IInterface(FDispatchList.Items[I]), IWebDispatch, Dispatch) then
    begin
      Result := DispatchHandler(Self, Dispatch,
        Request, Response, False);
    end;
    Inc(I);
 end;
继续:
function DispatchHandler(Sender: TObject; Dispatch: IWebDispatch; Request: TWebRequest; Response: TWebResponse;
 DoDefault: Boolean): Boolean;
begin
 Result := False;
 if (Dispatch.Enabled and ((Dispatch.MethodType = mtAny) or
    (Dispatch.MethodType = Dispatch.MethodType)) and
    Dispatch.Mask.Matches(Request.InternalPathInfo)) then
 begin
    Result := Dispatch.DispatchRequest(Sender, Request, Response);
 end;
end;
 
 
http调用在到达服务器后,WebModule父类TCustomWebDispatcher
会对其进行分析,抽取参数等信息。然后在TCustomWebDispatcher.HandleRequest
方法中调用TCustomWebDispatcher.DispatchAction方法,将调用
根据其path info重定向到相应的处理方法去。而DispatchAction方法将
Action重定向到FDispatchList字段中所有的实现了IWebDispatch接口的组件。
而THTTPSoapDispatcher正是实现了IWebDispatch,其将在
TCustomWebDispatcher.InitModule方法中被自动检测到并加入FDispatchList字段
具体如下:
procedure TCustomWebDispatcher.InitModule(AModule: TComponent);
var
  I: Integer;
  Component: TComponent;
  DispatchIntf: IWebDispatch;
begin
  if AModule <> nil then
    for I := 0 to AModule.ComponentCount - 1 do
    begin
      Component := AModule.Components[I];
      if Supports(IInterface(Component), IWebDispatch, DispatchIntf) then
        FDispatchList.Add(Component);
    end;
end;
...
  THTTPSoapDispatcher = class(THTTPSoapDispatchNode, IWebDispatch)
 
因此Web Service程序的http请求处理实际上是由THTTPSoapDispatcher进行的。
 
 
我们接着看看THTTPSoapDispatcher.DispatchRequest方法中对SOAP
协议的处理,关键代码如下
function THTTPSoapDispatcher.DispatchRequest(Sender: TObject;
 Request: TWebRequest; Response: TWebResponse): Boolean;
var
…..
 http信息被封装在TwebRequest里:我们来看是怎么进行分析的:
 
SoapAction := Request.GetFieldByName(SHTTPSoapAction);
首先得到SOAPAction信息, 这个SOAPAction大家应该比较熟悉了,前面讲过,这里主要是根据相应信息调用方法:() 具体的内容例如:urn:MyFirstWSIntf-IMyFirstWS
….
 
        if SoapAction = '' then
          SoapAction := Request.GetFieldByName('HTTP_' + UpperCase(SHTTPSoapAction)); { do not localize }
CGI或者Apache的处理方式。如果不是SOAP请求,就默认HTTP请求。
 
记录请求的路径。
Path := Request.PathInfo;
XMLStream := TMemoryStream.Create; //把客户端的请求流化。
ReqStream := TWebRequestStream.Create(Request);
创建一个响应的流信息,以例把结果返回客户端
 
RStream := TMemoryStream.Create; 创建返回信息的流。
try
       FSoapDispatcher.DispatchSOAP(Path, SoapAction, XMLStream, RStream, BindingTypeIn);
这句是最重要的:
它把HTTP的调用方法,委托给THTTPSoapPascalInvoker.DispatchSOAP来处理。
 
FSoapDispatcher.DispatchSOAP(Path, SoapAction, XMLStream, RStream, BindingTypeIn);
 
IHTTPSoapDispatch = interface
 ['{9E733EDC-7639-4DAF-96FF-BCF141F7D8F2}']
    procedure DispatchSOAP(const Path, SoapAction: WideString; const Request: TStream;
                           Response: TStream; var BindingType: TWebServiceBindingType);
 end;
父类实现的接口:
THTTPSoapDispatchNode = class(TComponent)
 private
    procedure SetSoapDispatcher(const Value: IHTTPSoapDispatch);
 protected
    FSoapDispatcher: IHTTPSoapDispatch;
    procedure Notification(AComponent: TComponent; Operation: TOperation); override;
 public
    procedure DispatchSOAP(const Path, SoapAction: WideString; const Request: TStream;
      Response: TStream); virtual;
 published
    property Dispatcher: IHTTPSoapDispatch read FSoapDispatcher write SetSoapDispatcher;
 end;
 
也被THTTPSoapPascalInvoker实现。所以THTTPSoapDispatcher中的Dispatcher接口的实例其实是:THTTPSoapPascalInvoker
 
THTTPSoapPascalInvoker = class(TSoapPascalInvoker, IHTTPSoapDispatch)
 public
    procedure DispatchSOAP(const Path, SoapAction: WideString; const Request: TStream;
                           Response: TStream; var BindingType: TWebServiceBindingType); virtual;
 end;
 
FSoapDispatcher.DispatchSOAP(Path, SoapAction, XMLStream, RStream, BindingTypeIn);
相应于调用了:
 
procedure THTTPSoapPascalInvoker.DispatchSOAP(const Path, SoapAction: WideString; const Request: TStream;
                                              Response: TStream; var BindingType: TWebServiceBindingType);
var
 IntfInfo: PTypeInfo;
 PascalBind: IHTTPSOAPToPasBind;
 InvClassType: TClass;
 ActionMeth: String;
  MD: TIntfMetaData;
 
if not PascalBind.BindToPascalByPath(Path, InvClassType, IntfInfo, ActionMeth) or (InvClassType = nil) then
调用:
function THTTPSOAPToPasBind.BindToPascalByPath(Path: String;
 var AClass: TClass; var IntfInfo: PTypeInfo; var AMeth: String): Boolean;
begin
 Result := InvRegistry.GetInfoForURI(Path, '', AClass, IntfInfo, AMeth);
end;
由InvRegistry的注册信息,返回相应的类名,接口信息等信息。
这了这些准备信息,下步才是真正的调用。
Invoke(InvClassType, IntfInfo, ActionMeth, Request, Response, BindingType);
函数最后一句:调用了父类:这里是真正工作的地方:
这里了仔细认真研究一下:
 
procedure TSoapPascalInvoker.Invoke(AClass: TClass; IntfInfo: PTypeInfo; MethName: string; const Request: TStream;
                                    Response: TStream; var BindingType: TWebServiceBindingType);
var
 Inv: TInterfaceInvoker;
 Obj: TObject;
 InvContext: TInvContext;
 IntfMD: TIntfMetaData;
 MethNum: Integer;
 SOAPHeaders: ISOAPHeaders;
 Handled: Boolean;
begin
 try
 
GetIntfMetaData(IntfInfo, IntfMD, True); 得到接口RTTL信息;
InvContext := TInvContext.Create;    构造调用堆栈。
   { Convert XML to Invoke Context }
          FConverter.MsgToInvContext(Request, IntfMD, MethNum, InvContext, FHeadersIn);
这个函数请见前面的参考InvContextToMsg, 把TinvContext内容转化成XML封包。
 
这个函数是逆操作,把XML内容转化成Context。
 
 
 
try
Obj := InvRegistry.GetInvokableObjectFromClass(AClass);
搜寻注册信息,创建实例:
            if Obj = nil then
raise Exception.CreateFmt(SNoClassRegistered, [IntfMD.Name]);
……………..
Inv := TInterfaceInvoker.Create;
Inv.Invoke(Obj, IntfMD, MethNum, InvContext);
真正调用的地方:

源代码为:
这段代码,就是根据对象,接口信息等,把CONtext的信息压入相应参数,应调用。
有时间再仔细研究。
 
procedure TInterfaceInvoker.Invoke(const Obj: TObject;
      IntfMD: TIntfMetaData; const MethNum: Integer;
      const Context: TInvContext);
var
 MethPos: Integer;
 Unk: IUnknown;
 IntfEntry: PInterfaceEntry;
 IntfVTable: Pointer;
 RetIsOnStack, RetIsInFPU, RetInAXDX: Boolean;
 I: Integer;
 RetP : Pointer;
 MD : TIntfMethEntry;
 DataP: Pointer;
 Temp, Temp1: Integer;
 RetEAX: Integer;
 RetEDX: Integer;
 TotalParamBytes: Integer;
 ParamBytes: Integer;
begin
{$IFDEF LINUX}
 try
{$ENDIF}
 TotalParamBytes := 0;
 MD := IntfMD.MDA[MethNUm]; //得到方法的动态数组信息:
 if not Obj.GetInterface(IntfMD.IID, Unk) then
    raise Exception.CreateFmt(SNoInterfaceGUID,
      [Obj.ClassName, GUIDToString(IntfMD.IID)]);
 IntfEntry := Obj.GetInterfaceEntry(IntfMD.IID); //得到接口的动态数组信息
 IntfVTable := IntfEntry.VTable; //指向VTB表的指针
 MethPos := MD.Pos * 4; { Pos is absolute to whole VMT } //定位
 if MD.ResultInfo <> nil then
 begin
    RetIsInFPU := RetInFPU(MD.ResultInfo);
    RetIsOnStack := RetOnStack(MD.ResultInfo);
    RetInAXDX := IsRetInAXDX(MD.ResultInfo);
    RetP := Context.GetResultPointer;     //根据context  得到返回参数的地址。
 end else
 begin
    RetIsOnStack := False;
    RetIsInFPU := False;
    RetInAXDX := False;
 end;
 
 if MD.CC in [ccCDecl, ccStdCall, ccSafeCall] then
 begin
    if (MD.ResultInfo <> nil) and (MD.CC = ccSafeCall) then
      asm PUSH DWORD PTR [RetP] end;    //把函数返回参数压入堆栈中。
    for I := MD.ParamCount - 1 downto 0 do   //遍历参数。
    begin
      DataP := Context.GetParamPointer(I);    //指向一个参数地址:
      if IsParamByRef(MD.Params[I].Flags,MD.Params[I].Info, MD.CC) then {基本类型}
      asm
        PUSH DWORD PTR [DataP]       //压入堆栈。
      end
      else
      begin
        ParamBytes := GetStackTypeSize(MD.Params[I].Info, MD.CC);    {特殊类型}
        PushStackParm(DataP, ParamBytes);
        Inc(TotalParamBytes, ParamBytes);
      end;
    end;
    asm PUSH DWORD PTR [Unk] end;         //压入Iunknown指针
    if RetIsOnStack and (MD.CC <> ccSafeCall) then
      asm PUSH DWORD PTR [RetP] end;
 end
 else if MD.CC = ccPascal then
 begin
    for I := 0 to MD.ParamCount - 1 do
    begin
      DataP := Context.GetParamPointer(I);
      if IsParamByRef(MD.Params[I].Flags,MD.Params[I].Info, MD.CC) then
      asm
         PUSH DWORD PTR [DataP]
      end
      else
      begin
//        PushStackParm(DataP, GetStackTypeSize(MD.Params[I].Info, MD.CC));
        ParamBytes := GetStackTypeSize(MD.Params[I].Info, MD.CC);
        PushStackParm(DataP, ParamBytes);
        Inc(TotalParamBytes, ParamBytes);
      end;
    end;
    if RetIsOnStack then
      asm PUSH DWORD PTR [RetP] end;
    asm PUSH DWORD PTR [Unk] end;
 end else
     raise Exception.CreateFmt(SUnsupportedCC, [CallingConventionName[MD.CC]]);
 
 if MD.CC <> ccSafeCall then
 begin
    asm
      MOV DWORD PTR [Temp], EAX   //把EAX保存到临时变量中
      MOV DWORD PTR [Temp1], ECX //把ECX保存到临时变量中
 
      MOV EAX, MethPos     //函数定位的地方
      MOV ECX, [IntfVtable]   //虚拟表的入口
      MOV ECX, [ECX + EAX]   //真正调用的地址
      CALL ECX
      MOV DWORD PTR [RetEAX], EAX //把结果返回的信息保存在变量RetEAX(低位)
      MOV DWORD PTR [RetEDX], EDX //把结果返回的信息保存在变量RetEDX(高位)
      MOV EAX, DWORD PTR [Temp]    //恢复寄存器EAX
      MOV ECX, DWORD PTR [Temp1]   //恢复寄存器ECX
 
    end;
 end else
 begin
    asm
      MOV DWORD PTR [Temp], EAX
      MOV DWORD PTR [Temp1], ECX
      MOV EAX, MethPos
      MOV ECX, [IntfVtable]
      MOV ECX, [ECX + EAX]
      CALL ECX
      CALL System.@CheckAutoResult
      MOV DWORD PTR [RetEAX], EAX
      MOV DWORD PTR [RetEDX], EDX
      MOV EAX, DWORD PTR [Temp]
      MOV ECX, DWORD PTR [Temp1]
    end;
 end;
 
 if MD.CC = ccCDecl then /如果是CCDECL方式,必须自己清除使用的堆栈。
 asm
    MOV EAX, DWORD PTR [TotalParamBytes]
    ADD ESP, EAX
 end;
 
//调用后,返回参数的处理:
 if MD.ResultInfo <> nil then 
 begin
    if MD.CC <> ccSafeCall then //返回类型不为ccSafeCall时,必须进行处理。
    begin
      if RetIsInFPU then //tkFloat类型:
      begin
        GetFloatReturn(RetP, GetTypeData(MD.ResultInfo).FloatType);
      end else if not RetIsOnStack then 
      begin
        if RetInAXDX then //tkInt64整型64位类型处理:
        asm
            PUSH EAX
            PUSH ECX
            MOV EAX, DWORD PTR [RetP]
            MOV ECX, DWORD PTR [RetEAX]
            MOV [EAX], ECX
            MOV ECX, DWORD PTR [RetEDX]
            MOV [EAX + 4], ECX
            POP ECX
            POP EAX
        end
        else
        asm                     //堆栈类型:
            PUSH EAX                      //EAX入栈
            PUSH ECX                      //ECX入栈
            MOV EAX, DWORD PTR [RetP]    //返回地址MOV到EAX
            MOV ECX, DWORD PTR [RetEAX] // RetEAX中是调用后得到的值
            MOV [EAX], ECX        //把调用后的结果写入返回的地址中 
            POP ECX                        //ECX出栈
            POP EAX                        //EAX出栈 (先入后出)
 
        end;
      end;
    end;
 end;
{$IFDEF LINUX}
 except
    // This little bit of code is required to reset the stack back to a more
    // resonable state since the exception unwinder is completely unaware of
    // the stack pointer adjustments made in this function.
    asm
      MOV EAX, DWORD PTR [TotalParamBytes]
      ADD ESP, EAX
    end;
    raise;
 end;
{$ENDIF}
end;
 
FSoapDispatcher.DispatchSOAP(Path, SoapAction, XMLStream, RStream, BindingTypeIn);
返回调用后的内存块为。
Response.ContentStream := RStream;
然后再发送给客户端。
到这里,基本上客户端和服务器端都进行了分析。

[1] [2] [3]

编辑 webmaster

发表评论  打印本文  推荐本文  加入收藏  返回顶部  关闭窗口
 
 
 
评论更多>>
 
 
发表
 
姓名: QQ:
性别: MSN:
E-mail: 主页:
评分: 1 2 3 4 5
评论内容:
验证码:
  
  • 请遵守《互联网电子公告服务管理规定》及中华人民共和国其他各项有关法律法规。
  • 严禁发表危害国家安全、损害国家利益、破坏民族团结、破坏国家宗教政策、破坏社会稳定、侮辱、诽谤、教唆、淫秽等内容的评论 。
  • 用户需对自己在使用本站服务过程中的行为承担法律责任(直接或间接导致的)。
  • 本站管理员有权保留或删除评论内容。
  • 评论内容只代表网友个人观点,与本网站立场无关。
  •