I have to access several functions of a DLL written in c from Delphi (currently Delphi7).
I can do it without problems when the parameters are scalar
(thanks to the examples found in this great site!), but I have been stuck for some time when in the parameters there is a pointer to an array of Longs.
This is the definition in the header file of one of the functions:
BOOL __stdcall BdcValida (HANDLE h, LPLONG opcl);
(opcl is an array of longs)
And this is a portion of my Delphi code:
type
TListaOpciones= array of LongInt; //I tried with static array too!
Popcion = ^LongInt; //tried with integer, Cardinal, word...
var
dllFunction: function(h:tHandle; opciones:Popcion):boolean;stdcall;
arrayOPciones:TListaOpciones;
resultado:boolean;
begin
.....
I give values ββto aHandle and array arrayOPciones
.....
resultado:=dllFunction(aHandle, #arrayopciones[0]);
end;
The error message when executing it is:
"Project xxx raised too many consecutive exceptions: access violation
at 0x000 .."
What is the equivalent in Delhpi for LPLONG? Or am I calling the function in an incorrect way?
Thank you!
LONG maps to Longint, and LPLONG maps to ^Longint. So, you have translated that type correctly.
You have translated BOOL incorrectly though. It should be BOOL or LongBool in Delphi. You can use either, the former is an alias for the latter.
Your error lies in code or detail we can't see. Perhaps you didn't allocate an array. Perhaps the array is incorrectly sized. Perhaps the handle is not valid. Perhaps earlier calls to the DLL failed to check for errors.
Related
Good day,
I have problem. I want to simulate some errors in hacklang.
<?hh
namespace Exsys\HHVM;
class HHVMFacade{
private $vector = Vector {1,2,3};
public function echoProduct() : Vector<string>{
return $this->vector;
}
public function test(Vector<string> $vector) : void{
var_dump($vector);
}
}
Function echoProduct() returns Vector of strings. But private property $vector is Vector of integers. When I call echoFunction and returning value use as argument for function test(). I get
object(HH\Vector)#35357 (3) { [0]=> int(1) [1]=> int(2) [2]=> int(3) }
Why? I am expecting some error because types mismatch.
There's two things at play here:
Generics aren't reified, so the runtime has no information about them. This means the runtime is only checking that you're returning a Vector.
$this->vector itself isn't typed. This means the type checker (hh_client) treats it as a unknown type. Unknown types match against everything, so there's no problem returning an unknown type where a Vector<string> is expected.
This is to allow you to gradually type your code. Whenever a type isn't known, the type checker just assumes that the developer knows what's happening.
The first thing I'd do is change the file from partial mode to strict mode, which simply involves changing from <?hh to <?hh // strict. This causes the type checker to complain about any missing type information (as well as a couple of other things, like no superglobals and you can't call non-Hack code).
This produces the error:
test.hh:6:13,19: Please add a type hint (Naming[2001])
If you then type $vector as Vector<int> (private Vector<int> $vector), hh_client then produces:
test.hh:9:16,28: Invalid return type (Typing[4110])
test.hh:8:44,49: This is a string
test.hh:6:20,22: It is incompatible with an int
test.hh:8:44,49: Considering that this type argument is invariant with respect to Vector
Which is the error you expected. You can also get this error simply by adding the type to $vector, without switching to strict mode, though I prefer to write my Hack in the strongest mode that the code supports.
With more recent versions of HHVM, the type checker is called whenever Hack code is run (there's an INI flag to turn this off), so causing the type mismatch will also cause execution of the code to fail.
Currently need to use Delphi to write a DLL, so that the main program calls to the specified place in the removable disk in a file,
Main program designed to VC + +, so use Strut way to call the DLL's data as round!!
Current problems encountered when the main program calls my DLL, the incoming group A Record, and other functions has been dealt with, to return the group B Record,
But using Delphi written DLL, can receive group A Record, but returned group B, but always errors!!
The following is the code for the DLL function, would like to ask if anyone encountered such problems can help point to mention turned a deaf ear
Thanks! !
enter code here
library usbdll;
uses
Windows,
SysUtils,
Classes;
{$R *.res}
Type
p_fin=^TSfin;
TSfin = record //A group of Record is the main program calls incoming Record Type
ST_act:Integer;
pathlen:Integer;//Pass true path length, so that I can get to the far left pathlen, then to remove the rear garbled
Id_hand:Integer;
Id_tail:Integer;
path: PWideChar://The reason why the file path Pwidechar do use guidelines because another branch dll is passed to the main program main program <file path + unicode>, is behind the path and dragging a bunch of gibberish characters
Type
p_out=^TRfout;//B Record is set to return to the main program of the Record Type
TRfout= Record
ST_act:Integer;
ST_move:Integer;
Revis:Integer;
Crchk:Integer;
end;
//The following is my comment out.
// The use of the test in two ways, directly back to the group B Record, does not receive group A Record,A group that does not receive Record, when the main program a call, immediately return the relevant data, the results are normal.
(*
function RFoutEt(test:p_out):Boolean;stdcall; //ok Function writing mode
begin
test^.ST_act:=14;
test^.ST_move:=10;
test^.Revis:=12;
test^.Crchk:=8;end;exports RFoutEt;
procedure RFoutE(out Result:TRfout);cdecl; //ok Procedure writing mode
begin
Result.ST_act:=14;
Result.ST_move:=10;
Result.Revis:=12;
Result.Crchk:=8;end;exports RFoutEt;
*)
// Actually, I need to charge the main program to my group A Record datain order to deal with post-op, get really want to move the file to specify the true path,and ultimately return to group B Record.
function RFoutE(ap_sendin:p_fin;num:Integer):TRfout;stdcall; //error
var
str_tmp,str_tmp2,temi_diry:string;
i,copyNum:Integer;
arr: array[0..100] of Char;
begin
//Program by adding the following {} after paragraph, Result is not an empty value is displayed to access illegal address,causing abnormal program termination.
{
StrCopy(arr,Pchar(ap_sendin^.path));
repeat
str_tmp:=temi_diry;//Use the file path string char array A group referred to in the PWidechar removed
str_tmp2:=arr[i];
Inc(i);
until i>=ap_sendin.pathlen;
copyNum:=Prs_Filecopy(temi_diry;ap_sendin^.path);//A group of Record with associated data to complete the move of the specified file
}
Result.ST_act:=4;//The following four lines of words alone are able to return data
Result.ST_move:=0;
Result.Revis:=2;
Result.Crchk:=copyNum;end;
PS. Following is a test using VC + + to try more than one function is normal demand
struct Sfin{
int ST_act;
int pathlen;
int Id_hand;
int Id_tail;
wchar_t *path;
};
struct Rfout{
int ST_act;
int ST_move;
int Revis;
int Crchk;
};
Rfout RFoutE(struct Sfin *a, int num)
{
int ret = 1;
Rfout OutStruct;
copyNum = Prs_Filecopy(temi_diry, inAnow, Anow->path);
ret=1;
if(ret==1){
OutStruct.ST_act =14;
OutStruct.ST_move =10;
OutStruct.Revis = 12;
OutStruct.Crchk = 8;
Anow = freeA(Anow);
}
return OutStruct;
}
There is no standard ABI for larger than machine word sized return values. And Delphi uses a different ABI from any other compiler that I've encountered so you'll have no luck returning large records that way.
You'll need to return the record as an out parameter rather than a function return value. Once you make that change, all will be well.
It also looks like your C++ functions use cdecl rather than stdcall.
In Delphi, suppose I have a method with a (much simplified) signature like this:
procedure abc( const prop1:string; const arg1:TValue; const prop2:string;
out arg2:TValue );
I'm building a TList<PPropValPair> of records like this using the parameters provided:
type
TPVPType = (ptIn, ptOut);
PPropValPair = ^TPropValPair;
TPropValPair = record
io : TPVPType;
prop : string; // property name
iVal : TValue; // input value
oVar : Variant; // <-- how to save for later use??? Variant? TValue?
end;
(On the face of it, this example looks silly. Again, it's quite simplified just to communicate the problem.)
At run-time, I want to stuff all of the input values ival (where io=ptIn) into each public property 'prop' in a class, call a class method, then extract the values of all public properites 'prop' (where io=ptOut) into oVar.
The input side is working fine using RTTI.
However, I need to somehow save a REFERENCE to the output vars in oVar so I can save the value of the associated properties after the class method has been called.
I'm not assigning anything to arg2 directly. Rather, I'm saving a reference to arg2 and assigning the value indirectly later on.
The trick is ... I don't want to have to do any additional annotations of the output parameters in abc(...).
In C++, you can declare a parameter as a 'reference' by prepending it with '&'. So in C++ terms this might be defined roughly as:
procedure abc( arg1 : TValue; &arg2 : TValue );
Later, you can refer to &arg2 and it's using a POINTER to the object. But in calling the function, you just say:
abc( somevar1, somevar2 );
somevar1 is passed by value, and somevar2 is passed by reference. Inside the function, I can save somevar2 in a reference var, then later on assign a value to it via the pointer (if it's a string) by saying &arg2ref = 'abc'.
I'm guessing there's a way to do this in Delphi, either with a Variant as the oVar type, or using RTTI, or something else. I just haven't figured out the magic combination of pieces yet. (I just don't use pointers very often in Delphi.)
Maybe I need to save a raw pointer in oVar along with the type (say, oType), and cast a value through the pointer to save the property's value?
I'm hoping someone here might have some clear ideas.
BTW, I'm using Delphi XE3.
Use a pointer. It doesn't have to (and indeed shouldn't) be a "raw" pointer. Use a typed pointer, PValue. Pass in a PValue to your function, and then store that in oVal, which you should also declare a a PValue. Use # to create a pointer, and ^ to dereference.
I would not recommend passing arg2 by reference. Although you can still use # on it to get a pointer to the original variable passed to abc, the reference parameter disguises the fact that the variable needs to remain available indefinitely. Instead, declare arg2 as PValue so it's more obvious to the caller that indirection is involved.
// declaration
procedure abc(...; arg2: PValue);
// call
abc(..., #somevar2);
I've got some code that worked fine under Delphi 2007 but breaks under D2010. It involves passing in a string, converting it to a PWideChar (specifically, a WideString pointer, not a UnicodeString pointer), doing some processing, and then calling SysFreeString on it. It works fine until a blank string is passed in, then SysFreeString breaks. It calls a bunch of things that end up raising an Int 3 breakpoint inside NTDLL.DLL. Continuing past this point results in
Project raised exception class
$C0000005 with message 'access
violation at 0x7747206e: read of
address 0x539b8dba'.
Which, if you look closely, is not the standard Access Violation message.
The top of the stack trace when it hits the Int 3 looks like this:
:774e475d ; ntdll.dll
:774afad0 ; ntdll.dll
:774e5de9 ; ntdll.dll
:774a6dff ; ntdll.dll
:76fc1075 ; C:\Windows\system32\ole32.dll
:770e443a ; C:\Windows\system32\oleaut32.dll
:770e3ea3 oleaut32.SysFreeString + 0x4a
Does anyone have any idea what's going on here?
Edit (from the comments):
This isn't a WideString, though. It's
a PWideChar generated by
StringToOleStr, and there are no
double-free errors when a non-blank
string is passed in. Unfortunately, I
can't really post a code sample
because this is a third-party
component that's under copyright. (And
I can't ask them for support because
it's no longer supported. Basically,
the whole thing's one big mess.)
I'm going to try psychic debugging. You've got some kind of heap corruption in your application and SysFreeString is the unfortunate victim (it's hard to tell without OS symbols, you should probably install the MSFT symbol packages for your OS).
Try enabling application verifier (in particular pageheap) for your app and see if it crashes earlier.
It is hard to diagnose without seeing your actual code, however WideString automatically calls SysFreeString() when it goes out of scope. It sounds like your code may be making a second call to SysFreeString() on memory that has already been freed. WideString itself has not changed at all between D2007 and D2010, but other aspects of Delphi's string handling have. Maybe you are not managing the strings correctly. Can you please show your actual code?
A simple test shows that you need to be really careful on what you do in which order.
So: even though you cannot post a small example, can you indicate what you are doing in a bit more detail?
Bad debugging; ignore the things below; see comment.
The SysFreeString() is being called at the end of the the Allocate() call, even though it returns a PWideChar:
program ShowStringToOleStrBehaviourProject;
{$APPTYPE CONSOLE}
uses
SysUtils;
function Allocate(const Value: UnicodeString): PWideChar;
begin
Result := StringToOleStr(Value);
// implicit SysFreeString(WideChars);
end;
procedure Run;
var
WideChars: PWideChar;
begin
WideChars := Allocate('Foo');
Writeln(WideChars);
end;
begin
try
Run();
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
end.
Note the console still outputs 'Foo' because the memory has not been overwritten yet.
--jeroen
It can be different reasons of such kind of errors:
You try to free with SysFreeString a memory which are allocated not with SysAllocString, but for example with CoTaskMemAlloc.
You have heap correct.
Heap corruptions are difficult to localize. The function HeapSetInformation can be very helpful. For example you can use
HeapSetInformation(NULL,HeapEnableTerminationOnCorruption,NULL,0);
Other good way is usage of HeapValidate function. For example you can define a function which verify all heaps pf the process (code in C, which can be easy rewritten in Delphi):
BOOL MyHeapValidate (void)
{
HANDLE hProcessHeaps[1024];
DWORD i;
DWORD dwNumberOfHeaps;
BOOL bSuccess = FALSE;
dwNumberOfHeaps = GetProcessHeaps (sizeof(hProcessHeaps)/sizeof(hProcessHeaps[0]),
hProcessHeaps);
if (dwNumberOfHeaps > sizeof(hProcessHeaps)/sizeof(hProcessHeaps[0])) {
MessageBox(NULL, TEXT("GetProcessHeaps()"),
TEXT("Error in MyHeapValidate()"), MB_OK);
return FALSE;
}
for (i=0; i<dwNumberOfHeaps; i++) {
bSuccess = HeapValidate (hProcessHeaps[i], 0, NULL);
if (!bSuccess)
return bSuccess;
}
return bSuccess;
}
The usage of this function can be like following:
void BadFunction(BSTR bstr)
{
LPOLESTR psz = OLESTR("Test12");
lstrcpy (bstr, psz);
}
int main()
{
LPOLESTR psz = OLESTR("Test");
BSTR bstr = SysAllocString (psz);
// verify that before call of BadFunction() all process heaps are OK!
if (!MyHeapValidate()) {
_tprintf(TEXT("heap is corrupted after the step 1.\n"));
return 1;
}
BadFunction(bstr);
if (!MyHeapValidate()) {
_tprintf(TEXT("heap is corrupted after the step 1.\n"));
return 1;
}
SysFreeString (bstr);
return 0;
}
With respect of inserting MyHeapValidate() in different suspected places you can very quickly local the place of corruption.
+1 for Larry Osterman's answer.
Some Windows memory functions behave slightly different under debugger: if they detect some kind of misuse - they trigger breakpoint to notify debugger. So, basically, your code is doing something wrong.
You can install hooks on SysAllocString/SysFreeString and redirect them to your memory manager (which should be in full debug mode) to collect more info. Or you can just pass these calls through to original functions, installing only a filter, which watches for memory actions.
And you can install debug symbols to get more info too (I'm not sure if Delphi debugger can use it, but Process Explorer - can. You can connect it to your process and see call stack).
I tried to make my code as simple as possible,but I failed at it.
This is my code:
class function TWS.WinsockSend(s:integer;buffer:pointer;size:word):boolean;
begin
dwError := Send(s,buffer,size,0);
// Debug
if(dwError = SOCKET_ERROR) then
begin
dwError := WSAGetLastError;
CloseSocket(s);
WSACleanup;
case (dwerror) of
//Case statement
else
LogToFile('Unhandled error: ' + IntToStr(dwError) + ' generated by WSASend');
end;
Exit(false);
end;
// if the size of the bytes sent isn't the expected one.
while(dwError <> size) do
dwError:= dwError + Send(s,Ptr(cardinal(buffer) + dwError),size-dwError,0);
Exit(true);
end;
The error is placed at
dwError:= dwError + Send(s,Ptr(cardinal(buffer) + dwError),size-dwError,0);
Error is "Constant object cannot be passed as var parameter"
I understand I need a variable,but isn't there a way I can do it without adding one more line?
When the compiler complains about the way you're passing a parameter, the first thing you need to know is what the parameter expects. Therefore, you should go look at the declaration of Send. If looking at the declaration doesn't immediately give you an idea of what to fix, then you need to include that declaration with the code you post in your question.
I suspect that this actually has nothing to do with incrementing a pointer. Instead, the compiler is complaining about the third parameter, where you are trying to pass the expression size-dwError. I guess the parameter is declared like this:
var buffersize: Word;
The function plans on providing a new value for that parameter β that's what var means β so the thing you pass to that parameter needs to be something that can receive a value. You can't assign a new value to the result of subtracting two variables.
Take a closer look at where the compiler complained about that line. Didn't it place the cursor somewhere near the third parameter? That's a clue that the problem is there.
Decrement size, and then pass it to the function.
Dec(size, dwError);
Inc(dwError, Send(s, Ptr(cardinal(buffer) + dwError), size, 0));
Why do you care about adding another line? Have you reached your quota for the day? Lines are cheap; don't be afraid to use two to express yourself when one won't do. Likewise for variables. When your code doesn't work, saving a byte or two doesn't matter at all.
At the very least, you should have added more lines in order to track down the source of the problem. When you have one line of code that's performing several independent calculations (such as getting a new pointer value, getting a new size, and calling a function), break the line into several separate pieces. That way, if there's a problem with one of them and the compiler complains, you'll know exactly which one to blame.
Correct, this will not work as written. When your dealing with var parameters, you have to build the parameter BEFORE passing it to the procedure/function. When a Var parameter is passed, the procedure is allowed to modify it. If you attempted to copy two variables together on the call, where would this result go?
The other issue is that dwError is not delcared. A class method does NOT have access to the data elements of the object the class defines. If you drop the class, then you will have access to the data elements, but will require that the class first be created.
You should only be using class methods in places where the input and output are completely contained within the method.
How are you allocating your buffer? Internally is it an array?
Sounds like Send has a format parameter (like send (const something;size:integer)
Workaround is using pchar (entirepointerexpression)[0]