in order to call the same hashpassword.dll binary as a dll from a win32 application, we have to declare the function from the assembly using the stdcall calling convention and with the external keyword. although the .net assembly does not use the stdcall calling convention, the inverse p/invoke will require that the functions hashpasswordmd5 and hashpasswordsha1 are declared with the stdcall calling convention (otherwise you'll get garbage results).
program win32client;
{$apptype console}
type
tpasswordformat = (sha1, md5);
function hashpasswordmd5(password: pchar): pchar; stdcall;
external 'hashpassword.dll';
function hashpasswordsha1(password: pchar): pchar; stdcall;
external 'hashpassword.dll';
var
passwd: string;
begin
write('password: ');
readln(passwd);
writeln('['+passwd+']');
write('md5: ');
writeln(hashpasswordmd5(pchar(passwd)));
write('sha1: ');
writeln(hashpasswordsha1(pchar(passwd)));
end.
this is a neat way to expose .net functionality to good-old win32 applications written in delphi 7.
summary
in this article, i've tried to explain what makes a .net assembly different from a win32 dll, how we can use them in delphi 8 for .net, and what the different ways are to make them yourself in delphi 8 for .net.
i've explained that package assemblies should be used to build your own .net assemblies in delphi 8 for .net, and library assemblies should be used and marked unsafe to build .net assemblies that can be used by both .net and win32 applications.
we can sum it up as follows: libraries are an oddity in .net, while packages are the "natural" thing for .net. this is exactly the opposite of win32.
the recommandation from the delphi r&d team is to build .net assemblies using the delphi package syntax, and link against it using the .dcpil. for delphi "round-tripping" (using delphi generated assemblies from delphi code), always use package syntax. library syntax was included in delphi 8 for .net only for the purpose of producing dlls with unmanaged entry points (callable from d7 win32 code), and for asp.net (a topic for another time).
finally, danny thorpe reminded me to be careful of relying on implementation details such as hashpassword.unit.hashpasswordmd5. the way the compiler exports delphi global procedures in metadata is very likely to change in future releases. the same is true for any implementation detail of how delphi language features are implemented at the il level that are not directly supported by clr.
the best way to avoid relying on details like hashpassword.unit.hashpasswordmd5 is to implement hashpasswordmd5 as a class static method of a delphi class. this is the normal clr way of doing things and eliminates the question of how delphi exposes non-clr things (like global procs) to clr.
references