I wrote this code and am getting AV in it.
procedure TForm1.Button1Click(Sender: TObject);
Var
C : Pchar;
s : string;
begin
c:= PChar('*');
s := string(c); // AV here , but code works if i put C:= PChar('**')
ShowMessage(c);
end;
I could not figure out why .Does anybody know ?
Thanks in advance.
With a one-character string literal, you're type casting a Char, not a string, so it's not a pointer. When you cast it back, it's still not really a pointer, despite its declared type, so it can't be converted to a string.
If you find yourself type casting a string literal, you're probably doing something unnecessary. Although you can give it a hint which type it should use, as the other answers here demonstrate, the compiler already detects which type a literal needs to have, and it's usually correct. Just assign the literal directly to the variable without any casting.
If you omit the type cast entirely, your code would work equally well for whatever length string you want:
// All PChar assignments, no casting
c := '';
c := '*';
c := '**';
Furthermore, the cast back to string is unnecessary as well. You can directly assign a PChar, and the compiler will perform the conversion automatically:
s := c;
In fact,
c:= PChar('*');
is compiled as
mov [c],$0000002a
as if it was written:
c:= PChar(ord('*'));
Since ord('*')=$2a, it appears that the '*' character is type-cast to an integer (NativeInt), then this integer is converted to a pointer. So when you try to access the c content, you access the memory address $0000002a, which is invalid, and triggers an access violation.
When you compile:
c:= PChar('**');
It is generated as
mov eax,$00548984
mov [c],eax
In this case, a constant #0-ended text buffer (and not a Delphi string) is generated by the compiler within the executable, and c is set to its address.
The fact that PChar('*') does not behave the same is one "optimization" of the char type, which can be typecasted to an integer.
But I understand it may be confusing.
If you want just a pointer to a single '*', you can write either:
c:=PChar('*'#0);
c:=PChar(string('*'));
Which will work as expected, since both will by-pass the cast to the character ordinal value.
AV means wrong work with memory. Get data from nowhere or write to nowhere.
Problem goes from different types of data.
'*'
is Char, but
'**'
is string
This will work fine with your code:
procedure TForm1.Button1Click(Sender: TObject);
Var
C : Pchar;
s : string;
begin
c:= PChar(string('*'));
s := string(c); // AV here , but code works if i put C:= PChar('**')
ShowMessage(c);
end;
Related
I need to modify pchar string at runtime.
Help me with this code:
var
s:pChar;
begin
s:='123123';
s[0]:=#32; // SO HERE I HAVE EXCEPTION !!!
end.
Now i have exception in Delphi 7 !
My project is not using native pascal strings (no any windows.pas classes and others)
String literals are read only and cannot be modified. Hence the runtime error. You'll need to use a variable.
var
S: array[0..6] of Char;
....
// Populate S with your own library function
S[0] := #32;
Since you aren't using the Delphi runtime library you'll need to come up with your own functions to populate character arrays. For instance, you can write your own StrLen, StrCopy etc. You'll want to make versions that are passed destination buffer lengths to ensure that you don't overrun said buffers.
Of course, not using the built in string type will be inconvenient. You might need to come up with something a little more powerful than ad hoc character arrays.
You can:
procedure StrCopy(destination, source: PChar);
begin
// Iterate source until you find #0
// and copy all characters to destination.
// Remember to allocate proper amount of memory
// (length of source string and a null terminator)
// for destination before StrCopy() call
end;
var
str: array[0..9] of Char;
begin
StrCopy(str, '123123');
s[0]:=#32;
end.
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;
In VirtualTreeview, I am storing my data in the PVirtualNodes. I have experienced several Access Violations (typically with "Read of adress 00000000") in my App, and they mostly (I'd actually dare to say Always) occur when I am doing something with my Node Data.
However, the thing is, I declare my stuff & use it like this:
// DUMMY CODE - Not written or tested in IDE
var
MyNode : PVirtualNode;
MyData : PMyNodeData;
Begin
MyNode := VST.GetFirstSelected;
if Assigned(MyNode) then
Begin
MyData := VST.GetNodeData(MyNode);
if Assigned(MyData) then
Begin
MyData.DummyProperty := 'Test';
End;
End;
End;
As you probably noticed, I do not "dereference" (correct?) my "MyData" by doing MyData^! The reason I don't is that I have been told it was not necessary to add the caret to the pointer name, however I have a feeling that it has something to do with it. If I knew, I wouldn't be posting on here. ;)
So my question is: Is it in the end necessary to add the little ^ to MyData? And is it possible that by not doing that, I may provoke an Access Violation?
When you have a pointer to a record, then you can omit the ^. The following are equivalent:
MyData.DummyProperty
MyData^.DummyProperty
This is also the case for the deprecated Turbo Pascal object. I would expect it to be so for Delphi classes, although I have never tried with them since they are already reference types.
Sadly, this is not the explanation for your AV.
Using ^ to dereference records is optionnal as it is assumed implicitly by the compiler. When not using any hard typecast, any situation that would requires the "^" would not compile. But only 1 level of dereferencing is implicit.
type
TMyRecord = record
MyField : Integer;
end;
PMyRecord = ^TMyRecord;
PPMyRecord = ^PMyRecord;
procedure DoSomething;
var vMyField : PPMyRecord;
begin
vMyField.MyField; <---Won't compile
vMyField^.MyField; <---Will compile
end;
As for your access violation, here's my best guess based on what you wrote... Assuming your exemple is representative (i.e. that is, crash on assigning a string), and assuming PMyNodeData points to a record. I'd guess that PMyNodeData's memory was reserved with "GetMem" instead of "New", making the string field of the record uninitialized.
There is an exception where Data.xx and Data^.xx are not the same: when the field pointed at is of the same pointer type or the generic pointer type:
var
x: PPointer;
y: Pointer;
begin
x := GetPPointer();
y := x;
y := x^;
end;
I consider it best practice to always add the operator ^ when the pointed value is used to avoid ambiguous situations like above.
Given your example: The problem is possibly memory corruption. Did you set NodeDataSize correctly?
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.
I've just been debugging a problem with a function that returns a string that has got me worried. I've always assumed that the implicit Result variable for functions that return a string would be empty at the start of the function call, but the following (simplified) code produced an unexpected result:
function TMyObject.GenerateInfo: string;
procedure AppendInfo(const AppendStr: string);
begin
if(Result > '') then
Result := Result + #13;
Result := Result + AppendStr;
end;
begin
if(ACondition) then
AppendInfo('Some Text');
end;
Calling this function multiple times resulted in:
"Some Text"
the first time,
"Some Text"
"Some Text"
the second time,
"Some Text"
"Some Text"
"Some Text"
the third time, etc.
To fix it I had to initialise the Result:
begin
Result := '';
if(ACondition) then
AppendInfo('Some Text');
end;
Is it necessary to initialise a string function result? Why (technically)? Why does the compiler not emit a warning "W1035 Return value of function 'xxx' might be undefined" for string functions? Do I need to go through all my code to make sure a value is set as it is not reliable to expect an empty string from a function if the result is not explicitly set?
I've tested this in a new test application and the result is the same.
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
for i := 1 to 5 do
S := GenerateInfo;
ShowMessage(S); // 5 lines!
end;
This is not a bug, but "feature":
For a string, dynamic array, method
pointer, or variant result, the
effects are the same as if the
function result were declared as an
additional var parameter following the
declared parameters. In other words,
the caller passes an additional 32-bit
pointer that points to a variable in
which to return the function result.
I.e. your
function TMyObject.GenerateInfo: string;
Is really this:
procedure TMyObject.GenerateInfo(var Result: string);
Note "var" prefix (not "out" as you may expect!).
This is SUCH un-intuitive, so it leads to all kind of problems in the code. Code in question - just one example of results of this feature.
See and vote for this request.
We've run into this before, I think maybe as far back as Delphi 6 or 7. Yes, even though the compiler doesn't bother to give you a warning, you do need to initialize your string Result variables, for precisely the reason you ran into. The string variable is getting initialized -- it doesn't start as a garbage reference -- but it doesn't seem to get reinitialized when you expect it to.
As for why it happens... not sure. It's a bug, so it doesn't necessarily need a reason. We only saw it happen when we called the function repeatedly in a loop; if we called it outside a loop, it worked as expected. It looked like the caller was allocating space for the Result variable (and reusing it when it called the same function repeatedly, thus causing the bug), rather than the function allocating its own string (and allocating a new one on each call).
If you were using short strings, then the caller does allocate the buffer -- that's long-standing behavior for large value types. But that doesn't make sense for AnsiString. Maybe the compiler team just forgot to change the semantics when they first implemented long strings in Delphi 2.
This is not a Bug. By definition no variable inside function is initialized, including Result.
So your Result is undefind on first call, and can hold anything. How it is implemented in compiler is irrelevant, and you can have different results in different compilers.
It seems like your function should be simplified like this:
function TMyObject.GenerateInfo: string;
begin
if(ACondition) then
Result := 'Some Text'
else
Result := '';
end;
You typically don't want to use Result on the right side of an assignment in a function.
Anyway, strictly for illustrative purposes, you could also do this, though not recommended:
procedure TForm1.Button1Click(Sender: TObject);
var
i: integer;
S: string;
begin
for i := 1 to 5 do
begin
S := ''; // Clear before you call
S := GenerateInfo;
end;
ShowMessage(S); // 5 lines!
end;
This looks like a bug in D2007. I just tested it in Delphi 2010 and got the expected behavior. (1 line instead of 5.)
If you think that some automatic management of strings are made to make your life easier, you're only partly right. All such things are also done to make string logic consistent and side-effects free.
In plenty of places there are string passed by reference, passed by value, but all these lines are expecting VALID strings in whose memory-management counter is some valid, not a garbage value. So in order to keep strings valid the only thing for sure is that they should be initialized when they firstly introduced. For example, for any local variable string this is a necessity since this is the place a string is introduced. All other string usage including function(): string (that actually procedure(var Result: string) as Alexander correctly pointed out) just expects valid strings on the stack, not initialized. And validness here comes from the fact that (var Result: string) construction says that "I'm waiting for a valid variable that definetly was introduced before". UPDATE: Because of that the actual contents of Result is unexpected, but due to the same logic, if it's the only call to this function that has a local variable at the left, the emptiness of the string in this case is guaranteed.
Alex's answer is nearly always right and it answers why I was seeing the strange behaviour that I was, but it isn't the whole story.
The following, compiled without optimisation, produces the expected result of sTemp being an empty string. If you swap the function out for the procedure call you get a different result.
There seems to be a different rule for the actual program unit.
Admittedly this is a corner case.
program Project1;
{$APPTYPE CONSOLE}
uses System.SysUtils;
function PointlessFunction: string;
begin
end;
procedure PointlessProcedure(var AString: string);
begin
end;
var
sTemp: string;
begin
sTemp := '1234';
sTemp := PointlessFunction;
//PointlessProcedure(sTemp);
WriteLn('Result:' + sTemp);
ReadLn;
end.