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;
Related
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.
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.
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.
I am just starting to get more familiar with how interfaces work so bear with me if this is a trivial question.
I have two plugins (call them A and B) in the form of DLLs (not packages). There is an interface with a GUID declared in application that loads the DLL, call it IMyInterface. Both plugins see the same interface definition with the same GUID. Plugin B actually implements the interface.
Plugin A wants to find out if plugin B supports the interface IMyInterface. I use obj.GetInterface (IMyInterface, IObj) to find this out:
var IObj : IMyInterface;
obj : TObject;
obj := getPluginObjReference;
if obj.GetInterface(IMyInterface, IObj) then
showmessage ('Interface Supported');
If I call this code within plugin B, the answer is yes, which is as expected. If I use the same code (cut and paste) in plugin A, the same code claims that plugin B does not support that interface. When I trace the GetInterface call into system.pas I find that InterfaceEntry := GetInterfaceEntry(IID); return nil, hence no interface to find.
For reference, IMyInterface looks like:
IMyInterface = interface
['{277A3122-A3F2-4A14-AE56-C99230F31CE9}']
function getModel : AnsiString;
function getDescription : AnsiString;
end;
and the implementation looks like:
// Now the real class, this is private to this plugin
TModelAPI = class (TInterfacedObject, IMyInterface)
function getModel : AnsiString;
function getDescription : AnsiString;
end;
etc.
My question:
As expected plugin B rightly claims that IMyInterface is supported. Why is it that Plugin A cannot discover that plugin B supports IMyInterface? Is there a problem with interrogating interfaces across DLL boundaries?
You cannot reliably pass objects across DLL boundaries. Instead you should pass interfaces across the boundary and use as or Supports to query capabilities. Interfaces are designed for binary compatibility across DLL boundaries, but objects are not.
You can readily pass an IInterface from one DLL to another and then query that. Or, if you have a common interface that all plugin objects implement, you could pass that. All that matters is that you always pass interfaces and never pass objects.
You should really use only interfaces, ie the getPluginObjReference should return the lowest common interface supported by all the plugins and then you use Supports() function to test what interfaces (plugin versions) that particular plugin supports.
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.