I need to use a DLL (Hardware ID Extractor) made in Delphi 7 in my C# application.
The functions exported by this DLL are:
Exported functions:
// CPU
function GetCPUSpeed: Double;
function CPUFamily: ShortString; { Get cpu identifier from the windows registry }
function GetCpuTheoreticSpeed: Integer; { Get cpu speed (in MHz) }
function IsCPUIDAvailable: Boolean; Register;
function GetCPUID (CpuCore: byte): ShortString;
Function GetCPUVendor: ShortString;
// RAM
function MemoryStatus (MemType: Integer): cardinal; { in Bytes }
function MemoryStatus_MB (MemType: Integer): ShortString; { in MB }
// HDD
function GetPartitionID (Partition : PChar): ShortString; { Get the ID of the specified patition. Example of parameter: 'C:' }
function GetIDESerialNumber(DriveNumber: Byte ): PChar; { DriveNr is from 0 to 4 }
I know (obviously) that string in Delphi are not null terminated and are byte (ASCII). But I have no clue on how to map these Delphi string to C#.
Thanks.
Here's an example of how you could declare the GetCPUSpeed function in C#:
And there are the other declarations you could try:
If you have the source of the D7 dll, you can change the exported function to make the ShortString function to return PChar. You can create new functions that call the original ones and do the typecast - that's the easiest path. If you follow this path do the same to the Integer and Cardinal types (cast them to LongInt and LongWord, respectively). Some code below (I imagine that mages like mr Hausladen have a more elegant approach, but this works :-) )
This works because internally ShortString is a array [0..255] of Char. I don't have an D2009 at hand to see if the SizeOf(char) must be changed to SizeOf(AnsiChar). BUT the principle is the same: allocate the PAnsiChar, get the Pointer to the first the char of the ShortString and do Copy (in this case I've done with CopyMemory) to the PAnsiChar. And put the null in its' place.
Or you can go the mghie's way and create a wrapper and do the casts there.
The problem comes from the way you designed your exported functions. DLLs are (among other things) a mechanism to provide code in a way that it can be used from applications (or other DLLs) written in different programming languages.
Have a look at the Windows API, there are lots of functions returning text. Unfortunately this is done in a lot of different ways, the Windows API has no real standard for it. However, I'm quite sure that you will not find a single function that returns a "string" (either a Pascal or a C (null-terminated) string) in the way you did. The reason is simple: This would make it very difficult or even impossible to use different programming languages for different modules. Your DLL would allocate the memory for the string, and once the caller is done with the string it would need to free the memory. Thus both modules need to share the manager for this slice of memory. How could the C# program use your DLL, seeing that they use completely different memory managers?
The customary way to get a string value from a DLL is to call the function with a preallocated buffer and the buffer length in parameters, let the function fill that buffer with the string contents, and let the function result denote success or failure. Passing a buffer of insufficient size for example would be one cause of failure. Different Windows API functions deal with this in different ways, the easiest one for you would probably to define your maximum string length with a constant, similar to
MAX_PATH
for example.To solve your problem you should take one Windows API function that you know how to call from C#, and which returns a string result in a buffer, as the example. Model your exported functions after this example, and it should be easy to call them from the C# program.
Edit:
I remembered that I'd seen a similar question (How to import a function from a DLL made in Delphi?) some time ago, and now that I looked I found it to be yours as well.
You wrote then that you don't have the source code for that DLL, so changing the exported functions is out of the question. What you could do is create a wrapper DLL (or COM object) in Delphi, call the original
HardwareIDExtractor.dll
and provide a sane API on top of it. That would allow you to provide both AnsiChar and WideChar versions of the exported functions at the same time.If you want to reduce the clutter (two DLLs necessary instead of one) you could also put the original
HardwareIDExtractor.dll
as a resource into your wrapper DLL, as described in this blog posting: Using DLLs stored as Resources in Delphi programs