您的位置:首页 > 编程语言

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

2008-03-11 15:04 429 查看
 [align=left]
最近阅读了SocketConn的源码和WebService 的源码,把追踪的过程写了下来,方便大家学习。毕竟这需要精力,时间和毅力。感谢煮茶待英雄博志区和三层数据库讨论区兄弟们的支持,特别是julian兄弟,不是他,我可能没耐心继续下去。如果有时间,大家可以继续完善。从socket和Websevice的底层实现细节,我们发现BORLAND的工程师们的构思和实现的过程。我觉得这对我们的学习应该是非常重要的。学会思考。学会读源码,学会分析。[/align][align=left]希望和我交往的朋友可通过QQ或Email联系我。Wu_yanan2003@yahoo.com.cn[/align][align=left]另见:《远程调用技术代码追踪(socket) 》[/align][align=left]关注我的:《远程调用技术代码追踪(第三方控件) 》[/align][align=left] [/align]  远程调用技术内幕有关WebService的相关的知识,我就不说了,我直接分析源码。有问题的地方请参考李维的书。initializationInvRegistry.RegisterInterface(TypeInfo(IMyFirstWS), 'urn:MyFirstWSIntf-IMyFirstWS', 'utf-8');看过李维的分布式架构的应该都知道,WEB服务端对类和接口进行了注册,客户端这里也进行了注册。然后客户端把数据通过HTTP传输到服务器端,服务器端通过拆包,去到注册管理的类中寻找相应的接口,并创建一个相应的对象,把客户端的数据压进去,调用后,把数据再传回来。在调用这句的时候,TinvokableClassRegistry类已经创建了,由于inquire_v1也引用了InvRegistry注册,所以在哪里被引用的时候已经被创建了。function InvRegistry: TInvokableClassRegistry;begin if not Assigned(InvRegistryV) then    InitIR; Result := InvRegistryV;end;初次引用会调用InitIR方法。procedure InitIR;begin InvRegistryV := TInvokableClassRegistry.Create; RemTypeRegistryV := TRemotableClassRegistry.Create; RemClassRegistryV:= RemTypeRegistry; InitBuiltIns; //定们到这一句: InitXSTypes; InitMoreBuiltIns;end; 先看InvRegistryV := TInvokableClassRegistry.Create;,这个类是用来注册,相应的接口及类,并能够根据soap封包内容找到相应的接口及方法。TRemotableClassRegistry       = TRemotableTypeRegistry;所对应的是TremotableTypeRegistry, 这个类主要是对数据类型进行注册。 大致来了解一下这个类。TInvokableClassRegistry = class(TInterfacedObject) private    FLock: TRTLCriticalSection;    FRegClasses: array of InvRegClassEntry;FRegIntfs: array of InvRegIntfEntry;这里可以看到,声明了两个动态数组。分别用来放接口注册,及类注册信息。TCreateInstanceProc = procedure(out obj: TObject);InvRegClassEntry = record    ClassType: TClass;    Proc: TCreateInstanceProc;    URI: string; end;它包含了webservice实现类的指针,以建立实现类的factory函数指针。 InvRegIntfEntry = record    Name: string;                             { Native name of interface    }    ExtName: Widestring;                      { PortTypeName                }    UnitName: string;                         { Filename of interface       }    GUID: TGUID;                              { GUID of interface           }   Info: PTypeInfo;                          { Typeinfo of interface       }    DefImpl: TClass;                          { Metaclass of implementation }    Namespace: Widestring;                    { XML Namespace of type       }    WSDLEncoding: WideString;                 { Encoding                    }    Documentation: string;                    { Description of interface    }    SOAPAction: string;                       { SOAPAction of interface     }    ReturnParamNames: string;                 { Return Parameter names      }    InvokeOptions: TIntfInvokeOptions;        { Invoke Options              }    MethNameMap: array of ExtNameMapItem;             { Renamed methods     }    MethParamNameMap: array of MethParamNameMapItem; { Renamed parameters }    IntfHeaders: array of IntfHeaderItem;      { Headers                    }    IntfExceptions: array of IntfExceptionItem;{ Exceptions                 }    UDDIOperator: String;                      { UDDI Registry of this porttype }    UDDIBindingKey: String;                    { UDDI Binding key           } end; 看到它里面有很多东西,接口名称,单元名,GUID等信息。  procedure InitBuiltIns;begin { DO NOT LOCALIZE } RemClassRegistry.RegisterXSInfo(TypeInfo(System.Boolean), XMLSchemaNameSpace, 'boolean');对于处理结构型数据,需要进行SOAP封包类型的转换开发人员在使用这种自定义数据类型前必须对其进行注册,分别是RegisterXSClass和RegisterXSInfo。前一个方法是注册从Tremotable继承下来的类,后一个不需要是从TremotablXS继承下来的类。 InitBuiltIns;    InitXSTypes; InitMoreBuiltIns;这三个函数类似,都是注册一些基本类型等。看看到底怎么处理的,(这里注册一个BOOLEAN类型)RemClassRegistry.RegisterXSInfo(TypeInfo(System.Boolean), XMLSchemaNameSpace, 'boolean');procedure TRemotableTypeRegistry.RegisterXSInfo(Info: PTypeInfo; const URI: WideString = '';                                                const Name: WideString = '';                                                const ExtName: WideString = ''); …Index := GetEntry(Info, Found, Name);     if Found then      Exit;    if AppNameSpacePrefix <> '' then      AppURI := AppNameSpacePrefix + '-';    if URI = '' then    begin      if Info.Kind = tkDynArray then      begin        UnitName := GetTypeData(Info).DynUnitName;        URIMap[Index].URI := 'urn:' + AppURI + UnitName;      end      else if Info.Kind = tkEnumeration then      begin        UnitName := GetEnumUnitName(Info);        URIMap[Index].URI := 'urn:' + AppURI + UnitName;      end      else if Info.Kind = tkClass then        URIMap[Index].URI := 'urn:' + AppURI + GetTypeData(Info).UnitName      else        URIMap[Index].URI := 'urn:' + AppURI;    end    else      URIMap[Index].URI := URI;    if Name <> '' then      URIMap[Index].Name := Name    else    begin      URIMap[Index].Name := Info.Name;    end;    URIMap[Index].ExtName := ExtName;    URIMap[Index].Info := Info;    if Info.Kind = tkClass then      URIMap[Index].ClassType := GetTypeData(Info).ClassType; finally    UnLock; end;end; 看研究一下GetEntry函数,这里以后多次用到,发现这个函数是TremotableClassRegistry类的,说明实际的注册还是在TremotableClassRegistry这个类完成的。 function TRemotableClassRegistry.GetEntry(Info: PTypeInfo; var Found: Boolean; const Name: WideString): Integer;begin Result := FindEntry(Info, Found, Name); if not Found then    SetLength(URIMap, Result + 1);end;这个函数功能是搜索类型是否已注册,否则,动态数组加1,分配空间进行注册。 看看FindEntry (这里传进来的info是TypeInfo(System.Boolean), name: Boolean)function TRemotableClassRegistry.FindEntry(Info: PTypeInfo; var Found: Boolean; const Name: WideString): Integer;begin Result := 0; Found := False; while Result < Length(URIMap) do begin    if (Info <> nil) and (URIMap[Result].Info = Info) then    begin      if (Name = '') or (URIMap[Result].Name = Name) then      begin        Found := True;        Exit;      end;    end;    Inc(Result); end;end;这个函数的功能是遍历整个动态数组TremRegEntry,利用TypeInfo信息和名字进行搜索,查看是否已进行注册。 看看URIMAP的定义:URIMAP:   array of TRemRegEntry; TObjMultiOptions = (ocDefault, ocMultiRef, ocNoMultiRef); TRemRegEntry = record    ClassType: TClass; //类信息    Info: PtypeInfo;    // typeInfo信息(RTTL)    URI: WideString;   //    Name: WideString; //    ExtName: WideString; //    IsScalar: Boolean;    //    MultiRefOpt: TObjMultiOptions; //    SerializationOpt: TSerializationOptions;    PropNameMap: array of ExtNameMapItem;             { Renamed properties } end;继续RegisterXSInfo函数:这是对动态数组的uri赋值:if AppNameSpacePrefix <> '' then      AppURI := AppNameSpacePrefix + '-';    if URI = '' then    begin      if Info.Kind = tkDynArray then      begin        UnitName := GetTypeData(Info).DynUnitName;        URIMap[Index].URI := 'urn:' + AppURI + UnitName;      end      else if Info.Kind = tkEnumeration then      begin        UnitName := GetEnumUnitName(Info);        URIMap[Index].URI := 'urn:' + AppURI + UnitName;      end      else if Info.Kind = tkClass then        URIMap[Index].URI := 'urn:' + AppURI + GetTypeData(Info).UnitName      else        URIMap[Index].URI := 'urn:' + AppURI;    end    else      URIMap[Index].URI := URI;    if Name <> '' then      URIMap[Index].Name := Name    else    begin      URIMap[Index].Name := Info.Name;end; 这句比较关键:URIMap[Index].Info := Info;把RTTL信息保存在URL动态数组中。 总结一下:一些基本类型,都是通过这种方式,把URI,及INFO信息保存在动态数组中的。为什么要进行登记,因为WEBSERVICE中的数据类型要转换成DELPHI的PAS类型,用URI标记的XML文件,传输之后,根据这张对照表,就可以分配相应的空间。另外这些类型的注册信息是放在:TremRegEntry动态数组中的。和我们自己定义的接口及类是不同的。FRegClasses: array of InvRegClassEntry; FRegIntfs: array of InvRegIntfEntry; 这是注册自己定义接口及类的动态数组。 再来分析:InitBuiltIns函数中的:RemClassRegistry.RegisterXSClass(TSOAPAttachment, XMLSchemaNamespace, 'base64Binary', '', False, ocNoMultiRef);大致和基本类型差不多。procedure TRemotableTypeRegistry.RegisterXSClass(AClass: TClass; const URI: WideString = '';                                                 const Name: WideString = '';                                                 const ExtName: WideString = '';                                                 IsScalar: Boolean = False;                                                 MultiRefOpt: TObjMultiOptions = ocDefault);var Index: Integer; Found: Boolean; AppURI: WideString;begin Lock; try    Index := GetEntry(AClass.ClassInfo, Found, Name);    if not Found then    begin      if AppNameSpacePrefix <> '' then        AppURI := AppNameSpacePrefix + '-';      if URI = '' then        URIMap[Index].URI := 'urn:' + AppURI + GetTypeData(AClass.ClassInfo).UnitName { do not localize }      else        URIMap[Index].URI := URI;      if Name <> '' then        URIMap[Index].Name := Name      else      begin        URIMap[Index].Name := AClass.ClassName;      end;      URIMap[Index].ExtName := ExtName;      URIMap[Index].ClassType := AClass;      URIMap[Index].Info := AClass.ClassInfo;      URIMap[Index].IsScalar := IsScalar;      URIMap[Index].MultiRefOpt := MultiRefOpt;    end; finally    UnLock; end;end;  前面都是说系统类型的注册。下面看看我们自己定义的接口,是如何注册的:procedure TInvokableClassRegistry.RegisterInterface(Info: PTypeInfo; const Namespace: InvString;                    const WSDLEncoding: InvString; const Doc: string; const ExtName: InvString);     for I := 0 to Length(FRegIntfs) - 1 do      if FRegIntfs[I].Info = Info then        Exit; Index := Length(FRegIntfs);SetLength(FRegIntfs, Index + 1); GetIntfMetaData(Info, IntfMD, True);    FRegIntfs[Index].GUID := IntfMD.IID;    FRegIntfs[Index].Info := Info;    FRegIntfs[Index].Name := IntfMD.Name;    FRegIntfs[Index].UnitName := IntfMD.UnitName;    FRegIntfs[Index].Documentation := Doc;    FRegIntfs[Index].ExtName := ExtName;    FRegIntfs[Index].WSDLEncoding := WSDLEncoding;     if AppNameSpacePrefix <> '' then      URIApp := AppNameSpacePrefix + '-';     { Auto-generate a namespace from the filename in which the interface was declared and      the AppNameSpacePrefix }    if Namespace = '' then      FRegIntfs[Index].Namespace := 'urn:' + URIApp + IntfMD.UnitName + '-' + IntfMD.Name    else    begin      FRegIntfs[Index].Namespace := Namespace;      FRegIntfs[Index].InvokeOptions := FRegIntfs[Index].InvokeOptions + [ioHasNamespace];    end;     if FRegIntfs[Index].DefImpl = nil then    begin      { NOTE: First class that implements this interface wins!! }      for I := 0 to Length(FRegClasses) - 1 do      begin         Table := FRegClasses[I].ClassType.GetInterfaceTable;        if (Table = nil) then        begin          Table := FRegClasses[I].ClassType.ClassParent.GetInterfaceTable;        end;        for J := 0 to Table.EntryCount - 1 do        begin          if IsEqualGUID(IntfMD.IID, Table.Entries[J].IID) then          begin            FRegIntfs[Index].DefImpl := FRegClasses[I].ClassType;            Exit;          end;        end;      end;    end; finally    Unlock; end;end; 功能:for I := 0 to Length(FRegIntfs) - 1 do      if FRegIntfs[I].Info = Info then        Exit;遍历FRegIntfs: array of InvRegIntfEntry;数组,根据TypeInfo信息判断该接口是否已注册。Index := Length(FRegIntfs);SetLength(FRegIntfs, Index + 1);新增一个数组元素。GetIntfMetaData(Info, IntfMD, True);//得到接口的RTTL信息,然后动态增加到注册的动态数组中。    FRegIntfs[Index].GUID := IntfMD.IID;    FRegIntfs[Index].Info := Info;    FRegIntfs[Index].Name := IntfMD.Name;    FRegIntfs[Index].UnitName := IntfMD.UnitName;    FRegIntfs[Index].Documentation := Doc;    FRegIntfs[Index].ExtName := ExtName;FRegIntfs[Index].WSDLEncoding := WSDLEncoding; DefImpl里存放的是classType信息:if FRegIntfs[Index].DefImpl = nil then    begin      for I := 0 to Length(FRegClasses) - 1 do      begin         Table := FRegClasses[I].ClassType.GetInterfaceTable;        if (Table = nil) then        begin          Table := FRegClasses[I].ClassType.ClassParent.GetInterfaceTable;        end;        for J := 0 to Table.EntryCount - 1 do        begin          if IsEqualGUID(IntfMD.IID, Table.Entries[J].IID) then          begin            FRegIntfs[Index].DefImpl := FRegClasses[I].ClassType;            Exit;          end;        end;      end;    end;注意这里:FRegClasses: array of InvRegClassEntry;到注册类的动态数组中去搜寻接口的实现类是否注册,如果注册,便把实现类的指针拷贝到DefImpl数据字段。 顺便看一下类是怎么注册的:procedure TInvokableClassRegistry.RegisterInvokableClass(AClass: TClass; CreateProc: TCreateInstanceProc);var Index, I, J: Integer; Table: PInterfaceTable; begin Lock; tryTable := AClass.GetInterfaceTable;     。。。。。。    Index := Length(FRegClasses);    SetLength(FRegClasses, Index + 1);    FRegClasses[Index].ClassType := AClass;    FRegClasses[Index].Proc := CreateProc;     for I := 0 to Table.EntryCount - 1 do    begin      for J := 0 to Length(FRegIntfs) - 1 do        if IsEqualGUID(FRegIntfs[J].GUID, Table.Entries[I].IID) then          if FRegIntfs[J].DefImpl = nil then            FRegIntfs[J].DefImpl := AClass;    end; finally    UnLock; end;end;可以看到和注册接口非常相似。在调用上面方法时,会传入实现类的指针及factory函数指针,调用GetInterfaceTable判断是否实现接口。否则为NIL, 然后在FregClasses增加一元素,把值写入。最后再到FregIntfs是搜寻此实现类的接口是否已经注册。是的话,就把指针储存在FRegIntfs[J].DefImpl中。继续:InvRegistry.RegisterDefaultSOAPAction(TypeInfo(IMyFirstWS), 'urn:MyFirstWSIntf-IMyFirstWS#%operationName%'); procedure TInvokableClassRegistry.RegisterDefaultSOAPAction(Info: PTypeInfo; const DefSOAPAction: InvString);var I: Integer;begin    I := GetIntfIndex(Info);    if I >= 0 then    beginFRegIntfs[I].SOAPAction := DefSOAPAction; //值为:urn:MyFirstWSIntf-IMyFirstWS#%operationName       FRegIntfs[I].InvokeOptions := FRegIntfs[I].InvokeOptions + [ioHasDefaultSOAPAction];      Exit;    end;end; 设置接口的SOAPAction, 及InvokeOptions属性。上面讲了用户接口及自定义类注册的实现。 看看这几句为何如此神奇,竟然可以实现对象的远程调用?MyHTTPRIO := THTTPRIO.Create(nil);MyHTTPRIO.URL :='http://localhost/soap/MyCGI.exe/soap/IMyFirstWS';ShowMessage(( MyHTTPRIO As IMyFirstWS ).GetObj); 研究一下客户端代码:constructor THTTPRIO.Create(AOwner: TComponent);begin inherited Create(AOwner); { Converter }  FDomConverter := GetDefaultConverter; FConverter := FDomConverter as IOPConvert; { WebNode } FHTTPWebNode := GetDefaultWebNode; FWebNode := FHTTPWebNode as IWebNode;end; 继续到父类中TRIO查看相应代码:constructor TRIO.Create(AOwner: TComponent);begin inherited Create(AOwner); FInterfaceBound := False; FContext := TInvContext.Create;  FSOAPHeaders := TSOAPHeaders.Create(Self); FHeadersInbound := THeaderList.Create; FHeadersOutBound:= THeaderList.Create; FHeadersOutbound.OwnsObjects := False; (FSOAPHeaders as IHeadersSetter).SetHeadersInOut(FHeadersInbound, FHeadersOutBound);end; 创建了TinvContext,这个对象是用来创建一个和服务器端一样的调用环境。客户端的参数信息一个个的填入这个环境中。创建一个TSOAPHeaders头对象。 回到constructor THTTPRIO.Create(AOwner: TComponent);begin inherited Create(AOwner); { Converter } FDomConverter := GetDefaultConverter; FConverter := FDomConverter as IOPConvert; { WebNode } FHTTPWebNode := GetDefaultWebNode; FWebNode := FHTTPWebNode as IWebNode;end; function THTTPRIO.GetDefaultConverter: TOPToSoapDomConvert;begin if (FDefaultConverter = nil) then begin    FDefaultConverter := TOPToSoapDomConvert.Create(Self);    FDefaultConverter.Name := 'Converter1';                 { do not localize }    FDefaultConverter.SetSubComponent(True); end; Result := FDefaultConverter;end;而TOPToSoapDomConvert可以把Object Pascal的呼叫和參數自動轉換為SOAP封裝的格式資訊,再藉由THTTPReqResp傳送HTTP封包。 function THTTPRIO.GetDefaultWebNode: THTTPReqResp;begin if (FDefaultWebNode = nil) then begin    FDefaultWebNode := THTTPReqResp.Create(Self);    FDefaultWebNode.Name := 'HTTPWebNode1';                { do not localize }    FDefaultWebNode.SetSubComponent(True); end; Result := FDefaultWebNode;end;//用来传送HTTP的封包。  function THTTPRIO.GetDefaultConverter: TOPToSoapDomConvert;begin if (FDefaultConverter = nil) then begin    FDefaultConverter := TOPToSoapDomConvert.Create(Self);    FDefaultConverter.Name := 'Converter1';                 { do not localize }    FDefaultConverter.SetSubComponent(True); end; Result := FDefaultConverter;end;  FHTTPWebNode := GetDefaultWebNode;
function THTTPRIO.GetDefaultWebNode: THTTPReqResp;begin if (FDefaultWebNode = nil) then begin    FDefaultWebNode := THTTPReqResp.Create(Self);    FDefaultWebNode.Name := 'HTTPWebNode1';                { do not localize }    FDefaultWebNode.SetSubComponent(True); end; Result := FDefaultWebNode;end; 创建了一个THTTPReqResp,用于HTTP通信。MyHTTPRIO.URL :='http://localhost/soap/MyCGI.exe/soap/IMyFirstWS';procedure THTTPRIO.SetURL(Value: string);begin if Assigned(FHTTPWebNode) then begin    FHTTPWebNode.URL := Value;    if Value <> '' then    begin      WSDLLocation := '';      ClearDependentWSDLView;    end; end;end; procedure THTTPReqResp.SetURL(const Value: string);begin if Value <> '' then    FUserSetURL := True  else    FUserSetURL := False; InitURL(Value); Connect(False);end; procedure THTTPReqResp.InitURL(const Value: string);     InternetCrackUrl(P, 0, 0, URLComp);    FURLScheme := URLComp.nScheme;    FURLPort := URLComp.nPort;    FURLHost := Copy(Value, URLComp.lpszHostName - P + 1, URLComp.dwHostNameLength); FURL := Value;end;设置THTTPReqResp的属性。和HTTP服务器通信。 procedure THTTPReqResp.Connect(Value: Boolean);if Assigned(FInetConnect) then      InternetCloseHandle(FInetConnect);    FInetConnect := nil;    if Assigned(FInetRoot) then      InternetCloseHandle(FInetRoot);    FInetRoot := nil;FConnected := False;Value 为FLASE。  ShowMessage(( MyHTTPRIO As IMyFirstWS ).GetObj);利用AS转换成webservice的接口。用转换后的接口到客户端的InvRegInftEntry表格中搜寻WEBSERVICE服务接口,根据RTTL生成SOAP封包。 procedure _IntfCast(var Dest: IInterface; const Source: IInterface; const IID: TGUID);先看这一句:CALL    DWORD PTR [EAX] + VMTOFFSET IInterface.QueryInterface function THTTPRIO.QueryInterface(const IID: TGUID; out Obj): HResult;var UDDIOperator, UDDIBindingKey: string;begin Result := inherited QueryInterface(IID, Obj); if Result = 0 then begin    if IsEqualGUID(IID, FIID) then    begin      FHTTPWebNode.SoapAction := InvRegistry.GetActionURIOfIID(IID);      if InvRegistry.GetUDDIInfo(IID, UDDIOperator, UDDIBindingKey) then      begin        FHTTPWebNode.UDDIOperator := UDDIOperator;        FHTTPWebNode.UDDIBindingKey := UDDIBindingKey;      end;    end; end;end; Result := inherited QueryInterface(IID, Obj);//跟踪一下这一句:这句比较重要,要重点分析。这里创建了虚拟表格。 function TRIO.QueryInterface(const IID: TGUID; out Obj): HResult;begin Result := E_NOINTERFACE; { IInterface, IRIOAccess } //判断接口是不是IRIOAccess类型 if IsEqualGUID(IID, IInterface) or IsEqualGUID(IID, IRIOAccess) then { ISOAPHeaders }//判断接口是不是ISOAPHeaders类型 if IsEqualGUID(IID, ISOAPHeaders) then…    if GenVTable(IID) then    begin      Result := 0;      FInterfaceBound := True;      Pointer(Obj) := IntfTableP;      InterlockedIncrement(FRefCount);    end; 看看GenVTable函数:function TRIO.GenVTable(const IID: TGUID): Boolean;Info := InvRegistry.GetInterfaceTypeInfo(IID);这个函数是去到TinvokableClassRegistry中搜寻该接口是否注册,注册过的接口则返回typeinfo信息赋给指针。function TInvokableClassRegistry.GetInterfaceTypeInfo(const AGUID: TGUID): Pointer;var I: Integer;begin Result := nil; Lock; try    for I := 0 to Length(FRegIntfs) - 1 do    begin      if IsEqualGUID(AGUID, FRegIntfs[I].GUID) then      begin        Result := FRegIntfs[I].Info;        Exit;      end;    end; finally    UnLock; end;end; 继续:通过infotype得到RTTL信息。 try    GetIntfMetaData(Info, IntfMD, True); except    HasRTTI := False;    Exit; end; { TProc = procedure of object; TObjFunc = function: Integer of Object; stdcall; TQIFunc = function(const IID: TGUID; out Obj): HResult of object; stdcall; PProc = ^TProc;TCracker = record    case integer of      0: (Fn: TProc);      1: (Ptr: Pointer);      2: (ObjFn: TObjFunc);      3: (QIFn: TQIFunc);    end;} Crack.Fn := GenericStub; StubAddr := Crack.Ptr; 地址指向函数TRIO.GenericStub函数。Crack.Fn结构的指针指向这段代码的意思是用C/stdcall等方式调用函数。从左到右,从右到左压入堆栈。调整TRIO.IntfTable的指针,最后调用TRIO.Genericprocedure TRIO.GenericStub;asm        POP     EAX { Return address in runtime generated stub }        POP     EDX { Is there a pointer to return structure on stack and which CC is used? }        CMP     EDX, 2        JZ      @@RETONSTACKRL         CMP     EDX, 1        JZ      @@RETONSTACKLR        POP     EDX           { Method # pushed by stub }        PUSH    EAX           { Push back return address }        LEA     ECX, [ESP+12] { Calc stack pointer to start of params }        MOV     EAX, [ESP+8] { Calc interface instance ptr }        JMP     @@CONT@@RETONSTACKLR:        POP     EDX           { Method # pushed by stub   }       PUSH    EAX           { Push back return address }        LEA     ECX, [ESP+12] { Calc stack pointer to start of params }        MOV     EAX, [ESP+8] { Calc interface instance ptr }        JMP     @@CONT@@RETONSTACKRL:        POP     EDX           { Method # pushed by stub }        PUSH    EAX           { Push back return address }        LEA     ECX, [ESP+8] { Calc stack pointer to start of params }        MOV     EAX, [ESP+12] { calc interface instance ptr }@@CONT:        SUB     EAX, OFFSET TRIO.IntfTable; { Adjust intf pointer to object pointer }        JMP     TRIO.Genericend;   Crack.Fn := ErrorEntry; ErrorStubAddr := Crack.Ptr; //首先分配vtable空间,接口数加3, 因为有Iunknown接口。 GetMem(IntfTable, (Length(IntfMD.MDA) + NumEntriesInIInterface) * 4); IntfTableP := @IntfTable; 然后把地址赋给IntfTableP变量  GetMem(IntfStubs, (Length( IntfMD.MDA) + NumEntriesInIInterface) * StubSize ); 分配存根接口空间。 这是解释 IntfTable: Pointer;             { Generated vtable for the object   }     IntfTableP: Pointer;            { Pointer to the generated vtable   }    IntfStubs: Pointer;             { Pointer to generated vtable thunks} //Load the IUnknown vtable 分配指针,加入三个接口Iunknown VTable := PPointer(IntfTable); Crack.QIFn := _QIFromIntf; QI查询指针赋值给 Crack结构体 VTable^ := Crack.Ptr; 赋给VT指针 IncPtr(VTable, 4);    增加一个指针。  Crack.ObjFn := _AddRefFromIntf; VTable^ := Crack.Ptr; IncPtr(VTable, 4); Crack.ObjFn := _ReleaseFromIntf; VTable^ := Crack.Ptr; IncPtr(VTable, 4);    VTable := AddPtr(IntfTable, NumEntriesInIInterface * 4);//增加IunKnown指针的三个方法。压入IntfTable中。 Thunk := AddPtr(IntfStubs, NumEntriesInIInterface * StubSize); //调整Thunk,加入IunKnown接口方法。 //遍历所有方法:产生机器相应的汇编机器代码。 for I := NumEntriesInIInterface to Length(IntfMD.MDA) - 1 do begin    CallStubIdx := 0;     if not IntfMD.MDA[I].HasRTTI then    begin      GenByte($FF); { FF15xxxxxxxx Call [mem]    }      GenByte($15);      Crack.Fn := ErrorEntry;      GenDWORD(LongWord(@ErrorStubAddr));    end else    begin      { PUSH the method ID }      GenPushI(I);  //定位这里:看看函数做了什么:CallStub: array[0..StubSize-1] of Byte;I=3。CallStubIdx=2procedure TRIO.GenPushI(I: Integer);begin if I < 128 then begin    CallStub[CallStubIdx] := $6A;    CallStub[CallStubIdx + 1] := I;    Inc(CallStubIdx, 2); end else begin    CallStub[CallStubIdx] := $68;    PInteger(@CallStub[CallStubIdx + 1])^ := I;    Inc(CallStubIdx, 5); end;end;登记函数调用信息, 数组增加一元素。 遍历接口信息,函数ID号压入堆栈中。       { PUSH the info about return value location }      if RetOnStack(IntfMD.MDA[I].ResultInfo) then      begin        if IntfMD.MDA[I].CC in [ccStdcall, ccCdecl] then          GenPushI(2)        else          GenPushI(1);      end      else        GenPushI(0);把返回值压入堆栈中。//把返回参数压入堆栈。     接着把GenericStub压入堆栈中。      { Generate the CALL [mem] to the generic stub }      GenByte($FF); { FF15xxxxxxxx Call [mem] }      GenByte($15);GenDWORD(LongWord(@StubAddr)); 这几句是生成汇编的代码。可以产生这样的调用:ff15xxxxxx:地址: caa [mem]编号: //这里调用的。//看看里面的内容是什么:       { Generate the return sequence }      if IntfMD.MDA[I].CC in [ccCdecl] then      begin        { For cdecl calling convention, the caller will do the cleanup, so }        { we convert to a regular ret. }        GenRet;      end      else      begin                BytesPushed := 0;        for J := 0 to IntfMD.MDA[I].ParamCount - 1 do        begin           if IsParamByRef(IntfMD.MDA[I].Params[J].Flags, IntfMD.MDA[I].Params[J].Info, IntfMD.MDA[I].CC) then             Inc(BytesPushed, 4)           elseInc(BytesPushed, GetStackTypeSize(IntfMD.MDA[I].Params[J].Info, IntfMD.MDA[I].CC ));//每个参数分配空间。        end;          Inc(BytesPushed, GetStackTypeSize(IntfMD.MDA[I].SelfInfo, IntfMD.MDA[I].CC ));//压入函数本身信息:         { TODO: Investigate why not always 4 ?? }        if RetOnStack(IntfMD.MDA[I].ResultInfo) or (IntfMD.MDA[I].CC = ccSafeCall) then          Inc(BytesPushed, 4);         if BytesPushed > 252 then          raise Exception.CreateFmt(STooManyParameters, [IntfMD.MDA[I].Name]);         GenRET(BytesPushed);      end;end; //GenRET(BytesPushed); 分配函数参数空间。    { Copy as much of the stub that we initialized over to the }    { block of memory we allocated. }    P := PByte(Thunk);    for J := 0 to CallStubIdx - 1 do    begin      P^ := CallStub[J];      IncPtr(P);    end;Thunk的指针,指向汇编代码相应的调用信息:     { And then fill the remainder with INT 3 instructions for             }    { cleanliness and safety. If we do the allocated more smartly, we    }    { can remove all the wasted space, except for maybe alignment.        }    for J := CallStubIdx to StubSize - 1 do    begin      P^ := $CC;      IncPtr(P);    end;增加Thunk指向存根相应调用信息:     { Finally, put the new thunk entry into the vtable slot. }    VTable^ := Thunk;IncPtr(VTable, 4);把thunk指针赋给vtable之后,压入堆栈。IncPtr(Thunk, StubSize);把存根相应调用信息压入堆栈。 然后继续下一个函数的相应操作。 end;end; procedure IncPtr(var P; I: Integer = 1);asm        ADD     [EAX], EDXend; 总结一下GenVTable函数,这个函数,根据注册的接口,生成了内存表格。首先遍历整个动态数组,然后,得到接口的RTTL信息,随后把Tcracker结构内存入相应的调用信息。然后再分配两块内存,一块放接口信息,一块放存根调用信息,再把接口内存的指针赋给TRIO的IntfTableP变量。IntfStubs存放存根指针IntfTable指接口信息后,又加入了Iunknown的指针空间。最近遍历接口函数,把函数信息写入CallStub数组之后(生成机器代码),再填入堆栈之中。继续:THTTPRIO.QueryInterfaceTInvokableClassRegistry.GetActionURIOfInfoif InvRegistry.GetUDDIInfo(IID, UDDIOperator, UDDIBindingKey) then调用之后:function TInvokableClassRegistry.GetUDDIInfo(const IntfInfo: PTypeInfo; var Operator, BindingKey: string): Boolean; 返回procedure _IntfCast(var Dest: IInterface; const Source: IInterface; const IID: TGUID);这里,继续:procedure TRIO.GenericStub;JMP     TRIO.Generic   //这里是最重要的地方:这个函数完成了。打包,传递,并返回服务器端结果。我们仔细研究一下。 function TRIO.Generic(CallID: Integer; Params: Pointer): Int64;。。。。MethMD := IntfMD.MDA[CallID]; //得到方法相应的属性。FContext.SetMethodInfo(MethMD); // FContext 产生虚拟的表函数表格。 procedure TInvContext.SetMethodInfo(const MD: TIntfMethEntry);begin SetLength(DataP, MD.ParamCount + 1); SetLength(Data, (MD.ParamCount + 1) * MAXINLINESIZE);end; if MethMd.CC <> ccSafeCall then begin    if RetOnStack(MethMD.ResultInfo) then    begin      RetP := Pointer(PInteger(P)^);      if MethMD.ResultInfo.Kind = tkVariant then        IncPtr(P, sizeof(Pointer))      else        IncPtr(P, GetStackTypeSize(MethMD.ResultInfo, MethMD.CC));      if MethMD.CC in [ccCdecl, ccStdCall] then      begin        IncPtr(P, sizeof(Pointer));   { Step over self }      end;    end else      RetP := @Result;    FContext.SetResultPointer(RetP); end;//把相应的返回信息压入Fcontext中。 for J := 0 to MethMD.ParamCount - 1 do begin    FContext.SetParamPointer(ParamIdx, P);    with MethMD.Params[J] do    begin      if (Info.Kind = tkVariant) and         (MethMD.CC in [ccCdecl, ccStdCall, ccSafeCall]) and         not (pfVar in Flags) and         not (pfOut in Flags) then      begin        IncPtr(P, sizeof(TVarData)); { NOTE: better would be to dword-align!! }      end      else if IsParamByRef(Flags, Info, MethMD.CC) then        IncPtr(P, 4)      else        IncPtr(P, GetStackTypeSize(Info, MethMD.CC));    end;    Inc(ParamIdx, LeftRightOrder); end;//把相应的参数压入Fcontext中。//转换成XML封包,并写入流中,这里就是具体打包的地方:大家看清楚了: Req := FConverter.InvContextToMsg(IntfMD, MethNum, FContext, FHeadersOutBound);现在来好好研究一下它是怎么转换成XML封包的。function TOPToSoapDomConvert.InvContextToMsg(const IntfMD: TIntfMetaData; MethNum: Integer;                                            Con: TInvContext; Headers: THeaderList): TStream; MethMD := IntfMD.MDA[MethNum];首先得到方法的动态信息。XMLDoc := NewXMLDocument; 看看这句: function TOPToSoapDomConvert.NewXMLDocument: IXMLDocument;begin Result := XMLDoc.NewXMLDocument; Result.Options := Result.Options + [doNodeAutoIndent]; Result.ParseOptions := Result.ParseOptions + [poPreserveWhiteSpace];end; function NewXMLDocument(Version: DOMString = '1.0'): IXMLDocument;begin Result := TXMLDocument.Create(nil); Result.Active := True; if Version <> '' then    Result.Version := Version;end;创建了一个TXMLDocument对象用于读写XML。 procedure TXMLDocument.SetActive(const Value: Boolean);begin 。。。。      CheckDOM;       FDOMDocument := DOMImplementation.createDocument('', '', nil);      try        LoadData;      except        ReleaseDoc(False);        raise;      end;      DoAfterOpen;    end    else    begin      DoBeforeClose;      ReleaseDoc;      DoAfterClose;    end; end;end; procedure TXMLDocument.CheckDOM;begin if not Assigned(FDOMImplementation) then    if Assigned(FDOMVendor) then      FDOMImplementation := FDOMVendor.DOMImplementation    else      FDOMImplementation := GetDOM(DefaultDOMVendor);end;在TXMLDocument内部使用了Abstract Factory模式Abstract Factory希望不用指定具体的类,但为了找到它们,在TXMLDocument是通过指定一个字符串,也就是我们点击DOMVendor时出现的哪几个字符串. GetDOM函数如下:Result := GetDOMVendor(VendorDesc).DOMImplementation; //根据传递进去的名字,创建相应在的实例:function GetDOMVendor(VendorDesc: string): TDOMVendor;begin if VendorDesc = '' then    VendorDesc := DefaultDOMVendor; if (VendorDesc = '') and (DOMVendorList.Count > 0) then    Result := DOMVendorList[0] else    Result := DOMVendorList.Find(VendorDesc); if not Assigned(Result) then   raise Exception.CreateFmt(SNoMatchingDOMVendor, [VendorDesc]);end; 最后取得一个IDOMImplementation,它有一个createDocument(….):IDOMDocument;函数,这个函数将返回一个IDOMDocument;接口让IXMLDoucment使用。//由此可见,默认状态下是创建DOM,微软的XML解析器。function DOMVendorList: TDOMVendorList;begin if not Assigned(DOMVendors) then    DOMVendors := TDOMVendorList.Create; Result := DOMVendors;end;function TDOMVendorList.GetVendors(Index: Integer): TDOMVendor;begin Result := FVendors[Index];end;如果为空,就返回默认的。function TMSDOMImplementationFactory.DOMImplementation: IDOMImplementation;begin Result := TMSDOMImplementation.Create(nil);end; 再返回到函数:procedure TXMLDocument.SetActive(const Value: Boolean);  FDOMDocument := DOMImplementation.createDocument('', '', nil);继续:function TMSDOMImplementation.createDocument(const namespaceURI, qualifiedName: DOMString; doctype: IDOMDocumentType): IDOMDocument;begin Result := TMSDOMDocument.Create(MSXMLDOMDocumentCreate);end; 在如果使用MSXML,接口对应的是TMSDOMDocument,TMSDOMDocument是实际上是调用MSXML技术,下面是调用MS COM的代码 function CreateDOMDocument: IXMLDOMDocument;begin Result := TryObjectCreate([CLASS_DOMDocument40, CLASS_DOMDocument30,    CLASS_DOMDocument26, msxml.CLASS_DOMDocument]) as IXMLDOMDocument; if not Assigned(Result) then    raise DOMException.Create(SMSDOMNotInstalled);end; 再返回到函数:procedure TXMLDocument.SetActive(const Value: Boolean);..LoadData //因为是新建的TXMLDocument,所以装内空数据,立即返回。procedure TXMLDocument.LoadData;const UnicodeEncodings: array[0..2] of string = ('UTF-16', 'UCS-2', 'UNICODE');var Status: Boolean; ParseError: IDOMParseError; StringStream: TStringStream; Msg: string;begin …Status := True; { No load, just create empty doc. }创建空的文档:  if not Status then begin    DocSource := xdsNone;    ParseError := DOMDocument as IDOMParseError;    with ParseError do      Msg := Format('%s%s%s: %d%s%s', [Reason, SLineBreak, SLine,        Line, SLineBreak, Copy(SrcText, 1, 40)]);    raise EDOMParseError.Create(ParseError, Msg); end; SetModified(False);end;设置不能修改。因为空文档。 继续返回到function NewXMLDocument(Version: DOMString = '1.0'): IXMLDocument;begin if Version <> '' then    Result.Version := Version;end;procedure TXMLDocument.SetVersion(const Value: DOMString);begin SetPrologValue(Value, xpVersion);end;procedure TXMLDocument.SetPrologValue(const Value: Variant;….    PrologNode := GetPrologNode;    PrologAttrs := InternalSetPrologValue(PrologNode, Value, PrologItem);    NewPrologNode := CreateNode('xml', ntProcessingInstr, PrologAttrs);    if Assigned(PrologNode) then      Node.ChildNodes.ReplaceNode(PrologNode, NewPrologNode)    else      ChildNodes.Insert(0, NewPrologNode); end;  NewPrologNode := CreateNode('xml', ntProcessingInstr, PrologAttrs);这句调用了:function TXMLDocument.CreateNode(const NameOrData: DOMString; NodeType: TNodeType = ntElement; const AddlData: DOMString = ''): IXMLNode;begin Result := TXMLNode.Create(CreateDOMNode(FDOMDocument, NameOrData,    NodeType, AddlData), nil, Self);end;  在返回到这个函数中:function TOPToSoapDomConvert.InvContextToMsg(const IntfMD: TIntfMetaData; MethNum: Integer;                                             Con: TInvContext; Headers: THeaderList): TStream;BodyNode := Envelope.MakeBody(EnvNode); if not (soLiteralParams in Options) then begin    SoapMethNS := GetSoapNS(IntfMD);    ExtMethName := InvRegistry.GetMethExternalName(IntfMD.Info, MethMD.Name); ;;;;; //创建一个SOAP的body:function TSoapEnvelope.MakeBody(ParentNode: IXMLNode): IXMLNode;begin   Result := ParentNode.AddChild(SSoapNameSpacePre + ':' + SSoapBody, SSoapNameSpace);end;  SoapMethNS := GetSoapNS(IntfMD); 返回:'urn:MyFirstWSIntf-IMyFirstWS'ExtMethName := InvRegistry.GetMethExternalName(IntfMD.Info, MethMD.Name);得到调用方法名。剩下的部分就是把参数打包。生成SOAP的源文件。然后写到内存流中。  再回到函数中:InvContextToMsg Result := TMemoryStream.Create(); DOMToStream(XMLDoc, Result);把内存块的数据,转化成XML。具体的函数如下:procedure TOPToSoapDomConvert.DOMToStream(const XMLDoc: IXMLDocument; Stream: TStream);var XMLWString: WideString; StrStr: TStringStream;begin    if (FEncoding = '') or (soUTF8EncodeXML in Options) then begin    XMLDoc.SaveToXML(XMLWString);    StrStr := TStringStream.Create(UTF8Encode(XMLWString));    try      Stream.CopyFrom(StrStr, 0);    finally      StrStr.Free;    end; end else    XMLDoc.SaveToStream(Stream);end;我们跟踪之后StrStr的结果如下: ''#$D#$A''#$D#$A' '#$D#$A'    '#$D#$A'      3'#$D#$A'      4[/b]'#$D#$A'    '#$D#$A' '#$D#$A''#$D#$A  转化后继续调用Generic函数:。。。。FWebNode.BeforeExecute(IntfMD, MethMD, MethNum-3, nil); if (BindingType = btMIME) thenbegin。。。FWebNode.BeforeExecute(IntfMD, MethMD, MethNum-3, nil); THTTPReqResp.BeforeExecute。。。。。MethName := InvRegistry.GetMethExternalName(IntfMD.Info, MethMD.Name);FSoapAction := InvRegistry.GetActionURIOfInfo(IntfMD.Info, MethName, MethodIndex);得到方法名和FsoapActionFBindingType := btSOAP DoBeforeExecute // TRIO.if Assigned(FOnBeforeExecute) then退出: 继续:Resp := GetResponseStream(RespBindingType);   继续返回到TRIO.Generic函数中执行:try   FWebNode.Execute(Req, Resp);比较重要的部分: 这个函数就是THTTPReqResp向IIS发出请求。并返回信息: procedure THTTPReqResp.Execute(const Request: TStream; Response: TStream);begin …    Context := Send(Request);    try      try        Receive(Context, Response);        Exit;      except        on Ex: ESOAPHTTPException do        begin          Connect(False);          if not CanRetry or not IsErrorStatusCode(Ex.StatusCode) then            raise;          { Trigger UDDI Lookup }          LookUpUDDI := True;          PrevError := Ex.Message;        end;        else        begin          Connect(False);          raise;        end;      end;    finally      if Context <> 0 then        InternetCloseHandle(Pointer(Context));    end; end;{$ENDIF}end; 现在看看Send函数,看看到底如何发送数据给WEB服务器的。function THTTPReqResp.Send(const ASrc: TStream): Integer;var Request: HINTERNET; RetVal, Flags: DWord; P: Pointer; ActionHeader: string; ContentHeader: string; BuffSize, Len: Integer; INBuffer: INTERNET_BUFFERS; Buffer: TMemoryStream; StrStr: TStringStream;begin { Connect } Connect(True);  Flags := INTERNET_FLAG_KEEP_CONNECTION or INTERNET_FLAG_NO_CACHE_WRITE; if FURLScheme = INTERNET_SCHEME_HTTPS then begin    Flags := Flags or INTERNET_FLAG_SECURE;    if (soIgnoreInvalidCerts in InvokeOptions) then      Flags := Flags or (INTERNET_FLAG_IGNORE_CERT_CN_INVALID or                         INTERNET_FLAG_IGNORE_CERT_DATE_INVALID); end;  Request := nil; try    Request := HttpOpenRequest(FInetConnect, 'POST', PChar(FURLSite), nil,                               nil, nil, Flags, 0{Integer(Self)});    Check(not Assigned(Request));     { Timeouts }    if FConnectTimeout > 0 then      Check(InternetSetOption(Request, INTERNET_OPTION_CONNECT_TIMEOUT, Pointer(@FConnectTimeout), SizeOf(FConnectTimeout)));    if FSendTimeout > 0 then      Check(InternetSetOption(Request, INTERNET_OPTION_SEND_TIMEOUT, Pointer(@FSendTimeout), SizeOf(FSendTimeout)));    if FReceiveTimeout > 0 then      Check(InternetSetOption(Request, INTERNET_OPTION_RECEIVE_TIMEOUT, Pointer(@FReceiveTimeout), SizeOf(FReceiveTimeout)));     { Setup packet based on Content-Type/Binding }    if FBindingType = btMIME then    begin      ContentHeader := Format(ContentHeaderMIME, [FMimeBoundary]);      ContentHeader := Format(ContentTypeTemplate, [ContentHeader]);      HttpAddRequestHeaders(Request, PChar(MIMEVersion), Length(MIMEVersion), HTTP_ADDREQ_FLAG_ADD);       { SOAPAction header }      { NOTE: It's not really clear whether this should be sent in the case              of MIME Binding. Investigate interoperability ?? }      if not (soNoSOAPActionHeader in FInvokeOptions) then      begin        ActionHeader:= GetSOAPActionHeader;        HttpAddRequestHeaders(Request, PChar(ActionHeader), Length(ActionHeader), HTTP_ADDREQ_FLAG_ADD);      end;     end else { Assume btSOAP }    begin      { SOAPAction header }      if not (soNoSOAPActionHeader in FInvokeOptions) then      begin        ActionHeader:= GetSOAPActionHeader;        HttpAddRequestHeaders(Request, PChar(ActionHeader), Length(ActionHeader), HTTP_ADDREQ_FLAG_ADD);      end;       if UseUTF8InHeader then        ContentHeader := Format(ContentTypeTemplate, [ContentTypeUTF8])      else        ContentHeader := Format(ContentTypeTemplate, [ContentTypeNoUTF8]);    end;     { Content-Type }    HttpAddRequestHeaders(Request, PChar(ContentHeader), Length(ContentHeader), HTTP_ADDREQ_FLAG_ADD);     { Before we pump data, see if user wants to handle something - like set Basic-Auth data?? }    if Assigned(FOnBeforePost) then      FOnBeforePost(Self, Request);     ASrc.Position := 0;    BuffSize := ASrc.Size;    if BuffSize > FMaxSinglePostSize then    begin      Buffer := TMemoryStream.Create;      try        Buffer.SetSize(FMaxSinglePostSize);         { Init Input Buffer }        INBuffer.dwStructSize := SizeOf(INBuffer);        INBuffer.Next := nil;        INBuffer.lpcszHeader := nil;        INBuffer.dwHeadersLength := 0;        INBuffer.dwHeadersTotal := 0;        INBuffer.lpvBuffer := nil;        INBuffer.dwBufferLength := 0;        INBuffer.dwBufferTotal := BuffSize;        INBuffer.dwOffsetLow := 0;        INBuffer.dwOffsetHigh := 0;         { Start POST }        Check(not HttpSendRequestEx(Request, @INBuffer, nil,                                    HSR_INITIATE or HSR_SYNC, 0));        try          while True do          begin            { Calc length of data to send }            Len := BuffSize - ASrc.Position;            if Len > FMaxSinglePostSize then              Len := FMaxSinglePostSize;            { Bail out if zip.. }            if Len = 0 then              break;            { Read data in buffer and write out}            Len := ASrc.Read(Buffer.Memory^, Len);            if Len = 0 then              raise ESOAPHTTPException.Create(SInvalidHTTPRequest);             Check(not InternetWriteFile(Request, @Buffer.Memory^, Len, RetVal));             RetVal := InternetErrorDlg(GetDesktopWindow(), Request, GetLastError,              FLAGS_ERROR_UI_FILTER_FOR_ERRORS or FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS or              FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, P);            case RetVal of              ERROR_SUCCESS: ;              ERROR_CANCELLED: SysUtils.Abort;              ERROR_INTERNET_FORCE_RETRY: {Retry the operation};            end;             { Posting Data Event }            if Assigned(FOnPostingData) then              FOnPostingData(ASrc.Position, BuffSize);          end;        finally          Check(not HttpEndRequest(Request, nil, 0, 0));        end;      finally        Buffer.Free;      end;    end else    begin      StrStr := TStringStream.Create('');      try        StrStr.CopyFrom(ASrc, 0);        while True do        begin          Check(not HttpSendRequest(Request, nil, 0, @StrStr.DataString[1], Length(StrStr.DataString)));          RetVal := InternetErrorDlg(GetDesktopWindow(), Request, GetLastError,            FLAGS_ERROR_UI_FILTER_FOR_ERRORS or FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS or            FLAGS_ERROR_UI_FLAGS_GENERATE_DATA, P);          case RetVal of            ERROR_SUCCESS: break;            ERROR_CANCELLED: SysUtils.Abort;            ERROR_INTERNET_FORCE_RETRY: {Retry the operation};          end;        end;      finally        StrStr.Free;      end;    end; except    if (Request <> nil) then      InternetCloseHandle(Request);    Connect(False);    raise; end; Result := Integer(Request);end;   function THTTPReqResp.Send(const ASrc: TStream): Integer;先调用了:procedure THTTPReqResp.Connect(Value: Boolean);……if InternetAttemptConnect(0) <> ERROR_SUCCESS then      SysUtils.Abort;这个函数可以说非常简单,只是尝试计算机连接到网络。 FInetRoot := InternetOpen(PChar(FAgent), AccessType, PChar(FProxy), PChar(FProxyByPass), 0);创建HINTERNET句柄,并初始化WinInet的API函数: Check(not Assigned(FInetRoot));    try      FInetConnect := InternetConnect(FInetRoot, PChar(FURLHost), FURLPort, PChar(FUserName),        PChar(FPassword), INTERNET_SERVICE_HTTP, 0, Cardinal(Self));    //创建一个特定的会话:      Check(not Assigned(FInetConnect));      FConnected := True;    except      InternetCloseHandle(FInetRoot);      FInetRoot := nil;      raise;    end;这里已经创建了一个会话:继续返回function THTTPReqResp.Send(const ASrc: TStream): Integer;函数之中:。。。。Request := HttpOpenRequest(FInetConnect, 'POST', PChar(FURLSite), nil,                               nil, nil, Flags, 0{Integer(Self)});Check(not Assigned(Request));。打开一个HTTP的请求。向WEB服务器提出请求:。。if not (soNoSOAPActionHeader in FInvokeOptions) then      begin        ActionHeader:= GetSOAPActionHeader;        HttpAddRequestHeaders(Request, PChar(ActionHeader), Length(ActionHeader), HTTP_ADDREQ_FLAG_ADD);end;。。。为请求添加一个或多个标头。可以看到标点的信息为:'SOAPAction: "urn:MyFirstWSIntf-IMyFirstWS#GetObj"'  HttpAddRequestHeaders(Request, PChar(ContentHeader), Length(ContentHeader), HTTP_ADDREQ_FLAG_ADD);继续加入标头'Content-Type: text/xml'信息:       StrStr := TStringStream.Create('');      try        StrStr.CopyFrom(ASrc, 0);        while True do        begin          Check(not HttpSendRequest(Request, nil, 0, @StrStr.DataString[1], Length(StrStr.DataString)));建立到internet 的连接,并将请求发送到指定的站点。这句执行完后的图如下(用工具跟踪的结果): 看看前面的soap生成的字符 StrStr的结果如下,发现后半部分是一样的。 继续function THTTPReqResp.Execute(const Request: TStream): TStream;Receive(Context, Response);  procedure THTTPReqResp.Receive(Context: Integer; Resp: TStream; IsGet: Boolean);var Size, Downloaded, Status, Len, Index: DWord; S: string;begin  ..//获取请求信息: HttpQueryInfo(Pointer(Context), HTTP_QUERY_CONTENT_TYPE, @FContentType[1], Size, Index);  repeat    Check(not InternetQueryDataAvailable(Pointer(Context), Size, 0, 0));    if Size > 0 then    begin      SetLength(S, Size);Check(not InternetReadFile(Pointer(Context), @S[1], Size, Downloaded));//下载数据:      Resp.Write(S[1], Size);       { Receiving Data event }      if Assigned(FOnReceivingData) then        FOnReceivingData(Size, Downloaded)    end; until Size = 0; S的结果如下和刚才跟踪器里的是一模一样的:''#$D#$A''#$D#$A' '#$D#$A'    '#$D#$A'      12'#$D#$A'    '#$D#$A' '#$D#$A''#$D#$A 最后关闭HTTP会话句柄: InternetCloseHandle(Pointer(Context)); 在返回function TRIO.Generic(CallID: Integer; Params: Pointer): Int64;函数中继续查看: RespXML := Resp;返回信息的内存流FConverter.ProcessResponse(RespXML, IntfMD, MethMD, FContext, FHeadersInbound); 再次把SOAP封包转换成PASCEL调用:procedure TOPToSoapDomConvert.ProcessResponse(const Resp: TStream;                                              const IntfMD: TIntfMetaData;                                              const MD: TIntfMethEntry;                                              Context: TInvContext;                                              Headers: THeaderList);var XMLDoc: IXMLDocument;begin XMLDoc := NewXMLDocument; XMLDoc.Encoding := FEncoding; Resp.Position := 0; XMLDoc.LoadFromStream(Resp); ProcessResponse(XMLDoc, IntfMD, MD, Context, Headers);end; procedure TOPToSoapDomConvert.ProcessResponse(const XMLDoc: IXMLDocument;                                              const IntfMD: TIntfMetaData;                                              const MD: TIntfMethEntry;                                              Context: TInvContext;                                              Headers: THeaderList);var ProcessSuccess(RespNode, IntfMD, MD, Context); ProcessSuccess函数如下:…. for I := 0 to RespNode.childNodes.Count - 1 do    begin      Node := RespNode.childNodes[I];      { Skip non-valid nodes }      if Node.NodeType <> ntElement then        continue;    // 处理返回值:      if I = RetIndex then      begin        InvData := InvContext.GetResultPointer;        ByRef := IsParamByRef([pfOut], MD.ResultInfo, MD.CC);        ConvertSoapToNativeData(InvData, MD.ResultInfo, InvContext, RespNode, Node, True, ByRef, 1);  ConvertSoapToNativeData(InvData, MD.ResultInfo, InvContext, RespNode, Node, True, ByRef, 1);把SOAP的结果,写入返回区地址空间。   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;//创建TCGIRequestHTTPResponse := 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 tryWebModules.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。   tryObj := InvRegistry.GetInvokableObjectFromClass(AClass);搜寻注册信息,创建实例:            if Obj = nil thenraise 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;然后再发送给客户端。到这里,基本上客户端和服务器端都进行了分析。    
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: