1. 程式人生 > >遠端呼叫技術程式碼追蹤(Remobjects第三方控制元件)

遠端呼叫技術程式碼追蹤(Remobjects第三方控制元件)

 遠端呼叫技術內幕在前面我已經分析了socket和webservice的程式碼追蹤。現在總結一下:三層架構的運作模型:1. BizSnap.NET Remoting Server端運作模式Client Request 送達Server端後,會經過一個Message Dispatcher機制,這個機制大多是幾個重要的元件合作完成,主要在於解出Request中對於所要求物件的描述,以及欲呼叫的方法等資訊,有了這些資訊後Dispatcher就可以找到對應的物件與方法,接著就開始了呼叫動作,由於Request SOAP訊息格式,並不能直接用來呼叫物件的方法,因此得先將SOAP訊息轉化為Stack(堆疊),完成這個轉換動作後就到了這種處理模式中的核心概念了,也就是建立起目的物件並呼叫對應的方法,這個動作非常依賴前面的
Message To Stack程式,因為這個程式會將SOAP訊息轉化為Stack,有了Stack之後Push Stack and Call Method 動作才能正確的執行,那麼如何呼叫目的方法呢?很簡單,只要利用該語言所提供的RTTI資訊(.NET 中則是MetaData),就可取得該方法的記憶體地址,接著只須以低階的ASM IL 所提供的CALL 指令即可呼叫該方法,由於已將SOAP訊息轉為Stack,因此傳入引數就不是問題了。在呼叫結束後,Stack 中已經有了傳回的引數,接著只須將Stack轉回SOAP 訊息傳回給Client端就可以了。BizSnap.NET Remoting
Client端運作模式不管是BizSnap或是.NET Remoting,當Client端欲呼叫Web Services時都會經過一個Proxy Object,於BizSnap中這個物件就是THTTPRIO.NET Remoting中這個物件就是RealProxy,由於這個物件屬於靜態的,因此在使用之前必需將其轉型回目的物件的型別,當Client端下達轉型動作後整個魔法就開始運行了,首先Proxy Object會利用RTTI或是MetaData資訊取得欲轉型的類別資訊,並依照這些資訊建立起一個兼容於該類別的物件(Transparent Proxy Object),接著將這個物件中的所有方法地址替換為
Stub MethodStub Method 做的事情很單純,只是將Stack轉為SOAP Message後送出,當Server端響應後再將SOAP Message轉換為Stack 後返回,這樣整個Client端呼叫動作就完成了,下次再呼叫時只需由Cache中取出這個已建立好的Transparent Proxy Object,就可以直接進行呼叫,這可以避免因反覆以RTTI或是MetaData建立Transparent Proxy Object而失去效率。BizSnap.NET Remoting 的處理模式屬於較低階的方法,這種方法的壞處大於好處,好處是設計者可以完全不瞭解其內部運作,以傳統方式來撰寫程式,壞處是過度依賴編譯器及平臺,增加日後移植到其它語言或平臺上的難度,另外使用動態產生物件類別的低階技術很容易引發效率及記憶體管理的問題。2. NET Web Services JavaNET Web Services Java 的處理模式與.NET RemotingBizSnap大同小異,其間最大的不同之處在於這種模式利用了其語言的特性,採動態呼叫的方式來執行呼叫的動作,而非如先前所提的模式在StackMessage之間進行轉換,這種模式簡單的在Client端與Server端之間插入一個預先編譯好的Proxy Object,這個Object是由WSDL所產生的,其中定義了所有目標物件的方法,在這些方法中簡單的將傳入的引數轉換為SOAP Message,將傳回的訊息轉回引數,其間的運作完全屬於高階型態:Client 端的呼叫伺服器端:由上面兩個圖上可看出,這種模式講求簡單,Client端的Stub Method 由原本的一個變成每個方法一個,Server端則由原本的低階CALL命令轉為語言本身所提供的動態呼叫功能。這樣的簡化帶來了效率,由於Client端不再經過動態轉型與建立中介物件的動作,因此在效率上有顯著的提升,也因為少了這些低階的動作,記憶體管理上顯得容易多了。但這種方式卻有著另外的幾個缺點,由於Proxy Object的程式程式碼增加,相對的程式所佔用的記憶體也隨之變大,另外Server採用動態呼叫的方式來喚起方法,這種方式通常效率不高。RemObjects SDK前面所提的兩種模式皆有其優缺點,RO 在這方面則提出了另一個嶄新的處理模式,下圖是RO Server端處理模式:上圖中大約與前面所提及的模式相同,其中不同之處在於Invoke Object,這是一個預先編譯好的物件,其作用與.NET Web ServicesProxy Object相同,這個物件中所有方法都是Stub Method,將SOAP訊息轉為引數後呼叫Real Object(Implement Object)的方法,完成後將引數轉回訊息後返回Client端。那麼這種模式有何獨到之處呢??答案是效率,整個動作之中看不到低階的Stack或是動態呼叫,沒有這些動作的加入,當然速度上也就加快不少。RO Client端處理方式與Server端大同小異,因此結論是! RO 沒有用到任何的中介技術,也沒有用到任何語言獨有的功能,這也是RO .NET 為何能在短短的幾個月內就能完成的主要原因。下面對RO自帶的程式碼CalcService進行分析:ROWinMessageServer1, TROIndyHTTPServer, TROIndyTCPServer伺服器支援windowMsg, HTTP, TCP等連線方式:ROMessage: TROSOAPMessage; ROBINMessage1: TROBINMessage;支援SOAP, bin兩種資料格式:RO支援多種連線方式和兩種資料格式,具體請參考相應資料。現在先分析伺服器端和ROTCPClient客戶端及相應的TROBINMessage開啟CalcService伺服器端之後,開始跟蹤程式碼:unit uROTypes;initializationtype TRODataType = (rtInteger, rtDateTime, rtDouble, rtCurrency, rtWidestring, rtString,rtInt64, rtBoolean, rtVariant, rtBinary, rtUserDefined);定義了RO的相應的資料型別unit uROClient;_MessageClasses := TClassList.Create; _ExceptionClasses := TClassList.Create;用來註冊Messageexception類。procedure RegisterStringFormats(const StringFormats: array of TROStringFormatClass);unit uROProxy;initialization _ProxyClasses := TStringList.Create;//建立ProxyClassesunit uROServer;initialization  AddTerminateProc(FinalizeClasses); _ServerClasses := TClassList.Create; _ClassFactoryList := nil;//建立成ServerClassesunit uROIndyTCPServer;initializationRegisterServerClass(TROIndyTCPServer);// TROIndyTCPServer 註冊到ServerClasses列表中。unit uROIndyHTTPServer;initialization RegisterServerClass(TROIndyHTTPServer);// TROIndyHTTPServer註冊到ServerClasses列表中。unit uROBINMessage;initialization RegisterMessageClass(TROBINMessage);// TROBINMessage註冊到MessageClass列表中。unit uROWinMessageServer;initialization RegisterServerClass(TROWinMessageServer);// TROWinMessageServer註冊到MessageClass列表中。然後到unit CalcLibrary_Intf;單元initialization RegisterProxyClass(CalcService_IID, TCalcService_Proxy);可以看到,注到到前面建立的_ProxyClass中。_ProxyClasses := TStringList.Create;procedure RegisterProxyClass(const anInterfaceID : TGUID; aProxyClass : TROProxyClass);begin _ProxyClasses.AddObject(GUIDToString(anInterfaceID), TObject(aProxyClass))end;unit CalcService_Impl;initialization TROClassFactory.Create('CalcService', Create_CalcService, TCalcService_Invoker);這裡是比較重要的地方:我們要把一些資訊到TROClassFactory中註冊。依次是介面,過程(喚起業務物件)invoker類(完成資料打包,傳送等)procedure Create_CalcService(out anInstance : IUnknown);begin anInstance := TCalcService.Create(NIL);end;另外unit CalcLibrary_Invk;TCalcService_Invoker = class(TROInvoker) private protected published    procedure Invoke_Sum(const __Instance:IInterface; const __Message:IROMessage; const __Transport:IROTransport);    procedure Invoke_GetServerTime(const __Instance:IInterface; const __Message:IROMessage; const __Transport:IROTransport); end;這個類則是完成了低層的呼叫。看看這段程式碼:procedure TCalcService_Invoker.Invoke_Sum(const __Instance:IInterface; const __Message:IROMessage; const __Transport:IROTransport);    __Message.Read('A', TypeInfo(Integer), A, []);    __Message.Read('B', TypeInfo(Integer), B, []);Result := (__Instance as CalcService).Sum(A, B);這裡呼叫了TcalcService的具體方法:    __Message.Initialize(__Transport, 'CalcLibrary', 'CalcService', 'SumResponse');    __Message.Write('Result', TypeInfo(Integer), Result, []);    __Message.Finalize;由此可見,真正工作的是TCalcService_Invoker這個類它把低層的通訊隱藏了起來。取出客戶端呼叫的引數後,呼叫TcalcService做實際的業務處理,然後把相應的資訊寫入Message中返回給客戶端。稍後請看詳細的分析。看看實際註冊的過程:TRORemotableCreatorFunc = procedure(out anInstance : IInterface);constructor TROClassFactory.Create(const anInterfaceName: string; aCreatorFunc: TRORemotableCreatorFunc; anInvokerClass : TROInvokerClass);begin inherited Create; fInvoker := NIL; fCreatorFunc := aCreatorFunc; fInterfaceName := anInterfaceName; fInvokerClass := anInvokerClass; RegisterClassFactory(Self);end;TROClassFactory = class(TInterfacedObject, IROClassFactory)     privatefCreatorFunc : TRORemotableCreatorFunc;把實際呼叫業務物件的地址賦給fCreatorFunc,在適當的時候建立該業務物件。繼續:procedure RegisterClassFactory(const aClassFactory : IROClassFactory);begin ClassFactoryList.Add(aClassFactory);end;把每個工廠增加到ClassFactoryList中,統一管理。TROClassFactoryList = class(TInterfaceList)繼續:Application.CreateForm(TfmMain, fmMain);constructor TROMessage.Create(aOwner : TComponent);begin inherited Create(aOwner); InitObject;end;繼續:procedure TROSOAPMessage.InitObject;begin inherited; fXMLMessage := NewROXmlDocument; fXMLMessage.New(tag_Envelope);end;function NewROXmlDocument : IXMLDocument; result := TROMSXMLDocument.Create;//這裡就不細分析了,在《遠端呼叫WEBSERVICE》中已經具體分析過。用來解析XML總結一下:CalcLibrary_Intf,CalcService_ImplCalcLibrary_Invk單元。這三個單元。CalcLibrary_Intf主要是聲明瞭業務介面。TCalcService_Proxy一個代理的類(這裡用的是靜態代理),這裡留到以後(RO程式碼生成技術,生成三個單元,我是不喜歡這種用法的,如果能改成用動態代理,估計會節省大量的程式碼,整個維護更加簡單。從EJBSPRING,大家可以看到這種程式碼生成技術的不足,否則JAVA中的SPRING也不會這麼流行,有興趣的兄弟,還是可以把它改成動態代理來實現。我也有一個想法,Indy控制元件的效率及程式碼生成還有資料持久層及業務物件處理上(做一款類似spring的框架)RO支援的還是不充分的,有時間,準備把它改寫一遍,有興趣的可以和我一起交流)。繼續程式碼追蹤:CoCalcService = class    class function Create(const aMessage : IROMessage; aTransportChannel : IROTransportChannel) : CalcService; end;這個是一個服務類。就是建立一個TCalcService_Proxy代理。CalcService_Impl這個單元,則是真正的業務物件方法實現的單元。procedure Create_CalcService(out anInstance : IUnknown);begin anInstance := TCalcService.Create(NIL);end;這裡RO用了一種常用的事件繫結的手法。這樣RO的工廠指標賦值後就可喚起業務物件,實現業務物件的啟用。TROClassFactory.Create('CalcService', Create_CalcService, TCalcService_Invoker);我們看還註冊了TCalcService_Invoker這個類。回到CalcLibrary_Invk單元中發現procedure TCalcService_Invoker.Invoke_Sum(const __Instance:IInterface; const __Message:IROMessage; const __Transport:IROTransport);{ function Sum(const A: Integer; const B: Integer): Integer; }var A: Integer; B: Integer; Result: Integer;begin try__Message.Read('A', TypeInfo(Integer), A, []);//把客戶端傳送的Message訊息包拆包,賦給已定義的變數。    __Message.Read('B', TypeInfo(Integer), B, []);    Result := (__Instance as CalcService).Sum(A, B); // 呼叫    __Message.Initialize(__Transport, 'CalcLibrary', 'CalcService', 'SumResponse');__Message.Write('Result', TypeInfo(Integer), Result, []);//把伺服器端的結果打包。標記'Result',寫入值。傳送客戶端。    __Message.Finalize; finally end;end;它呼叫了真正的業務物件,現在大概清楚它的工作原理了吧,讓我們繼續追蹤它的原始碼。首先建立TROBINMessage類。constructor TROBINMessage.Create(aOwner: TComponent);begin inherited; UseCompression := TRUE;end;前面說過,RO支援SOAP, bin兩種資料格式:它們的父類都是TROMessageRO的訊息處理的技術是非常值得學習的,它的模型比較先進。我會仔細分析的。先到父類裡看看:constructor TROMessage.Create(aOwner : TComponent);begin inherited Create(aOwner); InitObject;end;看看InitObjectprocedure TROMessage.InitObject;begin fSerializer := CreateSerializer; fClientID := NewGuid(); //CreateGUID(fClientID);end;先停下來仔細研究ROMessage,這個類非常重要。TROMessage = class(TComponent, IUnknown, IROMessage, IROMessageCloneable, IROModuleInfo)     private       fSerializer : TROSerializer;       fMessageName,       fInterfaceName : string;       fOnReadFromStream: TStreamOperation;       fOnWriteToStream: TStreamOperation;       fOnServerException: TExceptionEvent;……       fClientID : TGUID;這裡比較重要的TROSerializer, fMessageName, fInterfaceName, fClientIDTStreamOperation = procedure(aStream : TStream) of object; 處理TStreamTExceptionEvent = procedure(anException : Exception) of object; 處理異常另外用了GUID做標識。function TROMessage.Clone: IROMessage; 克隆一個RoMessagefunction TROMessage.Clone: IROMessage;begin result := TROMessageClass(ClassType).CreateRefCountedClone(self) as IROMessage;end;constructor TROMessage.CreateRefCountedClone(iMessage: TROMessage);begin Create(); //呼叫建構函式 Assign(iMessage);   //賦值 fReferenceCounted := true;end;現在看看TROSerializer,這是TROMessage最重要的部分:裡面主要定義了一些虛方法。這個一個序列化的類。主要功能是序列化。COM中也叫散集和列集。說穿了就是資料怎麼序列化到一個流中,或者逆操作。我們來仔細研究研究。個人覺得這裡面的東西很值得研究。搞清楚序列化,對於我們靈活的使用RO將有非常大的幫助。procedure TROSerializer.BeginReadObject(const aName: string; aClass : TClass; var anObject: TObject; var LevelRef : IUnknown; var IsValidType : boolean; ArrayElementId : integer = -1);begin IsValidType := aClass.InheritsFrom(TROComplexType)end;//判斷是不是RO定義的TROComplexType(合成型別的類)派生出來的子類。//序列化()procedure TROSerializer.Read(const aName: string; aTypeInfo: PTypeInfo; var Ptr; ArrayElementId : integer = -1);begin case aTypeInfo^.Kind of       tkClass    : ReadObject(aName, GetTypeData(aTypeInfo).ClassType, Ptr, ArrayElementId);    else RaiseError(err_TypeNotSupported, [GetEnumName(TypeInfo(TTypeKind), Ord(aTypeInfo^.Kind))]); end;end;其它型別就不研究了,大家估計都用過。我自己開發的一款型別的O/Pmaping中也用到了相應的技術,可以通過設定VO之間的對映,把兩個VO加入明細VO類中,然後通過RTTL資訊解析出SQL語句(select, insert,update,delete)(分析)。,有興趣的兄弟可以跟我聯絡。我的例子如下:/------------------------------------------------------例如: Tuser = (TdataTransferObject)A: TA;B: TB; End; 用法: rdmDS.select(TUser, ds) 返回一個數據集/-------------------------------------------------------繼續看看ReadObject怎麼處理的:procedure TROSerializer.ReadObject(const aName: string; aClass : TClass; var Ref; ArrayElementId : integer = -1);var obj : TObject absolute Ref;    props : PPropList;    cnt, i : integer;    LevelRef : IUnknown;    validtype : boolean;    // Temporary variables    int64val : int64;    intval : integer;    enuval : byte;    dblval : double;    //extval : extended;    strval : string;    {$IFNDEF DELPHI5}wstrval : widestring;{$ENDIF}    objval : TObject;begin obj := nil; { no matter what's passed in, we wanna start fresh } BeginReadObject(aName, aClass, obj, levelref, validtype, ArrayElementId);procedure TROSerializer.BeginReadObject(const aName: string; aClass : TClass; var anObject: TObject; var LevelRef : IUnknown; var IsValidType : boolean; ArrayElementId : integer = -1);begin IsValidType := aClass.InheritsFrom(TROComplexType)end;在序列化(讀)一個物件時,首先判斷是不是從TROComplexType派生的。if Assigned(obj) and (not validtype) then RaiseError(err_TypeNotSupported, [obj.ClassName]);   //如果不是從TROComplexType派生,則丟擲一個異常!這裡大家應該清楚了,如果想序列化一個物件,必須從TROComplexType派生才可以。清楚了這點,在三層中傳遞物件的問題,大家應該知道怎麼解決了吧。TROComplexType = class(TPersistent)     private       fFieldCount : integer;fFieldList : PPropList;序列化從Tpersistent派生出來({m+}{m-}這裡是關鍵),這裡RO還加上了RTTL資訊。 if Assigned(obj) and (obj.ClassInfo <> nil) then begin    cnt := GetTypeData(obj.ClassInfo).PropCount;//GetTypeData 函式獲得類的屬性數量。    if (cnt>0) then begin      GetMem(props, cnt*SizeOf(PPropInfo)); //分配存放屬性資訊的空間      try        cnt := GetPropList(PTypeInfo(obj.ClassInfo), tkProperties, props);//GetPropList 傳入類的 TTypeInfo 指標和 TPropList 的指標,它為 PropList 分配一塊記憶體後把該記憶體填充為指向 TPropInfo 的指標陣列,最後返回屬性的數量。        for i := 0 to (cnt-1) do begin          with props^[i]^ do begin。。。。。。。。。。。。。。              tkInteger : begin                ReadInteger(Name, GetTypeData(PropType^).OrdType, intval);//虛擬函式:呼叫子類方法(從客戶端傳送的一個流中) 例:procedure TROStreamSerializer.ReadInteger(const aName: string; anOrdType: TOrdType; var Ref; ArrayElementId : integer = -1);var sze : byte;    src : pointer;begin src := @Ref;。。。。。 fStream.Read(src^, sze);end;                SetOrdProp(obj, Name, intval); //把屬性資訊寫入物件中。              end;。。。。。。。。。。。              tkClass : begin                ReadObject(Name, GetTypeData(PropType^).ClassType, objval); //遞迴                SetObjectProp(obj, Name, objval); //把屬性(物件)寫入物件中。              end;………….. CustomReadObject(aName, aClass, obj, ArrayElementId); EndReadObject(aName, aClass, obj, levelref);end;//TROArray型別的資料(序列化)procedure TROSerializer.CustomWriteObject(const aName: string; aClass : TClass; const Ref; ArrayElementId : integer = -1);var obj : TObject absolute Ref;    i : integer;    itemref : pointer;begin if Assigned(obj) then begin    if (obj is TROArray) then with TROArray(obj) do begin      if (GetItemClass<>NIL) then begin        for i := 0 to (Count-1) do begin          itemref := GetItemRef(i);          Write(ArrayItemName, GetItemType, itemref, i);        end;      end      else begin        for i := 0 to (Count-1) do begin          itemref := GetItemRef(i);          Write(ArrayItemName, GetItemType, itemref^, i);        end;      end;    end; end;end;//再回到CreateSerializerprocedure TROMessage.InitObject;begin fSerializer := CreateSerializer; fClientID := NewGuid(); //CreateGUID(fClientID);end;function TROBINMessage.CreateSerializer : TROSerializer;begin result := TROStreamSerializer.Create(NIL);end;//真正工作的類是TROStreamSerializer,具體的資料(序列化)稍後再分析。constructor TROIndyTCPServer.Create(aComponent: TComponent);begin inherited; fIndyServer := CreateIndyServer; fIndyServer.Name := 'InternalIndyServer'; {$IFDEF DELPHI6UP} fIndyServer.SetSubComponent(True); {$ENDIF}end;跟蹤一下TROIndyTCPServer用法:constructor TROServer.Create(aOwner: TComponent);begin inherited; fDispatchers := GetDispatchersClass.Create(Self);end;function TROServer.GetDispatchersClass : TROMessageDispatchersClass;begin result := TROMessageDispatchers; // Defaultend;constructor TROMessageDispatchers.Create(aServer : TROServer);begin inherited Create(GetDispatcherClass); fServer := aServer;end;function TROIndyTCPServer.CreateIndyServer: TIdTCPServer;begin result := TROTIdTCPServer.Create(Self); result.OnExecute := InternalOnExecute; result.DefaultPort := 8090;end;呼叫:constructor TIdTCPServer.Create(AOwner: TComponent);begin inherited Create(AOwner); FBindings := TIdSocketHandles.Create(Self); // Before Command handlers FReplyTexts := TIdRFCReplies.Create(Self); FCommandHandlers := TIdCommandHandlers.Create(Self); FCommandHandlersEnabled := IdCommandHandlersEnabledDefault; FGreeting := TIdRFCReply.Create(nil); FMaxConnectionReply := TIdRFCReply.Create(nil); FThreads := TThreadList.Create; FThreadClass := TIdPeerThread; FReplyUnknownCommand := TIdRFCReply.Create(nil); // FTerminateWaitTime := 5000; FListenQueue := IdListenQueueDefault; //TODO: When reestablished, use a sleeping thread instead// fSessionTimer := TTimer.Create(self);end;//繼續:constructor TIdComponent.Create(axOwner: TComponent);begin inherited Create(axOwner); GStackCriticalSection.Acquire; try    Inc(GInstanceCount);    if GInstanceCount = 1 then begin      GStack := TIdStack.CreateStack;    end; finally GStackCriticalSection.Release; end;end;class function TIdStack.CreateStack: TIdStack;begin Result := GStackClass.Create;end;constructor TIdStackWindows.Create;var sData: TWSAData;begin inherited Create; if not GStarted then begin    if WSAStartup($202, sData) = SOCKET_ERROR then begin      raise EIdStackInitializationFailed.Create(RSWinsockInitializationError);    end;    GStarted := True; end;end;//可以看出真正進行SOCKET通過的部分應該是TidSocketHandle,TidSocketHandle又呼叫了TidStack的派生類TidStackWindows來完成真正的底層通訊工作的。TidStack裡面的方法都是抽象的,說明具體的通訊細節是由派生類實現的。這樣的好處是什麼,底層通訊的無關性。我覺得這是非常重要的部分,仔細閱讀RO的原始碼中,發現了類似非常多的應用,正是這種良好的設計,使RO的通訊部分與業務邏輯部分徹底分離。同時支援多種通訊協議。constructor TROIndyTCPServer.Create(aComponent: TComponent);begin inherited; fIndyServer := CreateIndyServer; fIndyServer.Name := 'InternalIndyServer'; fIndyServer.SetSubComponent(True);end;classs單元:function InitInheritedComponent(Instance: TComponent; RootAncestor: TClass): Boolean; function InitComponent(ClassType: TClass): Boolean; begin    Result := False;    if (ClassType = TComponent) or (ClassType = RootAncestor) then Exit;    Result := InitComponent(ClassType.ClassParent);    Result := InternalReadComponentRes(ClassType.ClassName, FindResourceHInstance(      FindClassHInstance(ClassType)), Instance) or Result; end;

