Reputation: 40205
How can I create a COM DLL (class library) in Delphi?
It is for use on very old PCs where .NET is not, and will not be, installed and will replace, and slightly extend, a VB 6 DLL for which I have the source code.
Upvotes: 2
Views: 7805
Reputation: 12584
Here you have how to do it:
An Introduction to COM Programming with Delphi So, what is an Interface? An interface is the heart of COM technology. When I use the word interface, do not confuse it with the visual display that the user interacts with. It is actually a fairly new reserved word incorporated into Delphi. An Interface is little more than group of abstract methods used to manipulate an object.
We established during the last lesson that an interface is not a class; it is an interface. Therefore, we must declare it as such. Let us explore some interface characteristics.
Now that we know more about what an interface can and cannot do, let us look at what it is like to declare an interface.
IMyInterface = interface
['{676C8DA0-D8B3-11D4-BDE0-00A024BAF736}']
procedure SomeUnimplementedMethod;
end;
On GUIDs I am sure you are wondering what the big string of crazy characters are. It is called a GUID and it stands for Globally Unique Identifier. Some people pronounce it as "gwid" or as "Gooey I.D.". Here some characteristics of a GUID:
Okay, but how do I implement an interface? That is a good question. The answer is: You cannot implement an interface directly. You must create a class that will implement your interface. Refer to the coding example shown below.
unit InterfaceUnit;
interface
uses
Windows, Messages, SysUtils, Classes,
Graphics, Controls, Forms, Dialogs, StdCtrls;
type
IMyInterface = interface
['{6675C5C0-D95C-11D4-BDE0-00A024BAF736}']
procedure DisplaySomething;
end;
TMyClass = class(TInterfacedObject, IMyInterface)
procedure DisplaySomething;
end;
TMyForm = class(TForm)
TestBtn: TButton;
procedure TestBtnClick(Sender: TObject);
end;
var
MyForm: TMyForm;
implementation
{$R *.DFM}
procedure TMyForm.TestBtnClick(Sender: TObject);
var
//Declare a TMyClass object
MyClass : TMyClass;
//Declare a IMyInterface Object
MyInterface : IMyInterface;
begin
//Create an interface pointing
//to a TMyClass object
MyInterface := TMyClass.Create;
//Call TMyClass'es DisplaySomething
//using our interface
MyInterface.DisplaySomething;
end;
{ TMyClass }
procedure TMyClass.DisplaySomething;
begin
ShowMessage('MyInterface was created and
will now fall out of scope!');
end;
end.
The coding example listed above is about as simple as it can get while still having some type of visual functionality. Let's take a closer look at procedure TextBtnClick. Is there something missing? If you haven?t notice, we are not explicitly freeing the interface. This would appear to be a memory leak, but appearances are deceiving. As soon as the interface goes out of scope, Delphi will actually free the interface for you automatically! Interface's declared within a procedure or function will naturally fall out of scope when the procedure ends. Interface's declared within a class or are declared globally will naturally fall out of scope when the object is freed or the program ends.
Now we need to discuss Reference Counting. Every time you retrieve an interface to an object, e.g. IMyInterface := MyClass; Delphi will automatically increment its reference count using IUnknown's _AddRef function. When an interface falls out of scope, Delphi automatically calls IUnknown's ._Release function. If you haven't figured it out yet, IUnkown is an interface as well, and since we already know that an interface cannot implement the methods it defines, then you may be wondering where AddRef and Release come from. The answer is TInterfacedObject! What in the world is TInterfacedObject? TInterfacedObject and IUnknown are defined in system.pas. Before I can elaborate on this, we need to at least take a look at the code:
SYSTEM.PAS Declaration of IUnkown Declaration of TInterfacedObject and its implementation
IUnknown = interface
['{00000000-0000-0000-C000-000000000046}']
function QueryInterface
(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
end;
TInterfacedObject = class(TObject, IUnknown)
protected
FRefCount: Integer;
function QueryInterface
(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
procedure BeforeDestruction; override;
property RefCount: Integer read FRefCount;
end;
procedure TInterfacedObject.BeforeDestruction;
begin
if RefCount <> 0 then Error(reInvalidPtr);
end;
function TInterfacedObject.QueryInterface
(const IID: TGUID; out Obj): HResult;
const
E_NOINTERFACE = $80004002;
begin
if GetInterface(IID, Obj) then
Result := 0 else Result := E_NOINTERFACE;
end;
function TInterfacedObject._AddRef: Integer;
begin
Result := InterlockedIncrement(FRefCount);
end;
function TInterfacedObject._Release: Integer;
begin
Result := InterlockedDecrement(FRefCount);
if Result = 0 then Destroy;
end;
The code may look a little scary, but we are going to walk through this step-by-step since it is crucial that you understand reference counting in order to effectively program in COM. It is obvious from the code above that TInterfacedObject implements the methods defined in IUnkown. When you create a class that uses TInterfacedObject, you are essentially telling that class that it is going to be reference counted if an interface is assigned to it.
Let us look at simple piece of code so that I can describe the process that takes place.
TMyInterfaceClass = class(TInterfacedObject, IMyInterface)
{blah blah blah}
{blah blah blah}
procedure DoThis;
var
MyClass : TMyInterfaceClass;
MyInterface : IMyInterface;
begin
MyClass := TMyInterfaceClass.Create;
MyInterface := MyClass
end;
When you use Direct Assignment to assign MyInterface to MyClass, the method _AddRef gets called automatically by Delphi. This says to MyClass, "Hey, an interface is referencing you! Were going to increment your reference count." If you were to change the aforementioned code to the following:
TMyInterfaceClass = class(TInterfacedObject, IMyInterface)
{blah blah blah}
{blah blah blah}
procedure DoThis;
var
MyClass : TMyInterfaceClass;
MyInterface : IMyInterface;
MyInterface2: IMyInterface;
begin
MyClass := TMyInterfaceClass.Create;
MyInterface := MyClass;
MyInterface2 := MyClass
end;
Notice that I added a new variable called MyInterface2. Since we are now setting 2 different interfaces equal to MyClass, guess what happens to the Myclass?s reference count? It becomes 2 because we now have two interfaces assigned to it!
You may be wondering about the _Release method that is implemented by TInterfacedObject. The _Release method automatically gets called for each interface assigned to a class that falls out of scope. Obviously, at the end of procedure DoThis, the program execution is going to return whence it came, therefore, your local class and interface declarations fall out of the scope in which they were declared. Since we have two interfaces defined, _Release will get called how many times? The answer is a bright shiny "2"! And once our reference count hits zero, then MyClass is automatically freed by Delphi. How nice and clean!
Upvotes: 5
Reputation: 6866
Go through this site which is good start point to develop COM in Delphi. It is having tutorials, articles, and code on COM technologies in Delphi. It might be helpful to you
Upvotes: 3
Reputation: 139010
You create a new ActiveX Library. You find it in the ActiveX tab. (File - New - Other - ActiveX - ActiveX Library)
Upvotes: 6