MASTERING DELPHI 6 phần 9 ppt

108 190 0
MASTERING DELPHI 6 phần 9 ppt

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

864 or to use the very precise timing functions of the multimedia support unit, MMSystem.) Here is the code of one of the methods; they are quite similar: procedure TClientForm.BtnIntfClick(Sender: TObject); var I, K: Integer; Ticks: Cardinal; begin Screen.Cursor := crHourglass; try Ticks := GetTickCount; K := 0; for I := 1 to 100 do K := K + IMyServer.Value; Ticks := GetTickCount - Ticks; ListResult.items.Add (Format ( ‘Interface: %d - Seconds %.3f’, [K, Ticks / 1000])); finally Screen.Cursor := crDefault; end; end; With this program, you can compare the output obtained by calling this method based on an interface, the corresponding version based on a variant, and even a third version based on a dispatch interface. An example of the output (which is added to a list box so you can do several tests and compare the results) is shown in Figure 20.6. Obviously, the timing depends on the speed of your computer, and you can also alter the results by increasing or decreasing the maximum value of the loop counter. FIGURE 20.6: The TLibCli OLE Automation controller can access the server in different ways, with different performance results. Notice the server window in the background. Chapter 20 • From Automation to COM+ Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 865 We’ve already seen how you can use the interface and the variant. What about the dispatch interface? You can declare a variable of the dispatch interface type, in this case: var DMyServer: IFirstServerDisp; Then you can use it to call the methods as usual, after you’ve assigned an object to it by cast- ing the object returned by the CoClass: DMyServer := CoFirstServer.Create as IFirstServerDisp; Looking at the timing and at the internal code of the example, there is apparently very little difference between the use of the interface and of the dispatch interface, because the two are actually connected. In other words, we can say that dispatch interfaces are a technique in between variants and interfaces, but they deliver almost all of the speed of interfaces. The Scope of Automation Objects Another important element to keep in mind is the scope of the automation objects. Variants and interface objects use reference-counting techniques, so if a variable that is related to an inter- face object is declared locally in a method, at the end of the method the object will be destroyed and the server may terminate (if all the objects created by the server have been destroyed). For example, writing a method with this code produces very little effect: procedure TClientForm.ChangeColor; var IMyServer: IFirstServer; begin IMyServer := CoFirstServer.Create; IMyServer.ChangeColor; end; Unless the server is already active, a copy of the program is created, and the color is changed, but then the server is immediately closed as the interface-typed object goes out of scope. The alternative approach I’ve used in the TLibCli example is to declare the object as a field of the form and create the COM objects at start-up, as in this procedure: procedure TClientForm.FormCreate(Sender: TObject); begin IMyServer := CoFirstServer.Create; end; With this code, as the client program starts, the server program is immediately activated. At the program termination, the form field is destroyed and the server closes. A further alternative Writing an OLE Automation Server Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 866 is to declare the object in the form, but then create it only when it is used, as in these two code fragments: // MyServerBis: Variant; if varType (MyServerBis) = varEmpty then MyServerBis := CoFirstServer.Create; MyServerBis.ChangeColor; // IMyServerBis: IFirstServer; if not Assigned (IMyServerBis) then IMyServerBis := CoFirstServer.Create; IMyServerBis.ChangeColor; NOTE A variant is initialized to the varEmpty type when it is created. If you instead assign the value null to the variant, its type becomes varNull. Both varEmpty and varNull represent variants with no value assigned, but they behave differently in expression evaluation. The varNull value always propagates through an expression (making it a null expression), while the varEmpty value quietly disappears. The Server in a Component When creating a client program for our server or any other Automation server, we can use a bet- ter approach, namely, wrapping a Delphi component around the COM server. Actually, if you look at the final portion of the TlibdemoLib_TLB file, you can find the following declaration: // OLE Server Proxy class declaration TFirstServer = class(TOleServer) private FIntf: IFirstServer; FProps: TFirstServerProperties; function GetServerProperties: TFirstServerProperties; function GetDefaultInterface: IFirstServer; protected procedure InitServerData; override; function Get_Value: Integer; procedure Set_Value(Value: Integer); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure Connect; override; procedure ConnectTo(svrIntf: IFirstServer); procedure Disconnect; override; procedure ChangeColor; property DefaultInterface: IFirstServer read GetDefaultInterface; property Value: Integer read Get_Value write Set_Value; published property Server: TFirstServerProperties read GetServerProperties; end; Chapter 20 • From Automation to COM+ Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 867 This is a new component, derived from TOleServer, that the system registers in the Register procedure, which is part of the unit. If you add this unit to a package, the new server compo- nent will become available on the Delphi Component Palette. You can also import the type library of the new server (with the Project ➢ Import Type Library menu command), add the server to the list (by clicking the Add button and selecting the server’s executable file), and install it in a new or existing package. The component will be placed in the Servers page of the Palette. The Import Type Library dialog box indicating these operations is visible in Figure 20.7. I’ve created a new package, PackAuto, available in the directory of the TlibDemo project. In this package, I’ve added the directive LIVE_SERVER_AT_DESIGN_TIME in the Directories/ Conditionals page of the Project Options dialog box of the package. This enables an extra feature that you don’t get by default: at design time, the server component will have an extra property that lists as subitems all the properties of the Automation server. You can see an example in Figure 20.8, taken from the TLibComp example at design time. WARNING The LIVE_SERVER_AT_DESIGN_TIME directive should be used with care with the most com- plex Automation servers (including programs such as Word, Excel, PowerPoint, and Visio). In fact, this setting requires the application to be in a particular mode before you can use some properties of their automation interfaces. For example, you’ll get exceptions if you touch the Word server before a document has been opened in Word. That’s why this feature is not active by default in Delphi—it’s problematic at design time for many servers. FIGURE 20.7: The Import Type Library dialog box can be used to import an Automation server object as a new Delphi component. Writing an OLE Automation Server Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 868 As you can see in the Object Inspector, the component has few properties. AutoConnection indicates when to start up the server component at design time and as soon as the client program starts. As an alternative, the Automation server is started the first time one of its methods is called. Another property, ConnectKind, indicates how to establish the connection with the server. It can always start a new instance (ckNewInstance), use the running instance (ckRunningInstance, which causes an access violation if the server is not already running), or select the current instance or start a new one if none is available (ckRunningOrNew). Finally, you can ask for a remote server with ckRemote and directly attach a server in the code after a manual connection with ckAttachToInterface. OLE Data Types OLE and COM do not support all of the data types available in Delphi. This is particularly important for OLE Automation, because the client and the server are often executed in dif- ferent address spaces, and the system must move the data from one side to the other. Also keep in mind that OLE interfaces should be accessible by programs written in any language. COM data types include basic data types such as Integer, SmallInt, Byte, Single, Double, WideString, Variant, and WordBool (but not Boolean). Table 20.1 presents the mapping of some basic data types, available in the type-library editor, to the corresponding Delphi types. TABLE 20.1: OLE and Delphi Data Types OLE Type Delphi Type BSTR WideString byte ShortInt CURRENCY Currency DATE TDateTime DECIMAL TDecimal FIGURE 20.8: A server component, with the live properties at design time Chapter 20 • From Automation to COM+ Continued on next page Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 869 TABLE 20.1 continued: OLE and Delphi Data Types OLE Type Delphi Type double Double float Single GUID GUID int SYSINT long Integer LPSTR PChar LPWSTR PWideChar short SmallInt unsigned char Byte unsigned int SYSUINT unsigned long UINT unsigned short Word VARIANT OleVariant Notice that SYSINT is currently defined as an Integer, so don’t worry about the apparently strange type definition. Besides the basic data types, you can also use OLE types for com- plex elements such as fonts, string lists, and bitmaps, using the IFontDisp, IStrings, and IPictureDisp interfaces. The following sections describe the details of a server that provides a list of strings and a font to a client. Exposing Strings Lists and Fonts The ListServ example is a practical demonstration of how you can expose two complex types, such as a list of strings and a font, from an OLE Automation server written in Delphi. I’ve chosen these two specific types simply because they are both supported by Delphi. The IFontDisp interface is actually provided by Windows and is available in the ActiveX unit. The AxCtrls Delphi unit extends this support by providing conversion methods like GetOleFont and SetOleFont. The IStrings interface is provided by Delphi in the StdVCL unit, and the AxCtrls unit provides conversion functions for this type (along with a third type I’m not going to use, TPicture). WARNING To run this and similar applications, the StdVCL library must be installed and registered on the client computer. On your computer, it is registered during Delphi’s installation. Writing an OLE Automation Server Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 870 The server we are building has a plain form containing a list-box component. It includes an Automation object built around the following interface: type IListServer = interface (IDispatch) [‘{323C4A84-E400-11D1-B9F1-004845400FAA}’] function Get_Items: IStrings; safecall; procedure Set_Items(const Value: IStrings); safecall; function Get_Font: IFontDisp; safecall; procedure Set_Font(const Value: IFontDisp); safecall; property Items: IStrings read Get_Items write Set_Items; property Font: IFontDisp read Get_Font write Set_Font; end; The server object has the same four methods listed in its interface as well as some private data storing the status, the initialization function, and the destructor: type TListServer = class (TAutoObject, IListServer) private fItems: TStrings; fFont: TFont; protected function Get_Font: IFontDisp; safecall; function Get_Items: IStrings; safecall; procedure Set_Font(const Value: IFontDisp); safecall; procedure Set_Items(const Value: IStrings); safecall; public destructor Destroy; override; procedure Initialize; override; end; The code of the methods is limited to few statements. The pseudoconstructor creates the internal objects, and the destructor destroys them. Here is the first of the two: procedure TListServer.Initialize; begin inherited Initialize; fItems := TStringList.Create; fFont := TFont.Create; end; The Set and Get methods copy information from the OLE interfaces to the local data and then from this to the form and vice versa. The two methods of the strings, for example, do this by calling the GetOleStrings and SetOleStrings Delphi functions. After we’ve compiled and registered the server, we can turn our attention to the client application. This embeds the Pascal translation of the type library of the server, as in the pre- vious example, and then implements an object that uses the interface. Instead of creating the Chapter 20 • From Automation to COM+ Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 871 server when the object starts, the client program creates it when it is required. I’ve described this technique earlier, but the problem is that because there are several buttons a user can click, and we don’t want to impose an order, every event should have a handler like this: if not Assigned (ListServ) then ListServ := CoListServer.Create; This kind of code duplication is quite dangerous, so I’ve decided to use an alternative approach. I’ve defined a property corresponding to the interface of the server and defined a read method for it. The property is mapped to some internal data I’ve defined with a different name to avoid the error of using it directly. Here are the definitions added to the form class: private fInternalListServ: IListServer; function GetListSrv: IListServer; public property ListSrv: IListServer read GetListSrv; The implementation of the Get method can check whether the object already exists. This code is going to be repeated often, but that should not slow down the application noticeably: function TListCliForm.GetListSrv: IListServer; begin // eventually create the server if not Assigned (fInternalListServ) then fInternalListServ := CoListServer.Create; Result := fInternalListServ; end; You can see an example of the client application running (along with the server) in Figure 20.9. This is an example of the selection of a font, which is then sent to the server: procedure TListCliForm.btnFontClick(Sender: TObject); var NewFont: IFontDisp; FIGURE 20.9: The ListCli and ListServ applications share complex data, namely fonts and lists of strings. Writing an OLE Automation Server Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 872 begin // select a font and apply it if FontDialog1.Execute then begin GetOleFont (FontDialog1.Font, NewFont); ListSrv.Font := NewFont; end; end; There are also several methods related to the strings, which you can see by looking at the source code of the program. Using Office Programs So far, we’ve built both the client and the server side of the OLE Automation connection. If your aim is just to let two applications you’ve built cooperate, this is certainly a useful tech- nique, although it is not the only one. We’ve seen some alternative data-sharing approaches in the last two chapters (using memory-mapped files and the wm_CopyData message). The real value of OLE Automation is that it is a standard, so you can use it to integrate your Delphi programs with other applications your users own. A typical example is the integration of a program with office applications, such as Microsoft Word and Microsoft Excel, or even with stand-alone applications, such as AutoCAD. Integration with these applications provides a two-fold advantage: • You can let your users work in an environment they know—for example, generating reports and memos from database data in a format they can easily manipulate. • You can avoid implementing complex functionality from scratch, such as writing your own word-processing code inside a program. Instead of just reusing components, you can reuse complex applications. There are also some drawbacks with this approach, which are certainly worth mentioning: • The user must own the application you plan to integrate with, and they may also need a recent version of it to support all the features you are using in your program. • You have to learn a new programming language and programming structure, often with limited documentation at hand. It is true, of course, that you are still using Pascal, but the code you write depends on the OLE data types, the types introduced by the server, and in particular, a collection of interrelated classes that are often difficult to understand. • You might end up with a program that works only with a specific version of the server application, particularly if you try to optimize the calls by using interfaces instead of variants. In particular, Microsoft does not attempt to maintain script compatibility between major releases of Word or other Office applications. Chapter 20 • From Automation to COM+ Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 873 We’ve already seen a small source code excerpt from the WordTest example, but now I want to complete the coverage of this limited but interesting test program by providing a few extra features. Sending Data to Microsoft Word Delphi simplifies the use of Microsoft Office applications by preinstalling some ready-to-use components that wrap the Automation interface of these servers. These components, avail- able in the Servers page of the Palette, have been installed using the same technique I demonstrated in the last section. NOTE What I want to underline here is that the real plus of Delphi lies in this technique of creating components to wrap existing Automation servers, rather than in the availability of some pre- defined server components. Technically, it is possible to use variants to interact with Automation servers, as we’ve seen in the section “Introducing Type Libraries.” Using interfaces and the type libraries is cer- tainly better, because the compiler helps you catch errors in the source code and produces faster code. Thanks to the new server component, this process is also quite straightforward. I’ve written a program, called DBOffice, which uses predefined server components to send a table to Word and to Excel. In both cases, you can use the application object, the document/ worksheet object, or a combination of the two. There are other specialized components, for tasks such as handling Excel charts, but this example will suffice to introduce use of the built- in Office components. NOTE The DBOffice program was tested with Office 97. I’m currently using StarOffice more often than the Microsoft suite, so I never feel compelled to give Microsoft more money by upgrading to their newer offerings. In case of Microsoft Word, I use only a document object with default settings. The code used to send the table to Word starts by adding some text to a document: procedure TFormOff.BtnWordClick(Sender: TObject); begin WordDocument1.Activate; // insert title WordDocument1.Range.Text := ‘American Capitals from ‘ + Table1.TableName; WordDocument1.Range.Font.Size := 14; This code follows the typical while loop, which scans the database table and has the fol- lowing code inside: while not Table1.EOF do begin // send the two fields Using Office Programs Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com [...]... drawback of using Delphi components? The real problem is not that there are fewer Delphi components than ActiveX controls, but that if you buy a Delphi component, you’ll only be able to use it in Delphi and Borland C++Builder If you buy an ActiveX control, on the other hand, you’ll be able to use it in multiple development environments from multiple vendors Even so, if you develop mainly in Delphi and find... corresponding Pascal file generated by Delphi Here is a small excerpt of the IAXForm1 interface from the XF1Lib.pas file, with some comments I’ve added: type IAXForm1 = interface(IDispatch) [‘{5 166 1AA1 -9 468 -11D0 -98 D0-444553540000}’] // Get and Set methods for TForm properties function Get_Caption: WideString; safecall; procedure Set_Caption(const Value: WideString); safecall; // TForm methods redeclared... Delphi environment You can see an example of its use in Figure 20. 19 Notice in this figure the effect of Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com ActiveX in Web Pages 897 the sunken border This is controlled by the AxBorderStyle property of the active form, one of the few properties of active forms that is not available for a plain form FIGURE 20. 19: The ActiveX timer installed in a Delphi. .. Controls 885 Using ActiveX Controls in Delphi Delphi comes with some preinstalled ActiveX controls, and you can buy and install more third-party ActiveX controls easily After this description of how ActiveX controls work in general, I’ll demonstrate one in an example The Delphi installation process is very simple Select Component ➢ Import ActiveX Control in the Delphi menu This opens the Import ActiveX... control You start from an existing VCL component, which must be a TWinControl descendant, and Delphi wraps an ActiveX around it During this step, Delphi adds a type library to the control (Wrapping an ActiveX control around a Delphi component is exactly the opposite of what we did to use an ActiveX inside Delphi. ) • You can create an ActiveForm, place several controls inside it, and ship the entire... see the library generated for our arrow control in Delphi s type-library editor in Figure 20. 16 From the type library information, the Wizard also generates an import file with the definition of an interface, the dispinterface, and other types and constants Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com Writing ActiveX Controls 8 89 FIGURE 20. 16: The type-library editor with the type library of... TMdWArrowX.Get_FillColor: Integer; begin Result := ColorToRGB (FDelphiControl.Brush.Color); end; procedure TMdWArrowX.Set_FillColor(Value: Integer); begin FDelphiControl.Brush.Color := Value; end; If you now install this ActiveX control in Delphi once more, the two new properties will appear The only problem with this property is that Delphi uses a plain integer editor, making it quite difficult to... 20.18 shows an example (If you’ve already installed the ActiveX control in Delphi, you should uninstall it prior to rebuilding it This process might also require closing and reopening Delphi itself.) FIGURE 20.18: The XArrow ActiveX control and its property page, hosted by the Delphi environment ActiveForms As I’ve mentioned, Delphi provides an alternative to the use of the ActiveX Control Wizard to... ActiveX and Delphi concepts of events and methods ActiveX Controls Versus Delphi Components Before I show you how to use and write ActiveX controls in Delphi, let’s go over some of the technical differences between the two kinds of controls ActiveX controls are DLL-based This means that when you use them, you need to distribute their code (the OCX file) along with the application using them In Delphi, ... 2000 version of the server component while installing Delphi) procedure TFormOff.BtnWordClick(Sender: TObject); var RangeW: Word97.Range; v1: Variant; ov1: OleVariant; Row1: Word97.Row; begin // code above RangeW := WordDocument1.Content; v1 := RangeW; v1.ConvertToTable ( #9, 19, 2); Row1 := WordDocument1.Tables.Item(1).Rows.Get_First; Row1.Range.Bold := 1; Row1.Range.Font.Size := 30; Row1.Range.InsertParagraphAfter; . next page Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com 8 69 TABLE 20.1 continued: OLE and Delphi Data Types OLE Type Delphi Type double Double float Single GUID GUID int SYSINT long Integer LPSTR. TObject); var RangeW: Word97.Range; v1: Variant; ov1: OleVariant; Row1: Word97.Row; begin // code above RangeW := WordDocument1.Content; v1 := RangeW; v1.ConvertToTable ( #9, 19, 2); Row1 := WordDocument1.Tables.Item(1).Rows.Get_First; Row1.Range.Bold. types, available in the type-library editor, to the corresponding Delphi types. TABLE 20.1: OLE and Delphi Data Types OLE Type Delphi Type BSTR WideString byte ShortInt CURRENCY Currency DATE

Ngày đăng: 12/08/2014, 21:20

Từ khóa liên quan

Tài liệu cùng người dùng

Tài liệu liên quan