相關推薦

遠端呼叫技術程式碼追蹤(Remobjects第三方控制元件)

 遠端呼叫技術內幕在前面我已經分析了socket和webservice的程式碼追蹤。現在總結一下:三層架構的運作模型:1. BizSnap與.NET Remoting 的Server端運作模式當Client 將Request 送達Server端後,會經過一個Message Dispatcher機制,這個機

遠端呼叫技術程式碼追蹤(webservice)

 最近閱讀了SocketConn的原始碼和WebService 的原始碼,把追蹤的過程寫了下來,方便大家學習。畢竟這需要精力,時間和毅力。感謝煮茶待英雄博志區和三層資料庫討論區兄弟們的支援,特別是julian兄弟,不是他,我可能沒耐心繼續下去。如果有時間,大家可以繼續完善。從socket和Websevic

Java程式設計師從笨鳥到菜鳥(七十一)WebService 一種遠端呼叫技術

原文連結:https://blog.csdn.net/c99463904/article/details/76018436 非常感謝作者 前言 隨著 web 應用程式的廣泛使用,不同應用程式之間的通訊也變得更加頻繁,如支付寶獲取銀行介面來獲取相應的賬戶資訊,各種天氣預報軟體獲

淺談WebService跨平臺遠端呼叫技術

  基於瀏覽器的瘦客戶端應用程式並不是 因為瘦客戶能夠提供更好的使用者介面,而是因為它能夠避免花在桌面應用程式釋出上的高成本。釋出桌面應用程式成本很高,一半是因為應用程式安裝和配置的問 題,另一半是因為客戶和伺服器之間通訊的問題。傳統的Windows富客戶應用程式使用DCOM來與伺服器進行通訊和呼叫遠端物件。

