I have this ansiString for example
DISC506002000001008100021041511207123051520515308154091550920TN177869-0151J1 36J207 70077 0 0
Trying to extract TN177869-0151J1
but the code I am using keeps returning me the whole ansistring.
function TForm5.ParseDataPartNumber(Data: AnsiString):ansistring;
var
ExtraData: Ansistring;
begin
extraData := data;
Delete(extraData,76,30);
Delete(extraData,0,61);
result:=extraData;
end;
What am I doing wrong? Is it due to it being an ansistring instead of string? that is throwing me off?
Your code is not working because you are passing wrong values in the params of the Delete method. Anyway you can use the Copy function instead like so
function TForm5.ParseDataPartNumber(const Data: AnsiString): ansistring;
begin
Result:=Copy(Data, 62,15);
end;
Strings are 1 based, so change your 2nd Delete to index 1 instead of 0.
ie:
function TForm5.ParseDataPartNumber(Data: AnsiString):ansistring;
var
ExtraData: Ansistring;
begin
extraData := data;
Delete(extraData,77,43);
Delete(extraData,1,61);
result:=extraData;
end;
Your indexes were wrong too to extract that string. My answer shows changed values.
Related
When I code my standalone executable in Delphi, I can perform the following to read text from memory on a process:
var
First: array [0..MAX_PATH] of AnsiChar;
Read: NativeUint;
begin
Win32Check(ReadProcessMemory(hProc, pointer(Base + AddrF), #First, SizeOf(First), Read));
The text from memory will be displayed without any problems. However when I am inside the process as a DLL I use the following code:
var
Value: NativeUint;
begin
Value := PNativeUint(Base + AddrF)^;
ShowMessage(IntToStr(Value)));
Which is fine, but it will show me the proper value at this address (4 byte value), however I want to see it as text.
If i use:
ShowMessage(AnsiChar(Value)));
It will only display the first letter of the text at that memory address.
Question: How can I display the full text?
As MBo said in comments, you can type cast the address to PAnsiChar instead of PNativeUInt. Also, remove the dereferencing ^ operator:
var
Value: PAnsiChar;
begin
Value := PAnsiChar(Base + AddrF);
ShowMessage(Value);
Alternatively:
var
Value: AnsiString;
begin
SetString(Value, PAnsiChar(Base + AddrF), MAX_PATH);
ShowMessage(Trim(Value));
For example
var
First: array [0..MAX_PATH] of AnsiChar;
begin
Move(Pointer(Base + AddrF)^, First, SizeOf(First));
I want to store to disk some data records. I want to have each data record of equal size so I can quickly compute and jump to a certain record.
So, I store the only string in my record as an array of chars:
type TFileNameIO = array[1..512] of Char;
After I read the string from disk, the content of my string is like this:
c:\windows#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0#0 etc
I want to truncate the #0 characters, and I do something like this:
function GetFileName(DiskName: TFileNameIO): string;
VAR PC: PChar;
begin
SetString(Result, PChar(#DiskName), Length(DiskName)); <----- here Result is: c:\windows#0#0#0 etc
PC:= PChar(Result);
Result:= WideCharToString(PC);
end;
There is a better way to do it?
You can assign a null-terminated PChar directly to a String:
function GetFileName(DiskName: TFileNameIO): string;
begin
Result := PChar(#DiskName);
end;
Just make sure the TDiskName always contains a null terminator. If it can ever be completely full of characters without a null, you will have to do this instead:
function GetFileName(DiskName: TFileNameIO): string;
var
Len, I: Integer;
begin
Len := 0;
For I := Low(DiskName) to High(DiskName) do
begin
if DiskName[I] = #0 then Break;
Inc(Len);
end;
SetString(Result, PChar(#DiskName), Len);
end;
Your function is not necessary because you can simply assign a character array to a string variable.
type
TCharArray = array [1..512] of Char;
....
var
arr: TCharArray;
str: string;
....
arr := ...;
str := arr;
This results in str being assigned the contents of arr. The compiler implements this with a call to System._UStrFromWArray which first of all looks for a null-terminator. If it finds one, that determines the length of str. Otherwise, the entire contents of the array are copied, and Length(str)=Length(arr).
As a broader point, I would recommend that you change to using zero-based character arrays. From the documentation:
Zero-based character arrays are compatible with PChar and PWideChar. When you use a character array in place of a pointer value, the compiler converts the array to a pointer constant whose value corresponds to the address of the first element of the array.
This makes it easier for you to interop with functions that expect PChar arguments.
As an aside, passing TFileNameIO by value is inefficient because it involves a memory copy. Pass such types by reference. For instance as a const parameter.
i have an ole Object created with (simple verion)
obj := CreateOleObject('foo.bar');
obj.OnResult := DoOnResult;
procedure TMyDM.DoOnResult(Res: olevariant);
which all works, the res variable has a function String[] GetAns()
which im calling like this
var
ans: array of string;
begin
ans := Res.GetAns;
end;
which again works.. except sometimes no array is returned, and then an exception is thrown.
as a temporary solution i have wrapped it in a empty try except block, which i know is bad. I have tried VarIsArray(Res.GetAns) but it still donst work if the result is null
What is the correct way check for the right result?
ps I have no control over the ole Object
Christopher try using the VarIsNull function
procedure TMyDM.DoOnResult(Res: olevariant);
var
ans: array of string;
begin
if not VarIsNull(Res) then
if not VarIsNull(Res.GetAns) then
begin
ans := Res.GetAns;
//do your stuff
end;
end;
I'd like to pass a multi-dimensional array to a constructor like so:
constructor TMyClass.Create(MyParameter: array of array of Integer);
begin
LocalField := MyParameter;
end;
Where LocalField is an array of array of Integer.
However the above code won't compile ('Identifier expected but ARRAY found'). Could somebody explain to me why this is wrong? I tried reading up on open, static and dynamic arrays but have yet to find something that works. Is there a way to fix it without changing the type of LocalField?
Make a specific type for localfield, then set that as the type of MyParameter, something along the lines of:
type
TTheArray = array[1..5] of array[1..10] of Integer;
var
LocalField: TTheArray;
constructor TMyClass.Create(MyParameter: TTheArray);
...
(Note: not verified in a compiler, minor errors may be present)
Note that most often in pascal-like syntax a multidimensional array is more properly declared as
type
TTheArray = array[1..5, 1..10] of Integer;
Unless, of course, you have some good reason for doing it the other way.
I don't have Delphi at hands, but I think this should work:
type
TIntArray = array of Integer;
...
constructor TMyClass.Create (MyParameter : array of TIntArray);
begin
...
end;
If a type is used before as suggested in the answer, please note that you are passing it as a reference, see:
https://blog.spreendigital.de/2016/08/01/pass-a-multidimensional-array-as-a-parameter-with-a-hidden-caveat/
I prefer this
procedure MakeMat(var c: TMatrix; nr, nc: integer; a: array of double);
var
i, j: integer;
begin
SetLength(c, nr, nc);
for i := 0 to nr-1 do
for j := 0 to nc-1 do
c[i,j] := a[i*nc + j];
end;
MakeMat(ya, 5, 11,
[1.53,1.38,1.29,1.18,1.06,1.00,1.00,1.06,1.12,1.16,1.18,
0.57,0.52,0.48,0.44,0.40,0.39,0.39,0.40,0.42,0.43,0.44,
0.27,0.25,0.23,0.21,0.19,0.18,0.18,0.19,0.20,0.21,0.21,
0.22,0.20,0.19,0.17,0.15,0.14,0.14,0.15,0.16,0.17,0.17,
0.20,0.18,0.16,0.15,0.14,0.13,0.13,0.14,0.14,0.15,0.15]);
First, apologies for my English, I hope it makes sense what I`ve written here. Now to my problem.
How can I get the string representation of the content type of a Variant using TypInfo.GetEnumName(). I have tried the following, without luck, I get a numeric representation.
myString := GetEnumName( TypeInfo(TVarType), TVarData(myVar).VType );
Thank you.
Just use the build-in Delphi function for getting the string representation of a Variant type.
var
MyVariantType: string;
MyVariant: Variant;
begin
MyVariant := 'Hello World';
MyVariantType := VarTypeAsText(VarType(MyVariant));
ShowMessage(MyVariantType); //displays: String
MyVariant := 2;
MyVariantType := VarTypeAsText(VarType(MyVariant));
ShowMessage(MyVariantType); //displays: Byte
end;
Quoting from the Delphi 2007 help:
Use GetEnumName to convert a Delphi enumerated value into the symbolic name that represents it in code.
That means that you can't use it for that purpose, as TVarData.VType is not an enumerated value, but an integer which is set to one of the constants in System.pas that are taken from the Windows SDK wtypes.h file. Look at the source of GetEnumName(), it does immediately return a string containing the value of the integer.
Edit:
is there any other way to get the string representation of TVarData.VType
You can determine the string representation manually. First you need to be aware of that there are several bits of information encoded in that integer, so a simple case statement or array lookup will not work. The lower 12 bits are the type mask, and the upper bits encode information about whether it is a vector or array type and whether it is given by reference or not. The important parts are:
const
varTypeMask = $0FFF;
varArray = $2000;
varByRef = $4000;
So you could do something like:
function VariantTypeName(const AValue: TVarData): string;
begin
case AValue.VType and varTypeMask of
vtInteger: Result := 'integer';
// ...
end;
if AValue.VType and varArray <> 0 then
Result := 'array of ' + Result;
if AValue.VType and varByRef <> 0 then
Result := Result + ' by ref';
end;
Since it's not an enum, you'll have to do it manually. Write something like this:
function VariantTypeName(const value: TVarData): string;
begin
case value.VType of
vtInteger: result := 'integer';
//and so on
end;
Or, since the values in System.pas are listed in order, you could try declaring a const array of strings and have your VariantTypeName function return the appropriate member of the array.
Here's a thought for Delphi versions that don't support VarTypeAsText: You could define a enumerate type yourself that follows the VType values:
type
{$TYPEINFO ON}
TMyVarType = (
varEmpty = System.varEmpty,
varNull = System.varNull,
// etc...
);
(Fill the unused enum slots too - see Why do I get "type has no typeinfo" error with an enum type for the reasoning behind this).
Next, use these functions to read the Variants' type as your own enumerate type :
function MyVarType(VType: TVarType): TMyVarType; overload;
begin
Result := TMyVarType(VType);
end;
function MyVarType(V: Variant): TMyVarType; overload;
begin
Result := TMyVarType(TVarData(V).VType);
end;
And then you can convert it to a string like this :
function VarTypeToString(aValue: TMyVarType): string;
begin
Result := GetEnumName(TypeInfo(TMyVarType), Ord(aValue));
end;