Delphi - using C DLL with int* parameter - delphi

I'm having trouble using a DLL function with an int* parameter.
DLL supplier information:
The function is declared in the DLL as:
int sendQuoGetInfDstn(char* nomed, int *rigd)
I have imported this into Delphi 11 using:
const
QUODLL = 'PcQuoDllNoWrap.dll';
implementation
function sendQuoGetInfDstn(Name: PAnsiChar; Count: PInteger): integer; stdcall; external QUODLL;
This compiles fine.
My question is, how do I call this function from my Delphi program?
I have tried all sorts of things, but I get Access violation errors or program crash.
For example, I have made this wrapper:
function TPCQuo.GetWorklistInfoTest(Name: String; Count: integer): integer;
begin
Result := sendQuoGetInfDstn(PAnsiChar(Ansistring(Name)), #Count); {I have also tried PInteger(Count)}
end;
And I call the wrapper like this:
procedure TForm1.Button4Click(Sender: TObject);
var
name: String;
count: integer;
begin
if QUO.GetWorklistInfoTest(name, count) <> 0 then
ShowMessage('No worklist available ')
else
ShowMessage('Worklist available ' + name + ' number of lines: ' + count.ToString );
end;
So, how should I call this function?

There are a few possible issues here:
The function may be cdecl rather than stdcall. It's not possible to say for sure but you'd need to look more closely at the supplied header file.
The first argument char *nomed might be expecting a modifiable character array. Perhaps the function does attempt to modify the text, or perhaps the author made a mistake and should have written const char *nomed. With the information provided here we cannot tell.
The encoding of the text could be ANSI, or it could be some other 8-bit encoding such as UTF-8. With the information provided here we cannot tell what it is.
The second argument, int *rigd, could be either a pointer to a single integer, or it could be a pointer to an array. With the information provided here we cannot tell.
In summary, the only potential mistake in your translation is item 1. But getting the arguments, types and calling convention correct is only the first step. You also need to know the semantics of how to call a function. My best guess is that the issues are in that area, which will require information that you have, but we do not.

Related

E2250 There is no overloaded version of 'StrPas' that can be called with these arguments

My code :
function ThisModuleName: Char; //bulo String
var
p: array [0..512] of char;
fileNamePart: pchar;
begin
GetModuleFileName(HInstance, #p[0], 512);
GetFullPathName(#p[0], 512, #p[0], fileNamePart);
result := StrPas(WideString(#p[0])); //stalo WideString
end;
In Delphi 7 compiles.
In Delphi 10.2 it gives an error:
[dcc32 Error] verinfo.pas(98): E2250 There is no overloaded version of 'StrPas' that can be called with these arguments
This code is wrong on all Delphi versions. I doubt it compiles anywhere. I'm guessing that the code you presented is not the Delphi 7 code, but rather the code after you've hacked at it for a while.
The return type should be string and not char. Furthermore, the cast to WideString is bogus. Finally, a zero-based array of characters can be treated as PChar.
Your function should be translated like so:
function ThisModuleName: string;
var
p: array [0..511] of Char;
fileNamePart: PChar;
begin
GetModuleFileName(HInstance, p, Length(p));
GetFullPathName(p, Length(p), p, fileNamePart);
Result := p;
end;
Having said all of that, while this is a faithful translation of the code in the question, it does not return a module name. I really don't know what your code is trying to do, but the call to GetFullPathName appears to be wrong in your code.
My guess is that you are trying to convert potential short 8.3 file names to long names. I believe that you need an extra buffer to make that work. Here's what that code looks like, with some error checking added:
function ThisModuleName: string;
var
ModuleFileName, Buffer: array [0..511] of Char;
FilePart: PChar;
begin
Win32Check(GetModuleFileName(HInstance, ModuleFileName, Length(ModuleFileName))<>0);
Win32Check(GetFullPathName(ModuleFileName, Length(Buffer), Buffer, FilePart)<>0);
Result := Buffer;
end;
Instead of asking a question for every problem you encounter in your porting project it might pay dividends to learn a bit more about Unicode Delphi.
Instead of calling the API directly, you can call System.SysUtils.GetModuleName, which simply returns a string.
It wraps GetModuleFilename, and by doing so it also shows how to call that function. I hope I'm allowed to quote a couple of lines from the unit mentioned above. It also uses the MAX_PATH constant, which contains the maximum length of a path.
Note that GetModuleFilename already returns a fully qualified path, so calling GetFullPathName afterwards is redundant.
function GetModuleName(Module: HMODULE): string;
var
ModName: array[0..MAX_PATH] of Char;
begin
SetString(Result, ModName, GetModuleFileName(Module, ModName, Length(ModName)));
end;
This is mainly useful if you want the path of a dll, if you're interested in the main executable, you can simply use Application.ExeName.

E2276 error when attempting to export to .DLL a function overloaded across units

I've been gathering useful routines into a utilities unit which I then compile into a .DLL and a .DCU, so I can choose which method is convenient to access those routines.
For instance, I've written my own Lowcase which acts on [wide]chars or [wide]strings to implement the obvious function, bizarrely not implemented by Embarcadero.
Similarly, I've extended Max and Min to find Max/Min of an array of numerics.
In some cases, I've essentially simply provided an alias to an existing function, such as
function LowCase
(const st : ANSIstring
) : ANSIstring;
stdcall;
begin
result := ansilowercase(st);
end;
which would be exported to the DLL by
exports LowCase
(const st : ANSIstring
) name 'lowcaseansistr';
So far, so good.
I recently found that
var
v_uint64 : uint64;
v_uint64 := $FFFFFFFFFFFFFFFF;
writeln('v_uint64=',inttostr(v_uint64));
v_uint64 := $7FFFFFFFFFFFFFFF;
writeln('v_uint64=',inttostr(v_uint64));
produced an incorrect result in the first instance (-1) - in fact, wherever MSB is set, but is fine in the second case where MSB is clear.
So I found uinttostr and substituting this standard function for inttostr solved the issue.
ISTM it would be far more convenient if I could simply perform the alias trick again, so I tried (implementation)
function inttostr
(p_nb_int : uint64
) : string;
stdcall;
begin
result := uinttostr(p_nb_int);
end;
(interface)
function inttostr
(p_nb_int : uint64
) : string;
overload;
stdcall;
Which worked quite happily, but implementing
exports inttostr
(p_nb_int : uint64
) name 'uinttostr64';
in the project generated E2276 Identifier 'IntToStr' cannot be exported. E2276 seems to be related to local directive, apparently invoked by local; in the declaration - but in the source provided by EMBT in System.Sysutils.pas, the declaration is
function IntToStr(Value: Integer): string; overload;
function IntToStr(Value: Int64): string; overload;
No local; in sight!
So - I'm stumped. How do I get around this? (XE2)
Here's a test program:
program countparamsshort;
{$APPTYPE CONSOLE}
{$R *.res}
uses
dxutypes,
// dxumethods,
system.math,windows,vcl.Graphics,classes,System.SysUtils;
//{$I C:\delphi\dxu.inc}
var
v_int64 : int64 ; // -2^63..2^63-1
v_uint64 : uint64; // 0..2^64-1
procedure showv;
begin
writeln('v_int64= ',inttostr(v_int64));
writeln('v_uint64=',inttostr(v_uint64));
end;
begin
writeln('min values');
v_int64 := -214748364999;
v_uint64 := 0;
showv;
writeln('max values');
v_int64 := $FFFFFFFFFFFFFFFF;
v_uint64 := $FFFFFFFFFFFFFFFF;
showv;
writeln('max 63-bit values');
v_int64 := $7FFFFFFFFFFFFFFF;
v_uint64 := $7FFFFFFFFFFFFFFF;
showv;
// writeln(inttostr(max(10,3)),' & ',inttostr(max([1,12,7,9,11,4])));
writeln('procedure finished');
readln;
end.
Running as-is with the .dcu dxumethods and include file C:\delphi\dxu.inc commented-out invokes the system.math implementation of inttostr with the resultant report
min values
v_int64= -214748364999
v_uint64=0
max values
v_int64= -1
v_uint64=-1
max 63-bit values
v_int64= 9223372036854775807
v_uint64=9223372036854775807
procedure finished
Which is NOT correct for v_uint64. Invoking uinttostr for a uint64 would cure the problem BUT that means remembering to do so.
Removing the comment from the dxumethods element means that my version of inttostr is included - which actually executes uinttostr. Results are:
min values
v_int64= -214748364999
v_uint64=0
max values
v_int64= -1
v_uint64=18446744073709551615
max 63-bit values
v_int64= 9223372036854775807
v_uint64=9223372036854775807
10 & 12
procedure finished
where the value of v_uint64 is now displayed correctly. This means that I can use inttostr and can forget all about uinttostr, so I don't have to sit here figuring out which I should use when. I've figured it out once, and now the compiler can do the work in future.
I've also un-commented-out the MAX call which uses the same methodology to allow me to find the max of an array - it means I can forget all about maxintvalue and maxvalue (it invokes those routines) but once again, I can simply use max - whether the traditional version or the same concept with regard to arrays. Don't clutter up the mind with three routines (and another 3 for the MIN equivalents) when one will do.
Finally, re-commenting dxumethods and un-commenting the include file, which contains lines like
function inttostr
(p_nb_int : uint64
) : string;
overload;
stdcall;
external 'dxuproject.dll' name 'uinttostr64';
produced identical results, this time from the .DLL - the key being to qualify the function-name when constructing the .DLL else you trip over the E2276 error.
Now - WHY this occurs, when similar machinations around MAX/MIN don't faze the compiler, I'll leave to the theoreticians - and what it has to do with 'local' declarations which seem to be a Kylix hangover - well, that's in the laps (or perhaps lapse) of the gods AFAICS.
Anyhow - good outcome; problem solved. Season's Greetings...
Perhaps when it comes to overloading functions and name mangling Delphi compiler cannot tell Int64 from Uint64 parameters. So try to precisely specify which function you meant to export without relying upon auto-resolving.
exports UnitName.FunctionName(Params);
I cannot quite understand your overall goal. The only thing that could import such a function is Delphi code built with the same compiler as the DLL. And that code could simply call UIntToStr directly.
And you also seem to be re-implementing functions that the RTL already has. For instance, the AnsiStrings unit will supply your case switching functions for 8 bit text. And in Math you will find MinValue/MaxValue for arrays of floating point values, and MinIntValue/MaxIntValue for arrays of integers. It does look to me as though you are re-inventing the wheel.
That said, taking your question at face value, here is how you export the function:
library Project1;
uses
SysUtils;
exports
UIntToStr(Value: UInt64) name 'uinttostr64';
begin
end.
This exports the function from SysUtils and so uses the register calling convention. That's no problem to you of course since you can only call the function from Delphi.
If you are desperate to export your IntToStr, then you should use a fully qualified unit name. For instance, suppose the function is declared in Unit1, then you would write:
exports
Unit1.IntToStr(Value: UInt64) name 'uinttostr64';

What is exact signature of System.Assign (for use in procedural expression)?

Unfortunately, exact Assign signature is not available in the RTL source, and my attempts to guess, like:
const
Assign: procedure (var F; const FileName: string) = System.Assign;
{ or }
Assign: function (var F; const FileName: string): Integer = System.Assign;
{ also I tried "internal" one from _AssignFile }
didn't yield any positive results, and compiler refuses to treat this contant expression as procedural and complains about right-value (E2029 '(' expected but ';' found)).
So, which type should I use to match Delphi RTL exactly?
Assign is a language remnant of Delphi tracing its origins to the original Turbo Pascal syntax. Long before function overloading was added to the language syntax, long before the RTL's text file and "file of type" internal data structures were documented, there was Assign.
Assign is a simple enough procedure to use, but when you look at what it has to do you'll see that it's pretty much impossible to implement without some sort of compiler magic. File of TFoo creates a file type that is distinct and incompatible with File of Integer. And yet, both can be passed as the first parameter to Assign.
Today, you could probably implement Assign using a generic type param for the file parameter. The generic type would conform to the type of the variable passed in. That's great and all, but we needed a way to associate a typed file variable with a string filename 25 years before generics were added to the mix.
Assign is a compiler intrinsic function. That means it lives outside of the Delphi/Pascal syntax space. Assign is essentially a type conforming procedure, which can't be represented in the strongly typed Delphi/Pascal language (without modern and complex language extensions such as generic types). It's not typeless, and it's not dynamically typed because the actual parameter type is fully determined at compile time.
You can't take the address of the Assign function. Even if you could, because it is amorphous, you won't be able to define a procedure type that accurately represents all of its possible call signatures.
Compiler magic functions exist outside the language to take care of tasks that could not be easily represented in the syntax available at the time. Assign is one example. Writeln() is another.
System.Assign is an intrinsic function. The official documentation of it is completely hopeless. It says:
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
function Assign(var F: File; FileName: String; [CodePage: Word]): Integer; overload;
I can only guess as to why the documentation generator cannot cope with this function. But the three identical overloads are clearly bogus. And it's not a function, rather it is a procedure.
No matter. Because it is an intrinsic, you cannot assign it to a function pointer. The solution is to wrap it up in a function of your own.
procedure MyAssign(var F: File; const FileName: string);
begin
Result := System.Assign(F, FileName);
end;
You can then assign that function to a variable or constant of procedural type.
const
AssignProc: procedure(var F: File; const FileName: string) = MyAssign;
The other variant of Assign takes a third parameter that specifies the code page, passed as a Word. You can only call that function if the first argument to Assign is a TextFile.
So, intrinsics are really a law unto themselves.
Note that the documentation does state that Assign should no longer be used. Instead you should use AssignFile. The documentation is no better there mind you!
If to open System.pas file and read it, in XE2 you have those internal procedures:
function _AssignFile(var t: TFileRec; const s: PChar): Integer;
function _AssignText(var t: TTextRec; const s: PChar; const CP: word): Integer;
And i don't think you have any global stable procedure w/o underscore in names - those listed are Delphi compiler internals, like invisible procedures, that are covertly called when you declare string or dynarray variable.
http://en.wikipedia.org/wiki/Intrinsic_function
Like a lot of procedures like _Write0LString which are implementing usual WriteLn calls.
So you would have to make a wrapper function that would call AssignFile and put reference to your wrapper into your variable instead;
However you did not put tag with your Delphi version, and different vesions may have different declarations for intrinsics. So look into sources of your particular Delphi.
Also, why would you need it ? I remember i did such a thing in TurboPascal 5.5 to make string-based file - make WriteLn act like C sprintf. But why would you need it in modern Delphi, when you can use TStream instead?

Delphi Unicode and Console

I am writing a C# application that interfaces with a pair of hardware sensors. Unfortunately the only interface that is exposed on the devices requires a provided dll written in Delphi.
I am writing a Delphi executable wrapper that takes calls the necessary functions for the DLL and returns the sensor data over stout. However, the return type of this data is a PWideChar (or PChar) and I have been unable to convert it to ansi for printing on command line.
If I directly pass the data to WriteLn, I get '?' for each character. If I look through the array of characters and attempt to print them one at a time with an Ansi Conversion, only a few of the characters print (they do confirm the data though) and they will often print out of order. (printing with the index exposed simply jumps around.) I also tried converting the PWideChar's to integer straight: 'I' corresponds to 21321. I could potentially figure out all the conversions, but some of the data has a multitude of values.
I am unsure of what version of Delphi the dll uses, but I believe it is 4. Definately prior to 7.
Any help is appreciated!
TLDR: Need to convert UTF-16 PWideChar to AnsiString for printing.
Example application:
program SensorReadout;
{$APPTYPE CONSOLE}
{$R *.res}
uses
Windows,
SysUtils,
dllFuncUnit in 'dllFuncUnit.pas'; //This is my dll interface.
var state: integer;
answer: PChar;
I: integer;
J: integer;
output: string;
ch: char;
begin
try
for I := 0 to 9 do
begin
answer:= GetDeviceChannelInfo_HSI(1, Ord('a'), I, state); //DLL Function, returns a PChar with the results. See example in comments.
if state = HSI_NO_ERRORCODE then
begin
output:= '';
for J := 0 to length(answer) do
begin
ch:= answer[J]; //This copies the char. Originally had an AnsiChar convert here.
output:= output + ch;
end;
WriteLn(output);
end;
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn(I);
end.`
The issue was PAnsiChar needed to be the return type of the function sourced from the DLL.
To convert PWideChar to AnsiString:
function WideCharToAnsiString(P: PWideChar): AnsiString;
begin
Result := P;
end;
The code converts from UTF-16, null-terminated PWideChar to AnsiString. If you are getting question marks in the output then either your input is not UTF-16, or it contains characters that cannot be encoded in your ANSI codepage.
My guess is that what is actually happening is that your Delphi DLL was created with a pre-Unicode Delphi and so uses ANSI text. But now you are trying to link to it from a post-Unicode Delphi where PChar has a different meaning. I'm sure Rob explained this to you in your other question. So you can simply fix it by declaring your DLL import to return PAnsiChar rather than PChar. Like this:
function GetDeviceChannelInfo_HSI(PortNumber, Address, ChNumber: Integer;
var State: Integer): PAnsiChar; stdcall; external DLL_FILENAME;
And when you have done this you can assign to a string variable in a similar vein as I describe above.
What you need to absorb is that in older versions of Delphi, PChar is an alias for PAnsiChar. In modern Delphi it is an alias for PWideChar. That mismatch would explain everything that you report.
It does occur to me that writing a Delphi wrapper to the DLL and communicating via stdout with your C# app is a very roundabout approach. I'd just p/invoke the DLL directly from the C# code. You seem to think that this is not possible, but it is quite simple.
[DllImport(#"mydll.dll")]
static extern IntPtr GetDeviceChannelInfo_HSI(
int PortNumber,
int Address,
int ChNumber,
ref int State
);
Call the function like this:
IntPtr ptr = GetDeviceChannelInfo_HSI(Port, Addr, Channel, ref State);
If the function returns a UTF-16 string (which seems doubtful) then you can convert the IntPtr like this:
string str = Marshal.PtrToStringUni(ptr);
Or if it is actually an ANSI string which seems quite likely to me then you do it like this:
string str = Marshal.PtrToStringAnsi(ptr);
And then of course you'll want to call into your DLL to deallocate the string pointer that was returned to you, assuming it was allocated on the heap.
Changed my mind on the comment - I'll make it an answer:)
According to that code if "state" is a code <> HSI_NO_ERRORCODE and there is no exception then it will write the uninitialised string "output" to the console. Which could be anything including accidentally showing "S" and "4" and a series of 1 or more question marks
type of answer(variable) is PChar. use length function good for string variable.
use strlen instead of length.
for J := 0 to StrLen(answer)-1 do
also accessible range of PChar(char *) is 0..n-1
To convert UTF-16 PWideChar to AnsiString you can use simple cast:
var
WStr: WideString;
pWStr: PWideString;
AStr: AnsiString;
begin
WStr := 'test';
pWStr := PWideString(WStr);
AStr := AnsiString(WideString(pWStr));
end;

How do I stop this Variant memory leak?

I'm using an old script engine that's no longer supported by its creators, and having some trouble with memory leaks. It uses a function written in ASM to call from scripts into Delphi functions, and returns the result as an integer then passes that integer as an untyped parameter to another procedure that translates it into the correct type.
This works fine for most things, but when the return type of the Delphi function was Variant, it leaks memory because the variant is never getting disposed of. Does anyone know how I can take an untyped parameter containing a variant and ensure that it will be disposed of properly? This will probably involve some inline assembly.
procedure ConvertVariant(var input; var output: variant);
begin
output := variant(input);
asm
//what do I put here? Input is still held in EAX at this point.
end;
end;
EDIT: Responding to Rob Kennedy's question in comments:
AnsiString conversion works like this:
procedure VarFromString2(var s : AnsiString; var v : Variant);
begin
v := s;
s := '';
end;
procedure StringToVar(var p; var v : Variant);
begin
asm
call VarFromString2
end;
end;
That works fine and doesn't produce memory leaks. When I try to do the same thing with a variant as the input parameter, and assign the original Null on the second procedure, the memory leaks still happen.
The variants mostly contain strings--the script in question is used to generate XML--and they got there by assigning a Delphi string to a variant in the Delphi function that this script is calling. (Changing the return type of the function wouldn't work in this case.)
Have you tried the same trick as with the string, except that with a Variant, you should put UnAssigned instead of Null to free it, like you did s := ''; for the string.
And by the way, one of the only reasons I can think of that requires to explicitly free the strings, Variants, etc... is when using some ThreadVar.

Resources