Delphi7 - How to extract information about SSE from JvComputerInfoEx1 component? - delphi

Information in form1.JvComputerInfoEx1.CPU.SSE is stored as TSSESupports.
My question is how data is actually stored in TSSESupports and how to convert to plain string?
The wiki page is not very helpful in this matter.

Look at the JCL sources.
type
TSSESupport = (sse, sse2, sse3, ssse3, sse4A, sse4B, sse5);
TSSESupports = set of TSSESupport;
So, TSSESupports is a set. Values in a set are stored bitwise.
You can test if a value is in set by "in" operator.
var
Value: TSSESupports;
begin
if sse in Value then
ShowMessage('Supports SSE');
end;
But the easiest way of converting a set into a string is using RTTI function "SetToString"
uses System.TypInfo, JclSysinfo;
var
Value: TSSESupports;
pTI: PTypeInfo;
S: string;
begin
pTI := TypeInfo(TSSESupports);
S := SetToString(pTI, Word(Value));
ShowMessage(S);
end;

Related

How to assign an OleVariant with RTTI? // Convert an OleVariant or a Variant to a TValue with specific TTypeKind or TRTTIType in mind?

I have an OleVariant or a Variant value that, for example, was read with IXMLNode.GetAttributeNS, making it a "String" (varOleStr or varString), and I would like to write that value with, for example, TRTTIField.SetValue, requiring a TValue assignment-compatible to TRTTIField.FieldType: TRTTIType.
For the base types (along TVarType and TRTTIType.TypeKind: TTypeKind), instead of making each a single case: case VarType(Value) and varTypeMask of varXXXX: ... end, I am looking for a general way to convert from OleVariant or Variant to a TValue that then is assignment-compatible to a specific TRTTIType.
What is the way to transition values between the Variant and the RTTI world?
Also, the Spring4D library is part of the project, in case that helps.
Update:
Technically I am looking for Convert in the following code (converting in the Variant world):
var
Left: TRTTIField;
Right: OleVariant;
Temp: TValue;
Instance: Pointer;
begin
{ Examples: varOleStr --> varXXXX --> assignment-compatible TValue }
Right := 'False'; // varOleStr, as read with IXMLNode.GetAttributeNS
Right := Convert(Right, Left.FieldType); // making it possibly varBoolean
Temp := TValue.FromVariant(Right); // tkEnumeration, like Left.FieldType.TypeKind
Right := '2'; // varOleStr, as read with IXMLNode.GetAttributeNS
Right := Convert(Right, Left.FieldType); // making it possibly varInteger
Temp := TValue.FromVariant(Right); // tkInteger, like Left.FieldType.TypeKind
Right := '3.1415'; // varOleStr, as read with IXMLNode.GetAttributeNS
Right := Convert(Right, Left.FieldType); // making it possibly varDoiuble
Temp := TValue.FromVariant(Right); // tkFloat, like Left.FieldType.TypeKind
Right := 'Hello!'; // varOleStr, as read with IXMLNode.GetAttributeNS
Right := Convert(Right, Left.FieldType); // making it possibly varOleStr
Temp := TValue.FromVariant(Right); // tkUString, like Left.FieldType.TypeKind
{ ... and an assignment: }
Left.SetValue(Instance, Temp);
end;
I have found VariantChangeTypeEx, however, I do not know how to relate Left.FieldType to it to make the subsequent code work. -- I also would not mind to convert in the RTTI world and instead start out with Temp := TValue.FromVariant(Right) (tkUString) and then reach assignment compatibility somehow; so Temp.Kind would become tkEnumeration/Boolean, tkFloat,... as given by Left.FieldType.TypeKind.
How to assign a Variant with RTTI? Or, how to convert a Variant to a TValue to then assign it?
Note: RTTIField.SetValue will fail with an EInvalidCast if field type and value type differ in nature, as the RTTI will not attempt to change the value's nature. My difficulty here is to reach assignment compatibility.
Update: Given the answer, the following code sketches my solution:
procedure (const Value: Pointer; const RTTIField: TRTTIField; const XMLNode: IXMLNode);
var
Temp1: OLEVariant;
Temp2: TValue;
begin
Assert(XMLNode.HasAttribute(Ref, Namespace));
Temp1 := XMLNode.GetAttributeNS(Ref, Namespace);
Temp2 := TValue.FromVariant(Temp1);
Temp2 := Temp2.Convert(RTTIField.FieldType.Handle{, FormatSettings}); // in Spring.TValueHelper
RTTIField.SetValue(Value, Temp2);
end;
The built-in type casts in TValue will not help you here as they only allow those types that are explicitly compatible (i.e. assignable). Technically if you store the Variant inside the TValue without "unpacking" it which is what FromVariant does internally it should be able to cast the Variant to anything it usually can be cast/converted to. However there are is at least one issue with casting a Variant holding 'True' or 'False' to a Boolean (see https://quality.embarcadero.com/browse/RSP-20160)
However since you are already using Spring4D you can use its improved TValue type conversion feature.
Just use the Convert method from the TValueHelper in Spring.pas.
There you can pass a PTypeInfo (which would be Left.FieldType.Handle in your code) and optionally a TFormatSettings - by default it will use the current locale.
What is the way to transition values between the Variant and the RTTI world?
Use the built in class function conversion in System.RTTI.TValue:
myTValue := TValue.FromVariant(myVariant);
Builds a new TValue record from a Variant value.
FromVariant is a static method that can be used to build TValue records with a stored Variant value. The Value parameter contains the Variant that will be stored inside the built TValue record.

Creating property to store two values

Using Delphi 10 I have two values work_start and work_finish of type TTime that I need to read and write from database table so I though to create a property for each one like that
private
fWorkStart: TTime;
function GetWS: TTime;
procedure SetWS(const Value: TTime);
Public
property WorkStart: TTime read GetWS write SetWS;
....
procedure MyClass.SetWS(const Value: TTime);
begin
fWorkStart := value;
mydataset.Edit;
mydataset.FieldByName('work_start').AsDateTime := fWorkStart;
mydataset.Post;
end;
function MyClass.GetWS: TTime;
begin
if mydataset.FieldByName('work_start').IsNull then
fWorkStart := encodetime(6,0,0,0)
else
fWorkStart := mydataset.FieldByName('work_start').AsDateTime;
result := fWorkStart;
end;
WorkFinish property is the same. So is there a way to create one property for both times or my code is fine ?
Craig's answer demonstrates record properties, which means you have a single property that gets set as a unit; you can't set the start and finish times independently. Dawood's answer demonstrates an array property, which allows independent accesses, but imposes cumbersome bracket notation on the consumer. Kobik's comment improves the semantics, but we can do even better using index specifiers.
First, define an enum to represent the two kinds of times:
type
TWorkTime = (wtStart, wtFinish);
Use those values in your property declarations, and provide an extra parameter to your property accessors to represent the index:
private
FWorkTime: :array[TWorkTime] of TTime;
function GetWT(Index: TWorkTime): TTime;
procedure SetWT(Index: TWorkTime; const Value: TTime);
public
property WorkStart: TTime index wsStart read GetWT write SetWT;
property WorkFinish: TTime index wsFinish read GetWT write SetWT;
To reduce the bloat Craig warns about in your accessors, you can define another array with the corresponding fields names, which lets you avoid duplicating code for your different fields:
const
FieldNames: array[TWorkTime] of string = (
'work_start',
'work_finish'
);
function MyClass.GetWT(Index: TWorkTime): TTime;
begin
if mydataset.FieldByName(FieldName[Index]).IsNull then
FWorkTime[Index] := EncodeTime(6, 0, 0, 0)
else
FWorkTime[Index] := mydataset.FieldByName(FieldNames[Index]).AsDateTime;
Result := FWorkTime[Index];
end;
It is possible:
//Define a record to hold both
type
TTimeRange = record
StartTime: TTime;
EndTime: TTime;
end;
//And have your property use the record
property WorkHours: TTimeRange read GetWorkHours write SetWorkHours;
However, this would force clients of your class to interact using the record structure. Basically the complications you'd encounter outweigh the small benefit you'd gain.
So I don't recommend it.
(Although it's worth remembering the technique because in other scenarios it may prove more useful.)
As for your code:
Handling of properties is fine. Although in the code you've presented fWorkStart is redundant.
I'd caution against Edit and Post within your property writer. Apart from the fact that updating 1 field at a time in the Db would be highly inefficient, your method has unexpected side-effects. (And can you always assume edit is the right choice and not insert?)
In your property reader, assuming NULL == 6:00 is not a good idea. NULL has very specific meaning that the value is unknown/unassigned. Defaulting it in the wrong place leads to being unable to tell the difference between 6:00 and NULL. (I'm not saying never default a null; just understand the implications.)
yes you can use indexed properties
property WorkTime[IsStart: Boolean]: TDataTime read GetWorkTime write SetWorkTime;
procedure MyClass.SetWorkTime(IsStart: Boolean;const value: TDataTime);
begin
mydataset.Edit;
if IsStart then
mydataset.FieldByName('work_start').AsDateTime := value else
mydataset.FieldByName('work_Finish').AsDateTime := value;
mydataset.Post;
end;
function MyClass.GetWorkTime(IsStart: Boolean): TTime;
begin
if IsStart then
Begin
if mydataset.FieldByName('work_start').IsNull then
fWorkStart := encodetime(6,0,0,0)
else
fWorkStart := mydataset.FieldByName('work_start').AsDateTime;
result := fWorkStart;
end else
begin
if mydataset.FieldByName('work_finish').IsNull then
fWorkfinish := encodetime(6,0,0,0)
else
fWorkfinish := mydataset.FieldByName('work_finish').AsDateTime;
result := fWorkfinish;
end
end;

How to pass multilined TStrings data from a TIdTCPServer to TIdTCPClient

I tried to pass a database record from my server-side application to my client-side application. On the client-side I need to store my data into a TStrings collection.
When I pass a multiline field, I receive two separate data items at the client-side, instead of one multiline data item! I've also tried to do that with Unicode UTF8 based commands, but unfortunately the result is same.
Server-side code:
procedure TForm1.IdCmdTCPServer1CommandHandlers0Command(ASender: TIdCommand);
var
myData: TStrings;
begin
myData := TStringList.Create;
myData.Add('12'); // ID
myData.Add('This is a multi line' + #13#10 + 'description.'); // Descriptions
myData.Add('Thom Smith'); // Name
try
ASender.Context.Connection.Socket.Write(myData, True{, TIdTextEncoding.UTF8});
finally
myData.Free;
end;
end;
myData debug-time values on server-side are:
myData[0] = '12'
myData[1] = 'This is a multi line'#$D#$A'description.'
myData[2] = 'Thom Smith'
Client-side code:
procedure TForm1.Button1Click(Sender: TObject);
var
myData: TStrings;
begin
with TIdTCPClient.Create(nil) do
begin
Port := 1717;
Host := 'localhost';
try
Connect;
//IOHandler.DefStringEncoding := TIdTextEncoding.UTF8;
myData := TStringList.Create;
try
SendCmd('greating');
Socket.ReadStrings(myData, -1{, TIdTextEncoding.UTF8});
eID.Text := myData[0]; // ID TEdit
mDes.Text := myData[1]; // Descriptions TMemo
eTelNo.Text := myData[2]; // Name TEdit
finally
myData.Free;
end;
finally
Disconnect;
Free;
end;
end;
end;
myData debug-time valuese on client-side:
myData[0] = '12'
myData1 = 'This is a multi line'
myData[2] = 'description.'
Telnet result:
Actually, myData[2] that should keep 'Thom Smith' was replaced with the second line of the Description field! and there are no items after myData[2]. myData[3] is not accessible any more.
I think this issue is related to Indy's Write or ReadStrings procedures, because it sends ItemCount as 3, but it sends two items (one correct, and next beaked to two items!).
How can I pass a Carriage Return character to the other side without having the Write procedure break myData[1] into two separate lines?
Thanks a lot.
If you want TStrings.Text be oblivious to special characters - you should escape them before sending by net, and un-escape after that. There are a lot of ways of escaping, so choose one that suits you.
function EscapeString:(String): String --- your choice
function DeEscapeString:(String): String --- your choice
procedure SendEscapedStrings(const socket: TIdSocket; const data: TStrings);
var s: string; temp: TStringList;
begin
temp := TStringList.Create;
try
temp.Capacity := data.Count;
for s in data do
temp.Add( EscapeString( s ) );
socket.Write(temp);
finally
temp.Destroy;
end;
end;
procedure ReadDeescapedStrings(const socket: TIdSocket; const data: TStrings);
var s: string; temp: TStringList;
begin
temp := TStringList.Create;
try
Socket.ReadStrings(temp, -1);
data.Clear;
data.Capacity := temp.Count;
for s in temp do
temp.Add( DeEscapeString( s ) );
finally
temp.Destroy;
end;
end;
Now the question is what would you choose for DeEscapeString and EscapeString ? The options are many.
You can choose convert string to base64 before sending and from base64 after reading
You can choose UUEEncode for escapgin and UUEDecode for de-escaping
You can choose yEnc: http://en.wikipedia.org/wiki/YEnc
Or you can choose very simplistic functions StrStringToEscaped and StrEscapedToString from JclString unit of from Jedi CodeLib ( http://jcl.sf.net ):
what kind of escaping
If you ask for suggestion i would suggest not using raw TCP Server. There is well-known and standard HTTP protocol, there are many libraries for Delphi implementing both HTTP server and HTTP client. And in the protocol (and libraries) there are already decided things like ciphering, compressing, languages support, etc. And if somethign goes wrong - you can take any HTTP sniffer and see who is in the wrong- clent or server - with your own eyes. Debugging is much simpler.
If you are just starting, i suggest you looking into HTTP+JSON Synopse mORMot library, maybe it would cover your needs. You can take sample server code from http://robertocschneiders.wordpress.com/2012/11/22/datasnap-analysis-based-on-speed-stability-tests/ for example, or from demos in the lib.
Then, if to arrange around raw TCP server, i'd send compressed data, so it would work better (networks are slower than CPU usually). See http://docwiki.embarcadero.com/CodeExamples/XE5/en/ZLibCompressDecompress_(Delphi).
Sending:
1: Send into network (int32) - TStringList.Count
2: for every string doing
2.1 creating TStringStream from the string[i]
2.2 passing it via TZCompressionStream
2.3 sending (int32) size of compressed data
2.4 sending the data itself
2.5 freeing the temporary streams
Receiving
1: Receive from net (int32) - count of packets
1.1 ResultStringList.Clear; ResultStringList.Capacity := read_count.
2: for every string doing
2.1 creating TBytesStream
2.2 read from net (int32) size of compressed data
2.3 read N bytes from the network into BytesStream
2.4 unpack it via TZDecompressionStream into TStringStream
2.5 ResultStringList.Add( StringStream -> string );
2.6 freeing the temporary streams
Now, if you really don't want ot change almost anything, then JCL escaping would hopefully be enough for you. At least it worked for me, but my task was very different and was not about networks at all. But you can just test them all and see how it works for you.
Don't use the TStrings overload as it seems to use line breaks as separator between strings which does not work if your strings contain line breaks themselves.
You can easily write your own wrapper method to send a list of strings over the wire (take that as pseudocode):
procedure WriteStrings(IOHandler : TIdIOHandler; Strings : TStrings);
var
Str : String;
begin
IOHandler.WriteBufferOpen;
try
IOHandler.Write(Strings.Count);
for Str in Strings do
IOHandler.Write(Str);
finally
IOHandler.WriteBufferClose;
end;
end;
procedure ReadStrings(IOHandler : TIdIOHandler; Strings : TStrings);
var
Count, I : Integer;
begin
Count := IOHandler.ReadInteger;
for I := 1 to Count do
Strings.Add(IOHandler.ReadString);
end;

Fastest way to access Data by Key

I have hundreds of variables that have to be accessed with a key.
Type of Keys is string (max:50 character) and Data is array of bytes (max:500 Byte).
I used this way :
define this types :
type
TMyBuf = array[0..(500-1)] of Byte;
TMyKey = string[50];
TMyBufs = TDictionary<TMyKey,TMyBuf>;
var
MyBufs :TMyBufs;
and used :
var vMyBuf :TMyBuf;
vMyData :TBytes ABSOLUTE vMyBuf;
vMyDataLen :Word;
begin
List := srvData.Contexts.LockList;
SetLength(vMyBuf, 500);
try
if(List.Count > 0) then begin
for i := 0 to List.Count-1 do begin
with TMyContext(List[I]) do begin
if SetedIdent then begin
try
vMyBuf:= MyBufs.Items[SeledData];
//extract length of data which stored in two byte
vMyDataLen:= ( ( (vMyBuf[1] shl 8)and $FF00) or (vMyBuf[0] and $FF) );
Connection.IOHandler.Write(vMYData, vMYDataLen);
finally
end;
end;
end;
end;
end;
finally
srvData.Contexts.UnlockList;
SetLength(vMyBuf, 0);
end;
end;
There is a similar code to write data.
1.Is it direct access to the Values? No need to copy the Value dictionary(vMyBuf:= MyBufs.Items[SeledData];).
2.Is there a better way?
You would be better off to make use of the implicit by-reference semantics of Classes
and use TObjectDictionary.
type
TMyBuf = class
public
Data:array[0..(500-1)] of Byte;
end;
TMyKey = string[50];
TMyBufs = TObjectDictionary<TMyKey,TMyBuf>;
var
MyBufs :TMyBufs;
This would allow you to write a single byte into the dictionary easily. You would of course have to allocate each TMyBuf by invoking it's constructor. Similarly cleaning up would be easier if you used a TObjectDictionary which can own (meaning therefore know how to free) all object references placed into it.
The other thing you might not know is that on a Unicode delphi, string[50] is an ancient TurboPascal/DOS-era shortstring type and not a unicode string.
I suggest that unless you REALLY need to, you not worry about using string[50] and simply use string. If you wish to validate at runtime that the string is 50 characters or less and throw an exception, then do it that way.

Delphi (win32) serialization libraries

Are there any Delphi serialization libraries that are capable of serializing records and arrays of records instead of classes?
#Max you can use the TJvAppXMLFileStorage component from JEDI to serialize an record or an array of records.
you can use the procedure called WriteBinary to store the data and ReadBinary to read.
unfortunately there is not much documentation on this component, so here you have an very simple example for store a single record (for an array of records you can easily modify this source code).
The record structure
type
MyRecord= record
Field1 : Integer;
Field2 : Double;
Field3 : String[20];
Field4 : String[20];
end;
Save an record
Procedure SaveMyRecord(Rec : MyRecord);
var
MyStore: TJvAppXMLFileStorage;
begin
MyStore:= TJvAppXMLFileStorage.Create(nil);
try
MyStore.FileName:='C:\temp\record.xml';
//this component supports store multiples objects to the same file, so you need use an identifier for you particular object, in this case i'm use the Dummy name.
MyStore.WriteBinary('Dummy', #Rec,sizeof(Rec));
MyStore.Xml.SaveToFile(MyStore.FileName);
finally
MyStore.Free;
end;
end;
this procedure create an XML file like this, the data is encoded in an hexadecimal format.
<?xml version="1.0" encoding="iso-8859-1"?>
<Configuration>
<Dummy>84030000000000003333333333331F400D737472696E6720746573742031000000000000000D737472696E672074657374203200000000000000000000000000</Dummy>
</Configuration>
Read the persisted data
Procedure LoadMyRecord(var Rec : MyRecord);
var
MyStore: TJvAppXMLFileStorage;
begin
MyStore:= TJvAppXMLFileStorage.Create(nil);
try
MyStore.FileName:='C:\temp\record.xml';//point to the same file
MyStore.Xml.LoadFromFile(MyStore.FileName); //load the file
MyStore.ReadBinary('Dummy', #Rec,sizeof(Rec));//use the Dummy identifier and pass the record as an pointer
finally
MyStore.Free;
end;
end;
Check this full project (tested in Delphi 7)
program ProjectPersistRecord;
{$APPTYPE CONSOLE}
uses
SysUtils,
JvAppXMLStorage;
type
MyRecord= record
Field1 : Integer;
Field2 : Double;
Field3 : String[20];
Field4 : String[20];
end;
Procedure SaveMyRecord(Rec : MyRecord);
var
MyStore: TJvAppXMLFileStorage;
begin
MyStore:= TJvAppXMLFileStorage.Create(nil);
try
MyStore.FileName:='C:\temp\record.xml';
MyStore.WriteBinary('Dummy', #Rec,sizeof(Rec));
MyStore.Xml.SaveToFile(MyStore.FileName);
finally
MyStore.Free;
end;
end;
Procedure LoadMyRecord(var Rec : MyRecord);
var
MyStore: TJvAppXMLFileStorage;
begin
MyStore:= TJvAppXMLFileStorage.Create(nil);
try
MyStore.FileName:='C:\temp\record.xml';
MyStore.Xml.LoadFromFile(MyStore.FileName);
MyStore.ReadBinary('Dummy', #Rec,sizeof(Rec));
finally
MyStore.Free;
end;
end;
Var
Rec : MyRecord;
begin
//Fill the record
Rec.Field1:=900;
Rec.Field2:=7.8;
Rec.Field3:='string test 1';
Rec.Field4:='string test 2';
SaveMyRecord(Rec); //save the record
FillChar(Rec,SizeOf(Rec),#0); //clear the record variable
LoadMyRecord(Rec);//restire the record data
//show the loaded data
Writeln(rec.field1);
Writeln(rec.field2);
Writeln(rec.field3);
Writeln(rec.field4);
Readln;
end.
If you have Delphi 2010, you might want to take a look at DeHL. It contains a serialization library that can handle pretty much any data type.
Another solution, working from Delphi 5 up to XE2, is available in one of our OpenSource unit.
In fact, it implements:
Some low-level RTTI functions for handling record types: RecordEquals, RecordSave, RecordSaveLength, RecordLoad;
A dedicated TDynArray object, which is a wrapper around any dynamic array, able to expose TList-like methods around any dynamic array, even containing records, strings, or other dynamic arrays. It's able to serialize any dynamic array.
Serialization uses an optimized binary format, and is able to save and load any record or dynamic array as RawByteString. You have also JSON serialization at hand, including custom layout - see sustom JSON serialization of records.
Being a record, if you don't have properties, I don't believe you're any further ahead, trying to use any of the persistence frameworks (like DeHL).
The accepted answer, while technically correct, is dubious in real-world utility, and has many long term support-nightmare scenarios if you use it in production. DON'T DO IT.
If your program is just a bit of ad-hoc'ery, may I humbly suggest that you rock it old-school with a "file of record", a classic Turbo Pascal technique that still works.

Resources