Putting classes in a DLL? - delphi

Is it possible to put some classes into a DLL?
I have several custom classes in a project I am working on and would like to have them put in a DLL and then accessed in the main application when needed, plus if they are in a DLL I can reuse these classes in other projects if I need to.
I found this link: http://www.delphipages.com/forum/showthread.php?t=84394 which discusses accessing classes in a DLL and it mentions delegating to a class-type property but I could not find any further information on this in the Delphi help or online.
Is there any reason I should not put classes in a DLL, and if it is ok is there a better way of doing it then in the example from the link above?
Thanks

It is not possible to get a Class/Instance from a DLL.
Instead of the class you can hand over an interface to the class.
Below you find a simple example
// The Interface-Deklaration for Main and DLL
unit StringFunctions_IntfU;
interface
type
IStringFunctions = interface
['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
function CopyStr( const AStr : WideString; Index, Count : Integer ) : WideString;
end;
implementation
end.
The simple DLL
library StringFunctions;
uses
StringFunctions_IntfU; // use Interface-Deklaration
{$R *.res}
type
TStringFunctions = class( TInterfacedObject, IStringFunctions )
protected
function CopyStr( const AStr : WideString; Index : Integer; Count : Integer ) : WideString;
end;
{ TStringFunctions }
function TStringFunctions.CopyStr( const AStr : WideString; Index, Count : Integer ) : WideString;
begin
Result := Copy( AStr, Index, Count );
end;
function GetStringFunctions : IStringFunctions; stdcall; export;
begin
Result := TStringFunctions.Create;
end;
exports
GetStringFunctions;
begin
end.
And now the simple Main Program
uses
StringFunctions_IntfU; // use Interface-Deklaration
// Static link to external function
function GetStringFunctions : IStringFunctions; stdcall; external 'StringFunctions.dll' name 'GetStringFunctions';
procedure TMainView.Button1Click( Sender : TObject );
begin
Label1.Caption := GetStringFunctions.CopyStr( Edit1.Text, 1, 5 );
end;

Use runtime packages for this purpose; it's exactly what they're designed for in the first place. They get loaded automatically (or can be loaded manually), and automatically set up the sharing of the same memory manager so you can freely use classes and types between them.
You're much better off using packages (which is exactly what the IDE does for much of its functionality for that very reason).

Delphi does not support either importing or exporting classes from DLLs. To import a class from another module, you need to use packages.

While the official answer is "you can't", anything is possible of course. Frameworks like Remobjects SDK and Remobjects Hydra has been doing this for a long time. The problem is that it requires you to create an infrastructure around such a system, which is not something Delphi deals with out of the box.
The first step is memory management. A DLL is injected into the process loading it, but it does not share memory management. It has to be this way since a DLL can be created in a myriad of languages, each with their own internal mechanisms. This poses a problem with safety (i.e program writing into DLL memory and visa versa).
Secondly, interface (read: content description). How will your application know what classes it can create, class members, parameter types and so on. This is why COM requires type-libraries, which describe the content of a DLL.
Third, life-time management. If memory management for the objects created from a DLL is handled by the DLL, the DLL must also release said objects.
The above steps already exists and it's called COM. You are of course free to create as many COM DLL files as you please, just remember that these have to be registered with Windows before you use them. Either "on the fly" by your application (if you have the security rights to do so) or by your installer.
This is why COM, while representing the ultimate plugin system, is rarely used by Delphi programmers, because the technical cost of using it as a plugin system outweighs the benefits.
The alternative way
If you can assure that your DLL's are only to be used by Delphi programs, then you have a second way to explore. You have to create methods to share the memory manager of your main program with the DLL (Remobjects does this). This allows you to share objects, strings and more between the DLL and the main application.
You can then use RTTI to "map" classes stored in the DLL (the DLL must do this and generate both class and method tables) which can be invoked through a proxy class you device yourself.
All in all, unless you have plenty of free time to waste, I would either buy a system like Remobjects Hydra - or stick with packages. But can it be done another way? Of course it can. But at the cost of time and hard work.

Related

Create com object from dll manually

I'm using a code that calls a CoCreateInstance with a GUID to create an object (a scripter)
Is it possible to create the same COM object without using the GUID, by loading manually the DLL? if yes, how to find the DLL, how to know the name of the function creating the object ?
No.
One COM Server, be it DLL or EXE or something, usually hosts the implementations for many different, while interconnected, interfaces.
So you will find the DLL of your server, but you would still have to tell the server which of its many GUIDs it should materialize for you.
See DllGetClassObject entry point http://msdn.microsoft.com/en-us/library/windows/desktop/ms680760.aspx
To find the COM server EXE or DLL check the registry keys like HKEY_CLASSES_ROOT\CLSID\{00000108-0000-0010-8000-00AA006D2EA4}\InprocServer32
Then, your specific library may duplicate their primary COM API with the additional non-COM API. Then that non-COM API would be the one you can use in the way you describe. If it provides extra means for it.
However in terms of generic COM standard it is not possible, COM is built exactly around GUIDs.
To create an object instance straight from the DLL, provided you know it's exact path, this code should work:
type
T_DGCO = function(const CLSID, IID: TGUID; var Obj): HResult; stdcall; //DllGetClassObject
var
p: T_DGCO;
f: IClassFactory;
x: IMyObject; //replace by an interface of choice
begin
p := GetProcAddress(LoadLibrary(FullPathToDLL), 'DllGetClassObject');
Win32Check(p <> nil);
OleCheck(p(CLASS_MyObject, IClassFactory, f));
OleCheck(f.CreateInstance(nil, IMyObject, x));
x.Hello('World'); //or whatever your object does
end;

What is the correct way to export an object in Delphi? [duplicate]

Is it possible to put some classes into a DLL?
I have several custom classes in a project I am working on and would like to have them put in a DLL and then accessed in the main application when needed, plus if they are in a DLL I can reuse these classes in other projects if I need to.
I found this link: http://www.delphipages.com/forum/showthread.php?t=84394 which discusses accessing classes in a DLL and it mentions delegating to a class-type property but I could not find any further information on this in the Delphi help or online.
Is there any reason I should not put classes in a DLL, and if it is ok is there a better way of doing it then in the example from the link above?
Thanks
It is not possible to get a Class/Instance from a DLL.
Instead of the class you can hand over an interface to the class.
Below you find a simple example
// The Interface-Deklaration for Main and DLL
unit StringFunctions_IntfU;
interface
type
IStringFunctions = interface
['{240B567B-E619-48E4-8CDA-F6A722F44A71}']
function CopyStr( const AStr : WideString; Index, Count : Integer ) : WideString;
end;
implementation
end.
The simple DLL
library StringFunctions;
uses
StringFunctions_IntfU; // use Interface-Deklaration
{$R *.res}
type
TStringFunctions = class( TInterfacedObject, IStringFunctions )
protected
function CopyStr( const AStr : WideString; Index : Integer; Count : Integer ) : WideString;
end;
{ TStringFunctions }
function TStringFunctions.CopyStr( const AStr : WideString; Index, Count : Integer ) : WideString;
begin
Result := Copy( AStr, Index, Count );
end;
function GetStringFunctions : IStringFunctions; stdcall; export;
begin
Result := TStringFunctions.Create;
end;
exports
GetStringFunctions;
begin
end.
And now the simple Main Program
uses
StringFunctions_IntfU; // use Interface-Deklaration
// Static link to external function
function GetStringFunctions : IStringFunctions; stdcall; external 'StringFunctions.dll' name 'GetStringFunctions';
procedure TMainView.Button1Click( Sender : TObject );
begin
Label1.Caption := GetStringFunctions.CopyStr( Edit1.Text, 1, 5 );
end;
Use runtime packages for this purpose; it's exactly what they're designed for in the first place. They get loaded automatically (or can be loaded manually), and automatically set up the sharing of the same memory manager so you can freely use classes and types between them.
You're much better off using packages (which is exactly what the IDE does for much of its functionality for that very reason).
Delphi does not support either importing or exporting classes from DLLs. To import a class from another module, you need to use packages.
While the official answer is "you can't", anything is possible of course. Frameworks like Remobjects SDK and Remobjects Hydra has been doing this for a long time. The problem is that it requires you to create an infrastructure around such a system, which is not something Delphi deals with out of the box.
The first step is memory management. A DLL is injected into the process loading it, but it does not share memory management. It has to be this way since a DLL can be created in a myriad of languages, each with their own internal mechanisms. This poses a problem with safety (i.e program writing into DLL memory and visa versa).
Secondly, interface (read: content description). How will your application know what classes it can create, class members, parameter types and so on. This is why COM requires type-libraries, which describe the content of a DLL.
Third, life-time management. If memory management for the objects created from a DLL is handled by the DLL, the DLL must also release said objects.
The above steps already exists and it's called COM. You are of course free to create as many COM DLL files as you please, just remember that these have to be registered with Windows before you use them. Either "on the fly" by your application (if you have the security rights to do so) or by your installer.
This is why COM, while representing the ultimate plugin system, is rarely used by Delphi programmers, because the technical cost of using it as a plugin system outweighs the benefits.
The alternative way
If you can assure that your DLL's are only to be used by Delphi programs, then you have a second way to explore. You have to create methods to share the memory manager of your main program with the DLL (Remobjects does this). This allows you to share objects, strings and more between the DLL and the main application.
You can then use RTTI to "map" classes stored in the DLL (the DLL must do this and generate both class and method tables) which can be invoked through a proxy class you device yourself.
All in all, unless you have plenty of free time to waste, I would either buy a system like Remobjects Hydra - or stick with packages. But can it be done another way? Of course it can. But at the cost of time and hard work.

How can I check that the dll that holds the ActiveX library that I use is signed with my certificate?

I know how to check the signing of an executable or dll by location from this question: Checking digital signature programmatically from Delphi
How can I know that an ActiveX library that I am using is signed with my certificate?
The executable can check the dll if it knows its location, but I want to be very sure that it is the one the executable is using at that moment. I know I can use the registry to find the library dll location (from the object IDs or library ID), but this seems like a weak spot vulnerable to spoofing.
Background:
I created an ActiveX library with an automation object. I sign the library dll and the consuming application with the same certificate. I can already check the consumer application from the library as follows
TSomeAutomationObj = class(TAutoObject, ISomeAutomationObj)
public
procedure Initialize; override;
end;
procedure TSomeAutomationObj.Initialize;
const
BufferSize = 2048;
var
LProcessPath: PChar;
begin
LProcessPath := StrAlloc(BufferSize);
try
GetModuleFileName(0, LProcessPath, BufferSize);
//Check signature of LProcessPath Executable as described here https://stackoverflow.com/questions/5993877/checking-digital-signature-programmatically-from-delphi
finally
StrDispose(LProcessPath);
end;
end;
initialization
TAutoObjectFactory.Create(ComServer, TSomeAutomationObj, Class_SomeAutomationObj,
ciMultiInstance, tmApartment);
What remains now is the check in the other direction (Executable to dll).
The automation objects will be registered and I will be using the Automation Object as follows
uses
LibraryThatHoldsAutomationObject_TLB;
TObjectWithApplicationLifetime = class
private
FSomeAutoObj : ISomeAutomationObj;
public
Constructor Create;
end;
Constructor TObjectWithApplicationLifetime.Create;
begin
FSomeAutoObj := CoSomeAutomationObj.Create;
// Check that the source library of this object is signed with my certificate
// If so, then use FSomeAutoObj else set it to nil, set a flag or prevent usage other ways
end;
A similar problem was covered in another question last year. The technique involves fetching the address of the interface's VMT (or vtable) and then asking the OS which module owns that memory.
An interface reference is a pointer to some data for the object. The first bytes of that data are in turn a pointer to the VMT for the interface.
var
VMT: Pointer;
Information: TMemoryBasicInformation;
DLL: HModule;
VMT := PPointer(FSomeAutoObj)^;
Win32Check(VirtualQueryEx(GetCurrentProcess, VMT, Information, SizeOf(Information)) = SizeOf(Information));
DLL := HModule(Information.AllocationBase);
When that code is finished, DLL should hold the handle of the DLL that holds the object that implements the interface. Call GetModuleFileName on it as in the question.
There are a few assumptions required for it to work:
It must be an in-process COM object; for an out-of-process object, the VMT will be that of the proxy, not the real object.
The object mustn't be behind any other sort of proxy, such as one inserted by the compiler. (I don't think Delphi does this, but I'm not sure. Just make sure the interface pointer you have was provided by the DLL and not by the RTL.)
The VMT of the interface needs to by static. This will be the case for most interfaces implemented in Delphi or C++, but interfaces implemented in other ways, such as with scripting languages, might have method tables allocated on the heap. In that case, the DLL variable above won't really hold a module handle.
Another assumption is that even if the DLL isn't signed by your required certificate, it's still trustworthy enough to load into memory in the first place. You're only testing the DLL after you've loaded it into your process space and started executing its code. (Loading the DLL calls its DllMain function. Instantiating the COM object involves calling the DLL's DllGetClassObject function plus whatever else the constructor of the COM object decides to do.) If you can't trust improperly signed DLLs, then you're already too late.

Delphi ntextcat - how to implement

I am trying to access ntextcat dll from Delphi 2007.
Registering the dll´s failed (I have tried 32 and 64Bit regsvr32).
Importing the typelibrary in Delphi failed as well (as .dll and .Net).
Is there anybody out there who got access to ntextcat within Delphi?
This is a .net library. As such it is not readily accessible from unmanaged code such as Delphi. If you do want to consume it from Delphi you'll need to create a wrapper. For instance by using one of the following:
A COM interface.
A mixed mode C++/CLI assembly that wraps the managed library and exposes an unmanaged interface.
A wrapper based on Robert Giesecke's UnmanagedExports.
The latter two allow you to consume a .net assembly as a native DLL.
If the library has a rich and broad interface then the task of wrapping it if not going to be terribly easy or convenient. You may be better finding a different library that is easier to consume.
If the library has a small interface, or if you only need a small part of its capabilities, then making a wrapper may be more tractable.
Based on David´s answer (thank you for pointing me in the right direction) this the solution using Robert Giesecke's UnmanagedExports:
C# (be aware to set the platform target to the same as your Delphi application targets to! Do not use "AnyTarget"!)
using RGiesecke.DllExport;
using System.Runtime.InteropServices;
namespace ClassLibrary1
{
public class Class1
{
[DllExport("testfunc", CallingConvention = CallingConvention.StdCall)]
public static int anytest(int iInt) {
return iInt;
}
}
}
The Delphi source:
Type
Ttest = function(const iInt: Integer): Integer; stdcall;
procedure TForm1.btTestClick(Sender: TObject);
var
Handle: Integer;
test : Ttest;
begin
Handle := LoadLibrary(<PathToDLL>ClassLibrary1.dll');
if Handle <> 0 then begin
#test := GetProcAddress(Handle, 'testfunc');
if #test <> NIL then ShowMessage(IntToStr(test(1234)));
end;
end;
Based on this it is possible to access the ntextcat .net library.

When a class implements a descendant interface, why doesn't it automatically count as implementing the base interface?

What's the reason this won't compile?
type
IInterfaceA = interface ['{44F93616-0161-4912-9D63-3E8AA140CA0D}']
procedure DoA;
end;
IInterfaceB = interface(IInterfaceA) ['{80CB6D35-E12F-462A-AAA9-E7C0F6FE0982}']
procedure DoB;
end;
TImplementsAB = class(TSingletonImplementation, IInterfaceB)
procedure DoA;
procedure DoB;
end;
var
ImplementsAB: TImplementsAB;
InterfaceA: IInterfaceA;
InterfaceB: IInterfaceB;
begin
ImplementsAB := TImplementsAB.Create;
InterfaceA := ImplementsAB; >> incompatible types
...
end
In contrast this is how I make it work:
InterfaceA := ImplementsAB as InterfaceB;
or
InterfaceA := InterfaceB;
I mean, if IInterfaceB inherits from IInterfaceA and TImplementsAB implements IInterfaceB, it wouldn't be logical to also implement IInterfaceA and be type compatible?
This so because early OLE/COM had a bug and Borland decided to be compatible with it. This is mentioned in this article: New Delphi language feature: Multiple inheritance for interfaces in Delphi for .NET. The solution is to list all ancestor interfaces explicitly in the class as Mikael wrote.
Some quotes from the linked article:
The problem was in COM itself. To load a module, COM would load the DLL, GetProcAddress on a well-known entry point that was supposed to be exported from the DLL, call the DLL function to obtain an IUnknown interface, and then QueryInterface for IClassFactory. The problem was, when Microsoft added support for IClassFactory2, they added the QueryInterface for IClassFactory2 after the existing code that queried for IClassFactory. IClassFactory2 would only be requested if the query for IClassFactory failed.
Thus, COM would never request IClassFactory2 on any COM server that implemented both IClassFactory2 and IClassFactory.
This bug existed in COM for a long time. Microsoft said that they couldn't fix the COM loader with an OS service pack because both Word and Excel (at the time) relied on the buggy behavior. Regardless of whether it's fixed in the latest releases of COM or not, Borland has to provide some way to preserve this behavior in Win32 Delphi for the forseeable future. Suddenly adding all ancestors into an implementing class that weren't there before is very likely to break existing code that unintentionally falls into the same pattern as the COM loader.
Another way to make it work is to include both interfaces in the class declaration.
TImplementsAB = class(TSingletonImplementation, IInterfaceA, IInterfaceB)
procedure DoA;
procedure DoB;
end;
I guess this is what is required for the compiler to realize that TImplementsAB implements both IInterfaceA and IInterfaceB.

Resources