[ Pobierz całość w formacie PDF ]
.This is the threading model generally used for ActiveX controls inside Inter-net Explorer.The Free model, or multi-threaded Apartment indicates that the client has norestrictions, which means that multiple threads can use the same object at the same time.For this reason, every method of every object must protect itself and the nonlocal data ituses against multiple simultaneous calls.This threading model is more complex for aContinued on next pageCopyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com2874c19.qxd 7/2/01 4:42 PM Page 830830 Chapter 19 " COM Programmingserver to support than the Single and Apartment models, because even access to theobject s own instance data must be handled with thread-safe care.The fourth option, Both indicates that the server object supports both the Apartmentmodel and the Free model.The final option, Neutral was introduced in Windows 2000 and is available onlyunder COM+.It indicates that multiple clients can call the object on different threads atthe same time, but COM guarantees you that the same method is not invoked twice atthe same time.Guarding for concurrent access to the data of the objet is required.UnderCOM, it is mapped to the Apartment model.Initializing the COM ObjectIf you look back at the definition of the TComObject class, you will notice it has a nonvirtualconstructor.(Actually, it has multiple nonvirtual constructors, which I ve omitted from thelisting.) Each TComObject constructor calls the virtual Initialize method.For this reason, ifyou want to customize the creation of an object and then initialize it, you should not define anew constructor (which will never be called).What you should do is override its Initializemethod, as I ve done in the TNumber class.Here is the final version of this class:typeTNumber = class(TComObject, INumber)privatefValue: Integer;publicfunction GetValue: Integer; virtual; stdcall;procedure SetValue (New: Integer); virtual; stdcall;procedure Increase; virtual; stdcall;procedure Initialize; override;destructor Destroy; override;end;As you can see, I ve also overridden the destructor of the class, because I wanted to test theautomatic destruction of the COM objects provided by Delphi.Here is the code for thispseudoconstructor and the destructor:procedure TNumber.Initialize;begininherited;fValue := 10;end;Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com2874c19.qxd 7/2/01 4:42 PM Page 831A First COM Server 831destructor TNumber.Destroy;begininherited;MessageBox (0, Object Destroyed , TDLLNumber , mb_OK); // API callend;In the first method, calling the inherited version is good practice, even though theTComObject.Initialize method has no code in this version of Delphi.The destructor,instead, must call the base class version.This is the code required to make our COM objectwork properly and to let us know when an object is actually destroyed.Testing the COM ServerNow that we ve finished writing our COM server object, we can register and use it.Simplycompile its code and then use the Run ¢' Register ActiveX Server menu command in Delphi.You do this to register the server on your own machine, with the results you can see inFigure 19.3.FI GURE 19.3:The new registered serverin Windows RegEditWhen you distribute this server, you should install it on the client computers.To accom-plish this, you can write a REG file to install the server in the Registry.However, this is notreally the best approach, because the server already includes a function you can activate toregister the server.This function can be activated by the Delphi environment, as we ve seen,or in a few other ways:" You can pass the COM server DLL as a command-line parameter to Microsoft sRegSvr32.exe program, found in the \Windows\System directory." You can use the similar TRegSvr.exedemo program that ships with Delphi.(The com-piled version is in the \Bin directory, and its source code is in the \Demos\ActiveXdirectory.)" You can let an installation builder program call the registration function of the server.Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com2874c19.qxd 7/2/01 4:42 PM Page 832832 Chapter 19 " COM ProgrammingHaving registered the server, we can now turn to the client side of our example.This time,the example is called TestCom and is stored in a separate directory.In fact, the program loads theserver DLL through the OLE/COM mechanism, thanks to the server information present inthe Registry, so it s not necessary for the client to know which directory the server resides in.The form displayed by this program is very similar to the one we ve used to test the objectinside the DLL.In the client program, you must include the source code file with the inter-face and redeclare the COM server GUID.Of course, the code of the program s FormCreatemethod should be updated to create the required COM objects.The program starts with allthe buttons disabled (at design time), and it enables them only after an object has been created.This way, if an exception is raised while creating one of the objects, the buttons related to theobject won t be enabled:procedure TForm1.FormCreate(Sender: TObject);begin// create first objectNum1 := CreateComObject (Class_Number) as INumber;Num1.SetValue (SpinEdit1.Value);Label1.Caption := Num1: + IntToStr (Num1.GetValue);Button1.Enabled := True;Button2.Enabled := True;// create second objectNum2 := CreateComObject (Class_Number) as INumber;Label2.Caption := Num2: + IntToStr (Num2.GetValue);Button3.Enabled := True;Button4.Enabled := True;end;Notice in particular the call to CreateComObject and the following as cast.The API callstarts the COM object-construction mechanism I ve already described in detail.This callalso dynamically loads the server DLL.The return value is an IUnknown object.This objectmust be converted to the proper interface type before assigning it to the Num1 and Num2 fields,which now have the interface type INumber as their data type:typeTForm1 = class(TForm).privateNum1, Num2 : INumber;Copyright ©2001 SYBEX, Inc., Alameda, CA www.sybex.com2874c19.qxd 7/2/01 4:42 PM Page 833A First COM Server 833WARNINGTo downcast an interface to the actual type, always use the as cast, which for interfaces per-forms a QueryInterface call behind the scenes.This provides some protection, because itraises an exception if the interface you are casting to is not supported by the given object.Inthe case of interfaces, the as cast is the only way to extract an interface from another inter-face.If you write a plain cast of the form INumber(CreateComObject(Class_Number)), theprogram will crash, even if the cast seems to make sense, as in the case above.Casting aninterface pointer to another interface pointer is an error.Period.Never do it.In Figure 19.4, you can see the output of this test program, which is very similar to theprevious version.Notice that this time, Num2 shows the initial value of the object at start-up,as set up in its Initializemethod.Notice also that I ve added one more button, which createsa third temporary COM object:procedure TForm1 [ Pobierz caÅ‚ość w formacie PDF ]