stdcall required on import function? - delphi

I'm curious about this:
When I import a function from a dynamic link library that exports using stdcall calling convention, is it mandatory to add stdcall before external in Delphi starting from version 7?
i.e.
LIBNAME int __stdcall Foo(void);
as
function Foo: Integer; stdcall; external 'libname.dll';
or I can get away with just
function Foo: Integer; external 'libname.dll';
IIRC by default it will use stdcall but I'm not 100% sure on this, your opinion is required.
EDIT:
The question is related to 32bit library, Arnaud Bouchez has made a good point that for 64bit, the calling convention is not taken into consideration since there's only one.

If you omit stdcall then the default calling convention register will be used. So you must include that stdcall.
The fact that you are using external does not change anything. The default calling convention is register, even for external imports.
Of course, this only matters when compiling for 32 bit. On x64 Windows there is a single calling convention specified in the ABI. On x64 Windows, all calling conventions specified in code are ignored and all function calls are made with the Windows x64 calling convention.

Related

Sending TStringList between different Delphi versions

I´m migrating my Delphi 5 source code to Delphi 10 Berlin. I´ve got many DLLs in my project which export functions. These functions are called from other DLLs. There are two DLLs which I can not migrate to Delphi 10 but I still want to use them in my program.
Here an example:
function DoSomething( aList: TStringList ): Boolean; external 'Delphi5.dll';
I want to call "DoSomething" from my Delphi 10 Project. But the problem is, that TStringList in Delphi 5 is not compatible to TStringList in Delphi 10 Berlin (unicode). It would work, when DoSomething would have a parameter like "aString: AnsiString" because AnsiString is compatible to "string" in Delphi 5.
Is there a way to send a List between these two Delphi-Versions? Perhaps a TList or something else? Of course I could send a AnsiString with a separator between the strings to simulate a list, but I want a clean solution, because I´ve got many of these export-functions.
Thanks!
One should NEVER pass an object reference from an EXE to a DLL if it is meant to be used inside the DLL, or vice versa. An object reference can safely be passed to a DLL only if all the DLL does is pass the object back to the EXE (ro vice versa), such as through a callback function.
As you experienced, an object reference is not valid if the EXE and DLL aren't compiled with the same version of Delphi. Even if they are compiled with the same version, I suspect some compiler options could make them incompatible ({$Align} comes to mind, though I have never verified it). And even then, some incompatibilities might still occur (such as "Cannot assign TStringList to TStringList" errors due to RTTI mismatches).
Something that could fix your issue with minimal changes to your code would be to change the declaration of your functions to pass an interface to the DLL, and create a wrapper around TStringList that supports that interface. Said interface would need to support all the functionality you need from TStringList.
function DoSomething( aList: IStringList ): Boolean
Interfaces can be passed between DLL/EXE without most of the problems related to the object reference (as long as they use the exact same interface definition when they are compiled). (Edit: You still need to ensure the data passed to the interface's method are safe to pass to/from a DLL.)
That said, the interface should explicitly use AnsiString use a null-terminated PAnsiChar, or even a WideString (which can safely be sent to/from DLL - Reference).
function DoSomething( aListText: PAnsiChar ): Boolean
function DoSomething( aListText: WideString ): Boolean
Do not use String, which is AnsiString in Delphi 5 but is UnicodeString is Delphi 10. And don't use AnsiString, as it is not compatible between Delphi 5 and Delphi 10 due to internal structure differences.

How to create default parameters for DLL procedures (stdcall)?

I have made a DLL which exports several functions (with stdcall).
I want to have some of them accept parameters or not. So a lazy programmer can just call it without any parameters.
I read somewhere on a forum that default parameters don't work in DLL-s. Is my only option is creating 2 functions with different names, like:
procedure DoSomething();
begin
DoSomethingParams(1, 'Hi');
end;
procedure DoSomethingParams(one: Integer; two: PChar);
begin
//
end;
? Or maybe there is a more elegant way to achieve this?
Default parameters can be used with DLLs. But the default parameters must be declared when the function is imported rather than when it is exported. That's because default parameters are implemented at the call site. The caller detects that parameters are missing and generates code to supply the missing parameters.
So you can use default parameters when you import the DLL, provided that the language that consumes the DLL supports that.
In the DLL code, export the function. You can specify default parameters there if you wish, but it won't have any effect for the consumer of the DLL.
In the code that imports the DLL function, declare your default parameters. It is the default values declared at this point that matter.
Since DLLs are typically used to provide language neutral interfaces, and since some languages do not support default parameters, it is rare to use them in DLL interfaces.

The "local" directive in Delphi

I was sitting around debugging some code and I stumbled across this line in SysUtils.pas:
procedure ConvertError(ResString: PResStringRec); local;
What does the local keyword do exactly? It seems the ConvertError function is not declared in the interface section of the file, is this just a clarification that the function is indeed local, or is there a practical benefit to using this directive beyond that?
It dates back to the Linux compiler, Kylix. Here's what I can see in my Delphi 6 language guide, page 9-4:
The directive local, which marks routines as unavailable for export, is platform-specific and has no effect in Windows programming.
On Linux, the local directive provides a slight performance optimization for routines that are compiled into a library, but are not exported. The directive can be specified for standalone procedures and functions, but not for methods. A routine declared with local—for example.
function Contraband(I: Integer): Integer; local;
—does not refresh the EBX register and hence
cannot be exported from a library.
cannot be declared in the interface section of a unit.
cannot have its address take or be assigned to a procedural-type variable.
if it is a pure assembler routine, cannot be called from a another unit unless the caller sets up EBX.

Memory management for a Delphi plugin framework based on TInterfacedClass

For a server-side plugin framework I would like to implement DLLs which expose a RegisterPlugin method that returns a class reference (TInterfacedClass).
The host application then creates the instance(s) of this class, and instances will run in the context of the host thread(s). (This is different for example from the Jedi VCL plugin framework which instantiates the plugin in the DLL or BPL and returns the instance to the host.)
First tests showed no problems so far. However, are there hidden problems with memory management I should be aware of? As I am using Delphi 2009 for this project, FastMM4 is the default memory manager.
Here a sketch of the plugin DLL project:
library ExamplePlugin;
uses
...
type
TPluginOne = class(TInterfacedObject, ...)
...
end;
function RegisterPlugin: TInterfacedClass; stdcall;
begin
Result := TPluginOne;
end;
exports
RegisterPlugin;
{ TPluginOne }
// ... plugin class implementation
begin
end.
No problems with memory manager, because FastMM works as a shared memory manager between the EXE and the DLL. But I'm really not comfortable with the concept of passing pure objects, or (worst) metaclasses between the DLL and the EXE. The problem is, TInterfacedObject from the EXE is not the same as TInterfacedObject from the DLL! Sure, they might look exactly the same, but they're not! And if you ever upgrade the version of Delphi for the EXE or for any of the DLL's, you'll need to rebuild everything (thus losing whatever advantage you gained from implementing a Plugin framework).
A much more portable solution would be to return a "Factory Interface", something along the lines of:
IFactoryInterface = interface
[GUId-goes-here]
function MakeWhateverInterfaceYouNeed: IUnknownDerivate
end;
then export a function with this signature:
function RegisterPlugin: IFactoryInterface;
Your code is incomplete but from what you have included there is one obvious flaw.
You appear to be exporting a class (TInterfacedClass) from a DLL. This is going to cause problems when clients try to consume your class with a different version of Delphi. What's more it's going to leave them helpless if they want to write plug-ins in a different language.
Personally I'd go for a COM based interface which will allow plug-in authors to create plug-ins in any language. This is in fact the very problem that COM was invented to solve.
If you are happy to be constrained to using the same compiler for plug-ins and host app, and you prefer to expose classes to COM interfaces, then you need to make sure that all deallocation is performed with the same memory manager as allocated the memory. The simplest way is to use ShareMem and then you'll be safe.
UPDATE
Cosmin points out in a comment another flaw with exporting classes across module boundaries. It's basically something that you shouldn't do. COM was designed for this very purpose and it still should be the first choice for you. Delphi interfaces which are COM compatible so you can get the same benefits of binary interoperability without having to create servers, register CLSID's etc.
I think your plug-in should look like this:
library ExamplePlugin;
type
TPluginOne = class(TInterfacedObject, IPlugin)
[GUID]
public
constructor Create(const Host: THostApp);
end;
function RegisterPlugin(const Host: IHostApp): IPlugin; stdcall;
begin
Result := TPluginOne.Create(Host);
end;
exports
RegisterPlugin;
begin
end.

Which version of IMalloc should I use in Delphi?

I'm trying to refactor a Delphi 5 project in Delphi XE, to do that I need to fix some errors in a unit called BrowseDr. The error I'm getting is
[DCC Error] BrowseDr.pas(1033): E2033 Types of actual and formal var parameters
must be identical
line 1033: SHGetMalloc(FShellMalloc);
"MyShlObj":
//SHGetMalloc declaration
function SHGetMalloc(var ppMalloc: IMalloc): HResult; stdcall;
Now the IMalloc used in the declaration of FShellMalloc is derived from a OLE2.IMalloc
while the one used in "MyShlObj" is from ActiveX.IMalloc.
Is it possible alter one of them?
If yes, is it recommended?
The OLE2 unit was used by older Delphi versions, this unit was replaced by the ActiveX unit, so now you must use the ActiveX types in your new project.
The only alteration I think will work is to make sure that FShellMalloc is derived from the same interface as the one used from MyShlObj.
Modify either one to use the same IMalloc as the other.
Check your uses clause. A lot of third party code uses compiler directives to load the correct unit. Changing Compiler version will often result in the wrong file being loaded.
DFS is a classic example. Go to DFS.INC and ensure an appropriate DFS_COMPILER_xxx is defined.

Resources