soap方式的遠端呼叫示例程式碼

需要jar包根據import的內容檢視. axis是升級版 package com.soap; import java.net.URL; import javax.xml.namespace.Q

jquery 第三方控制元件ztree使用說明

1、引入相關資源 <link href="zTree/zTreeStyle/zTreeStyle.css" rel="stylesheet" /> <script src="../javascript/jquery-1.7.1.min.js">&l

React-Native開發中常用的第三方控制元件持續更新

筆者簡書:https://www.jianshu.com/u/8ba7c349861d, 歡迎大家關注 2018.8.23更新: 動態修改Android的softmodule: react-native-android-keyboard-adjust    

IOS開發學習筆記七 使用程式碼為介面新增控制元件

首先是要實現的效果圖:demo下載 我們用程式碼在介面上建立了一個button,有預設圖片背景和高亮圖片背景兩種狀態。 具體實現程式碼: 主要是在介面對應的ViewController類初始化的時候,來通過程式碼建立按鈕。 #import "ViewControll

Githup上第三方控制元件的地址收藏

倒計時 https://github.com/iwgang/CountdownView RecyclerView實現帶有頭部的頂部懸浮置頂佈局https://github.com/byd666/CeilingDemo Android載入Gif和ImageView的通

unity如何在C#程式碼裡面獲得介面控制元件的屬性

用GameObject.Find("遊戲物件名").GetComponent<組建名>().具體屬性 比如名字叫Player的遊戲物件下,有一個Collider元件,我想取得它的大小屬性: var tmpSize = GameObject.Find("Player").GetComp

android 在java程式碼中動態設定控制元件的位置和設定片大小

需要動態改變佈局裡面控制元件的相對位置 如: 1.改變RelativeLayout佈局裡面某個控制元件的layout_toLeftOf 的屬性 RelativeLayout.Layoutparams params = (RelativeLayout.LayoutParams)view

基於uniGUI的第三方控制元件Unifalcon的安裝

Unifalcon是一組基於unigui的第三方UI元件,是一套漂亮實用的UI控制元件,用這套控制元件可以輕易設計出漂亮美觀的介面。控制元件的安裝方法如下:(控制元件官方網站https://store.falconsistemas.com.br/) 將壓縮包完全解壓

Asp.Net MVC4入門指南(10):第三方控制元件Studio for ASP.NET MVC4 工具應用

ComponentOne Studio for ASP.NET最新版本2013V1支援MVC4,其中包括: 新增 MVC 4 工程模板 (C# & VB) 開箱即用的MVC 4 工程模板基於Microsoft內建模板建立,我們僅優化了標記和CSS樣式為預設風格,熟悉的模板佈局和介面風格,無疑將縮

如何使用第三方控制元件上傳圖片?

第一步下載,commons-fileupload-1.2.2.jar和commons-io-2.4.jar建議在API中參考使用到的類和介面將jar匯入專案中(WEB-INF/lib),並勿忘 build Path;修改新增新聞的頁面newsDetailCreateSimpl

第三方控制元件DevExpress.repositoryItemCheckEdit複選擇框多選的問題

最近一直在弄第三方控制元件DevExpress,在gridcontol中巢狀進複選框,卻只能單選,為了解決此問題在網上查了相關資料,並結合專案情況,最終得以解決1、在GridView中某列中新增進複選框,相關的資料繫結設定成0或1進行繫結。2、新增事件(此程式碼為轉載)

c# 第三方控制元件 多語言設定問題

  最近有個專案用了第三方控制元件,ComponentOne Studio ,介面本身支援多語言,包括中文,但程式執行起來,控制元件只有英文介面。   查半天資料沒有結果。   原因:VS專案有語言支援設定,SupportedCultures,預設值為空   VS本身沒有

Android中第三方控制元件PhotoView的基本使用

PhotoView的簡介:這是一個圖片檢視庫,實現圖片瀏覽功能,支援pinch(捏合)手勢或者點選放大縮小。支援在ViewPager中翻頁瀏覽圖片。 PhotoView 是一款擴充套件自Android ImageView ,支援通過單點/多點觸控來進行圖片縮放的智慧控制

常用的delphi第三方控制元件

      AutoUpgrader 這個是自動更新的一個控制元件,適合桌面程式自動更新,但是已經沒有更新了。       Devart 公司出品的UniDAC,ODAC,SDAC,IBDAC, 這幾個是目前delphi 資料庫存取最好的控制元件,UniDAC 幾乎支援所有的資料庫存取,而後面幾個則是針對每種

第三方控制元件:上拉載入、下拉重新整理控制元件

使用: (1)匯入第三方外掛庫 Android-PullToRefresh-master.zip (2) 在佈局檔案中使用第三方外掛 com.handmark.pulltorefresh.library.PullToRefreshListView (3) 自定

Delphi第三方控制元件大測評

  古人云∶“工欲善其事,必先利其器。” 這句話,我想凡是用Delphi的朋友,應該都有很深切的體會吧。的確,如果Delphi沒有了控制元件的支援,那麼人氣度一定會大大的降低,不會有現在這樣多的Fan了,俺也就改行用BCB或VC了,呵呵。 但是,現在控制元件滿天飛,不要說D