I'm out of ideas so I appeal to the immense StackOverflow supermind. What I want is to have a a tedit or whatever text control that lets me input text with the following format: "nnnnnn:nn" where n is an integer. Examples: If I type "100" I want a "100:00" text property. If I type "123:1" I should get "123:01". May be I should only type numbers in a calculator style with the ":" separator at a fixed position. I want rejected something like this "10 : 1", "10:95" (minutes 0-59), "0100:10", etc. Any ideas or component?
Greetings, Marcelo.
Any formatting that changes in input text during entry is bad, so the following shows you how to do it, but input is only changed on exiting the field or pressing the enter key:
Edit 1 is the edit field in question. Edit2 is simply there to allow the tab key to exit Edit1.
Note that I am using standard evets and event name (OnKeyPress and OnExit)
unit Unit10;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.StrUtils, Vcl.Mask;
type
TForm10 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
procedure Edit1KeyPress(Sender: TObject; var Key: Char);
procedure Edit1Exit(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
function EntryValid( const pVal : string ) : boolean;
end;
var
Form10: TForm10;
implementation
{$R *.dfm}
{ TComboBox }
procedure TForm10.Edit1Exit(Sender: TObject);
var
iPos : integer;
iCheck : string;
i1 : string;
begin
iPos := Pos( ':', Edit1.Text );
if iPos > 0 then
begin
// we already know there can only be one ':'
i1 := Copy( Edit1.Text, 1, iPos );
iCheck := Copy(Edit1.Text, iPos + 1 );
if iCheck = '' then
begin
Edit1.Text := i1 + '00';
end
else if StrToInt( iCheck ) < 10 then
begin
Edit1.Text := i1 + '0' + iCheck;
end
else
begin
// already correct, so ignore
end;
end
else
begin
Edit1.Text := Edit1.Text + ':00';
end;
end;
procedure TForm10.Edit1KeyPress(Sender: TObject; var Key: Char);
begin
// only allow numbers and a single :
case Key of
'0'..'9': ;
':':
begin
if Pos( ':', Edit1.Text ) <> 0 then
begin
Key := #0; // don't allow
Beep;
end;
end;
#13:
begin
Key := #0; // silently this time
Edit1Exit( Sender );
end
else
begin
Key := #0;
Beep;
end;
end;
end;
function TForm10.EntryValid(const pVal: string): boolean;
var
iPos : integer;
iCheck : string;
begin
iPos := Pos( ':', pVal );
if iPos > 0 then
begin
// we already know there can only be one ':'
iCheck := Copy( pVal, iPos + 1 );
if iCheck = '' then
begin
Result := TRUE;
end
else if StrToIntDef( iCheck, 60 ) < 60 then
begin
Result := TRUE;
end
else
begin
Result := FALSE;
end;
end
else
begin
Result := TRUE;
end;
end;
end.
Related
I have an Indy TCPServer that connects a device with several clients.
When device-data arrives, the server sends it to every client.
When client-data arrives, it is sent to the device. (And the device will send it to the server again (echo)).
I only process 2 bytes per cycle.
The above works perfect.
Now i want to process/save that data.
Only when data arrives via the device port, i want to 'Translate' the data.
I need to save the data to a clientdatset.
Then i want to take the bytes apart and compare them with som other info
...
The translated data is also saved in another clientdataset.
unit BusServer;
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Classes, System.Variants,
Vcl.Graphics, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs,
IdContext, IdAntiFreezeBase, IdAntiFreeze, IdBaseComponent, IdComponent,
IdCustomTCPServer, IdTCPServer,
Data.DB, Datasnap.DBClient;
type
TBus_Server = class(TService)
tcpBusDataServer: TIdTCPServer;
IdAntiFreeze1: TIdAntiFreeze;
cdsBusMonitor: TClientDataSet;
cdsBusMonitorNr: TIntegerField;
cdsBusMonitorDate: TStringField;
cdsBusMonitorTime: TStringField;
cdsBusMonitorAad: TIntegerField;
cdsBusMonitorAgr: TIntegerField;
cdsBusMonitorAName: TStringField;
cdsBusMonitorAFct: TStringField;
cdsBusMonitorOrigin: TStringField;
cdsIncoming: TClientDataSet;
cdsMemberState: TClientDataSet;
cdsMemberStateMemberID: TStringField;
cdsMemberStateState: TStringField;
cdsMemberStateDateTime: TDateTimeField;
cdsMemberStateTotaal: TFloatField;
procedure tcpBusDataServerExecute(AContext: TIdContext);
procedure ServiceCreate(Sender: TObject);
private
{ Private declarations }
functionmon: String;
DevicePort: Integer;
ClientPort: Integer;
ClientLSB, ClientMSB: Byte;
DeviceLSB, DeviceMSB: Byte;
FunctionList: TStringList;
procedure TranslateData;
function CodeValue: String;
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;
var
Bus_Server: TBus_Server;
implementation
uses
CodesiteLogging;
{$R *.dfm}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
Bus_Server.Controller(CtrlCode);
end;
function TBus_Server.CodeValue: String;
begin
if (cdsIncoming.FieldbyName('MemberType').AsInteger = 11) or
(cdsIncoming.FieldbyName('MemberType').AsInteger = 22)or
(cdsIncoming.FieldbyName('MemberType').AsInteger = 33) then
begin
Result := FunctionMon
end
else
begin
// TODO:
end
end;
function TBus_Server.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TBus_Server.ServiceCreate(Sender: TObject);
var
DataSetName: String;
begin
DevicePort := 10001;
ClientPort := 10012;
tcpBusDataServer.Bindings.Clear;
tcpBusDataServer.Bindings.Add.Port := DevicePort;
tcpBusDataServer.Bindings.Add.Port := ClientPort;
tcpBusDataServer.Active := True;
FunctionList := TStringList.Create;
FunctionList.Add('Null');
FunctionList.Add('Reset');
FunctionList.Add('Toggle');
FunctionList.Add('Set');
FunctionList.Add('Misc');
FunctionList.Add('Status');
FunctionList.Add('Timer/Direct');
FunctionList.Add('Value');
FunctionList.Add('Dimmer');
FunctionList.Add('Readout');
FunctionList.Add('Teller');
FunctionList.Add('System');
FunctionList.Add('Settings');
FunctionList.Add('Select');
FunctionList.Add('Data');
FunctionList.Add('Program');
ForceDirectories('c:\Test\');
DataSetName := 'c:\Test\BusMonitor' + FormatDateTime('YYYY-MM-DD', Now) + '.xml';
if cdsBusMonitor.Active then
cdsBusMonitor.Close;
cdsBusMonitor.Filename := DataSetName;
if not System.SysUtils.FileExists(DataSetName) then
begin
cdsBusMonitor.CreateDataSet;
cdsBusMonitor.SaveToFile
end;
DataSetName := 'c:\Test\MemberState' + FormatDateTime('YYYY-MM-DD', Now) + '.xml';
if cdsMemberState.Active then
cdsMemberState.Close;
cdsMemberState.Filename := DataSetName;
if not System.SysUtils.FileExists(DataSetName) then
begin
cdsMemberState.CreateDataSet;
cdsMemberState.SaveToFile
end;
end;
procedure TBus_Server.tcpBusDataServerExecute(AContext: TIdContext);
var
Ctx: TIdContext;
List: TList;
begin
if AContext.Binding.Port = ClientPort then // Client
begin
DeviceLSB := AContext.Connection.IOHandler.ReadByte;
DeviceMSB := AContext.Connection.IOHandler.ReadByte;
List := tcpBusDataServer.Contexts.LockList;
try
for var i := 0 to List.count - 1 do
begin
Ctx := TIdContext(List[I]);
if (Ctx <> AContext) and (Ctx.Binding.Port = DevicePort) then
begin
Ctx.Connection.IOHandler.Write(DeviceLSB);
Ctx.Connection.IOHandler.Write(DeviceMSB);
// Since only 1 has to be written to
Break
end
end
finally
tcpBusDataServer.Contexts.UnlockList
end
end
else
begin
if AContext.Binding.Port = DevicePort then // Device
begin
ClientLSB := AContext.Connection.IOHandler.ReadByte;
ClientMSB := AContext.Connection.IOHandler.ReadByte;
List := tcpBusDataServer.Contexts.LockList;
try
for var i := 0 to List.count - 1 do
begin
Ctx := TIdContext(List[I]);
if (Ctx <> AContext) and (Ctx.Binding.Port = ClientPort) then
begin
Ctx.Connection.IOHandler.Write(ClientLSB);
Ctx.Connection.IOHandler.Write(ClientMSB)
end
end
finally
TIdNotify.NotifyMethod(TranslateData);
tcpBusDataServer.Contexts.UnlockList
end
end
end
end;
procedure TBus_Server.TranslateData;
const {$J+}
LastSave: TDateTime = 0;
type
TProgramState = (psNone,psProgram,psIgnore1,psIgnore2);
const
ProgramState: TProgramState = psNone;
const
ValueMode: Boolean = False;
var
i: Integer;
fct: Integer;
GroupMon: Integer;
AddressMon: Integer;
CorrecteSettings: Boolean;
TmpStr: String;
begin
fct := 0;
// Functie uit MSB halen
if ClientMSB >= 128 then
begin
ClientMSB := ClientMSB - 128;
fct := 8
end;
if ClientMSB >= 64 then
begin
ClientMSB := ClientMSB - 64;
fct := fct + 4
end;
if ClientMSB >= 32 then
begin
ClientMSB := ClientMSB - 32;
fct := fct + 2
end;
if ClientMSB >= 16 then
begin
ClientMSB := ClientMSB - 16;
fct := fct + 1
end;
// Variabelen voor monitor bepalen
functionMon := FunctionList[fct];
if cdsBusMonitor.Active then
begin
cdsBusMonitor.Filtered := False;
cdsBusMonitor.Append;
cdsBusMonitor.FieldByName('Nr').AsInteger := cdsBusMonitor.RecordCount + 1;
cdsBusMonitor.FieldByName('AFct').Asstring := functionMon;
cdsBusMonitor.FieldByName('Aad').AsInteger := ClientLSB;
cdsBusMonitor.FieldByName('Agr').AsInteger := ClientMSB;
cdsBusMonitor.FieldByName('Time').Asstring := TimeToStr(Now);
cdsBusMonitor.FieldByName('Origin').AsString := 'Van de Bus: '{ + UserPeerIP};
cdsBusMonitor.Post;
end;
if ProgramState = psNone then
begin
CodeSite.Send('New situation...');
try
if cdsIncoming.Locate('Group;Address', VarArrayOf([IntToStr(DeviceMsb),IntToStr(DeviceLsb)]), []) then
begin
CodeSite.Send('After locate...');
if cdsMemberState.Locate('MemberID', cdsIncoming.FieldByName('MemberID').AsString, []) then
cdsMemberState.Edit
else
cdsMemberState.Append;
if cdsMemberStateState.AsString = Codevalue then
begin
CodeSite.Send('New state ' + Codevalue + ' already known');
cdsMemberState.Cancel
end
else
begin
CodeSite.Send('New state ' + Codevalue);
cdsMemberStateState.AsString := Codevalue;
if Codevalue.ToLower = 'reset' then
cdsMemberStateTotaal.AsFloat := cdsMemberStateTotaal.AsFloat + (Now - cdsMemberStateDateTime.AsDateTime);
cdsMemberStateDateTime.AsDateTime := Now;
cdsMemberState.Post
end
end
else
CodeSite.SendError('ServerMethodsBServer.cdsIncoming Locate Fail');
except
on E: Exception do
CodeSite.SendException(E);
end
end;
if ((cdsBusMonitor.RecordCount mod 100) = 0) or ((Now - LastSave) > (1/24/60)) then
begin
LastSave := Now;
cdsBusMonitor.MergeChangeLog;
cdsBusMonitor.SaveToFile;
cdsMemberState.MergeChangeLog;
cdsMemberState.SaveToFile
end
end;
In the clientdataset, i occasionally see "wrong data", meaning:
First i have a correct record, followed by a record with the same lsb and a wrong msb.
I split up the msb in the procedure(Translatedata) in a 'high nibble' and a 'low nibble'.
So now i'm trying to find out where this comes from.
As you can see in my code i call the procedure via TIdNotify.NotifyMethod(TranslateData);
Is this te correct way?
Could appending/posting data in a clientdataset (whilst in the serverthread) be a problem?
Is this a timing issue?
Has anyone an idea of what could be wrong?
Your code is not very thread-safe. You are not protecting your data values from concurrent access across thread boundaries, if multiple clients send data to the server at the same time.
Also, you should not be doing all of your service initializations in the OnCreate event, use the OnStart event instead. The OnCreate event is triggered whenever your TService object is created for any reason, which includes not only running the service, but also (un)installing the service.
Try something more like this:
unit BusServer;
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Classes, System.Variants,
Vcl.Graphics, Vcl.Controls, Vcl.SvcMgr, Vcl.Dialogs,
IdContext, IdBaseComponent, IdComponent,
IdCustomTCPServer, IdTCPServer, IdThreadSafe,
Data.DB, Datasnap.DBClient, System.SyncObjs;
type
TBus_Server = class(TService)
tcpBusDataServer: TIdTCPServer;
cdsBusMonitor: TClientDataSet;
cdsBusMonitorNr: TIntegerField;
cdsBusMonitorDate: TStringField;
cdsBusMonitorTime: TStringField;
cdsBusMonitorAad: TIntegerField;
cdsBusMonitorAgr: TIntegerField;
cdsBusMonitorAName: TStringField;
cdsBusMonitorAFct: TStringField;
cdsBusMonitorOrigin: TStringField;
cdsIncoming: TClientDataSet;
cdsMemberState: TClientDataSet;
cdsMemberStateMemberID: TStringField;
cdsMemberStateState: TStringField;
cdsMemberStateDateTime: TDateTimeField;
cdsMemberStateTotaal: TFloatField;
procedure tcpBusDataServerExecute(AContext: TIdContext);
procedure ServiceStart(Sender: TObject; var Started: Boolean);
procedure ServiceStop(Sender: TObject; var Stopped: Boolean);
procedure ServiceShutdown(Sender: TObject);
private
{ Private declarations }
FunctionMon: TIdThreadSafeString;
DevicePort: Integer;
ClientPort: Integer;
DeviceLSB, DeviceMSB: Byte;
DeviceDataLock: TCriticalSection;
FunctionList: TStringList;
procedure TranslateData(ClientLSB, ClientMSB: Byte);
function CodeValue: String;
public
function GetServiceController: TServiceController; override;
{ Public declarations }
end;
var
Bus_Server: TBus_Server;
implementation
uses
CodesiteLogging;
{$R *.dfm}
procedure ServiceController(CtrlCode: DWord); stdcall;
begin
Bus_Server.Controller(CtrlCode);
end;
function TBus_Server.CodeValue: String;
begin
case cdsIncoming.FieldByName('MemberType').AsInteger of
11, 22, 33: begin
Result := FunctionMon.Value;
end;
else
// TODO
Result := '';
end;
end;
function TBus_Server.GetServiceController: TServiceController;
begin
Result := ServiceController;
end;
procedure TBus_Server.ServiceStart(Sender: TObject; var Started: Boolean);
var
DataSetName: String;
begin
DevicePort := 10001;
ClientPort := 10012;
DeviceDataLock := TCriticalSection.Create;
FunctionMon := TIdThreadSafeString.Create;
FunctionList := TStringList.Create;
FunctionList.Add('Null');
FunctionList.Add('Reset');
FunctionList.Add('Toggle');
FunctionList.Add('Set');
FunctionList.Add('Misc');
FunctionList.Add('Status');
FunctionList.Add('Timer/Direct');
FunctionList.Add('Value');
FunctionList.Add('Dimmer');
FunctionList.Add('Readout');
FunctionList.Add('Teller');
FunctionList.Add('System');
FunctionList.Add('Settings');
FunctionList.Add('Select');
FunctionList.Add('Data');
FunctionList.Add('Program');
ForceDirectories('c:\Test\');
DataSetName := 'c:\Test\BusMonitor' + FormatDateTime('YYYY-MM-DD', Now) + '.xml';
if cdsBusMonitor.Active then
cdsBusMonitor.Close;
cdsBusMonitor.Filename := DataSetName;
if not System.SysUtils.FileExists(DataSetName) then
begin
cdsBusMonitor.CreateDataSet;
cdsBusMonitor.SaveToFile;
end;
DataSetName := 'c:\Test\MemberState' + FormatDateTime('YYYY-MM-DD', Now) + '.xml';
if cdsMemberState.Active then
cdsMemberState.Close;
cdsMemberState.Filename := DataSetName;
if not System.SysUtils.FileExists(DataSetName) then
begin
cdsMemberState.CreateDataSet;
cdsMemberState.SaveToFile;
end;
tcpBusDataServer.Bindings.Clear;
tcpBusDataServer.Bindings.Add.Port := DevicePort;
tcpBusDataServer.Bindings.Add.Port := ClientPort;
tcpBusDataServer.Active := True;
Started := True;
end;
procedure TBus_Server.ServiceStop(Sender: TObject; var Stopped: Boolean);
begin
ServiceShutdown(Sender);
Stopped := True;
end;
procedure TBus_Server.ServiceShutdown(Sender: TObject);
begin
tcpBusDataServer.Active := False;
cdsBusMonitor.Close;
cdsMemberState.Close;
DeviceDataLock.Free;
FunctionMon.Free;
FunctionList.Free;
end;
procedure TBus_Server.tcpBusDataServerExecute(AContext: TIdContext);
var
LSB, MSB: Byte;
List: TList;
Ctx: TIdContext;
begin
LSB := AContext.Connection.IOHandler.ReadByte;
MSB := AContext.Connection.IOHandler.ReadByte;
if AContext.Binding.Port = ClientPort then // Client
begin
DeviceDataLock.Enter;
try
DeviceLSB := LSB;
DeviceMSB := MSB;
finally
DeviceDataLock.Leave;
end;
List := tcpBusDataServer.Contexts.LockList;
try
for var i := 0 to List.count - 1 do
begin
Ctx := TIdContext(List[I]);
if (Ctx <> AContext) and (Ctx.Binding.Port = DevicePort) then
begin
Ctx.Connection.IOHandler.Write(LSB);
Ctx.Connection.IOHandler.Write(MSB);
// Since only 1 has to be written to
Break;
end;
end;
finally
tcpBusDataServer.Contexts.UnlockList;
end;
end
else if AContext.Binding.Port = DevicePort then // Device
begin
List := tcpBusDataServer.Contexts.LockList;
try
for var i := 0 to List.count - 1 do
begin
Ctx := TIdContext(List[I]);
if (Ctx <> AContext) and (Ctx.Binding.Port = ClientPort) then
begin
Ctx.Connection.IOHandler.Write(LSB);
Ctx.Connection.IOHandler.Write(MSB)
end;
end
finally
tcpBusDataServer.Contexts.UnlockList;
TThread.Queue(nil,
procedure
begin
TranslateData(LSB, MSB);
end
);
end;
end;
end;
procedure TBus_Server.TranslateData(ClientLSB, ClientMSB: Byte);
const {$J+}
LastSave: TDateTime = 0;
type
TProgramState = (psNone,psProgram,psIgnore1,psIgnore2);
const
ProgramState: TProgramState = psNone;
const
ValueMode: Boolean = False;
var
i: Integer;
fct: Integer;
GroupMon: Integer;
AddressMon: Integer;
CorrecteSettings: Boolean;
TmpFunc, TmpCodeValue: string;
TmpDeviceLSB, TmpDeviceMSB: Byte;
begin
fct := 0;
// Functie uit MSB halen
if ClientMSB >= 128 then
begin
Dec(ClientMSB, 128);
fct := 8;
end;
if ClientMSB >= 64 then
begin
Dec(ClientMSB, 64);
Inc(fct, 4);
end;
if ClientMSB >= 32 then
begin
Dec(ClientMSB, 32);
Inc(fct, 2);
end;
if ClientMSB >= 16 then
begin
Dec(ClientMSB, 16);
Inc(fct, 1);
end;
// Variabelen voor monitor bepalen
TmpFunc := FunctionList[fct];
FunctionMon.Value := TmpFunc;
if cdsBusMonitor.Active then
begin
cdsBusMonitor.Filtered := False;
cdsBusMonitor.Append;
try
cdsBusMonitor.FieldByName('Nr').AsInteger := cdsBusMonitor.RecordCount + 1;
cdsBusMonitor.FieldByName('AFct').Asstring := TmpFunc;
cdsBusMonitor.FieldByName('Aad').AsInteger := ClientLSB;
cdsBusMonitor.FieldByName('Agr').AsInteger := ClientMSB;
cdsBusMonitor.FieldByName('Time').AsString := TimeToStr(Now);
cdsBusMonitor.FieldByName('Origin').AsString := 'Van de Bus: '{ + UserPeerIP};
cdsBusMonitor.Post;
except
cdsBusMonitor.Cancel;
raise;
end;
end;
if ProgramState = psNone then
begin
CodeSite.Send('New situation...');
try
DeviceDataLock.Enter;
try
TmpDeviceLSB := DeviceLSB;
TmpDeviceMSB := DeviceMSB;
finally
DeviceDataLock.Leave;
end;
if cdsIncoming.Locate('Group;Address', VarArrayOf([IntToStr(TmpDeviceMSB),IntToStr(TmpDeviceLSB)]), []) then
begin
CodeSite.Send('After locate...');
if cdsMemberState.Locate('MemberID', cdsIncoming.FieldByName('MemberID').AsString, []) then
cdsMemberState.Edit
else
cdsMemberState.Append;
try
TmpCodeValue := CodeValue;
if cdsMemberStateState.AsString = TmpCodeValue then
begin
CodeSite.Send('New state ' + TmpCodeValue + ' already known');
cdsMemberState.Cancel;
end
else
begin
CodeSite.Send('New state ' + TmpCodeValue);
cdsMemberStateState.AsString := TmpCodeValue;
if TmpCodeValue = 'Reset' then
cdsMemberStateTotaal.AsFloat := cdsMemberStateTotaal.AsFloat + (Now - cdsMemberStateDateTime.AsDateTime);
cdsMemberStateDateTime.AsDateTime := Now;
cdsMemberState.Post;
end;
except
cdsMemberState.Cancel;
raise;
end;
end
else
CodeSite.SendError('ServerMethodsBServer.cdsIncoming Locate Fail');
except
on E: Exception do
CodeSite.SendException(E);
end
end;
if ((cdsBusMonitor.RecordCount mod 100) = 0) or ((Now - LastSave) > (1/24/60)) then
begin
LastSave := Now;
cdsBusMonitor.MergeChangeLog;
cdsBusMonitor.SaveToFile;
cdsMemberState.MergeChangeLog;
cdsMemberState.SaveToFile;
end;
end;
Good day sir/ma
i want to create a status bar with a rolling information like
Os version
current User Name
Date and time
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, ComCtrls;
type
TForm1 = class(TForm)
tmr2: TTimer;
stsbr: TStatusBar;
procedure tmr2Timer(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.tmr2Timer(Sender: TObject);
begin
if tmr2.Interval = 3000 then begin
stsbr.Panels[1].Text:= FormatDateTime('dddd' + ', ' + 'dd/mm/yyyy',date) + ', ' + TimeToStr(Time);
tmr2.Interval := 3001;
end else if tmr2.Interval = 3001 then begin
tmr2.Interval := 3002;
stsbr.Panels[1].Text:= 'PC Owner: '+GetUsersName+ ' - '+ GetLocalPCName;
end else if tmr2.Interval = 3002 then begin
tmr2.Interval := 3003;
stsbr.Panels[1].Text:= GetOSVersion;
end else if tmr2.Interval = 3003 then begin
tmr2.Interval := 3000;
stsbr.Panels[1].Text:= GetCPUName;
end;
procedure Form.FormCreate(Sender: TObject);
begin
tmr2Timer(Sender);
end;
end
.
that my full code
what i wanted to Achieve was a moving Information on a status bar
Please Help if u can
thanks..
You should not use Timer.Interval as lookout value to determine which data you should show in status bar. Use separate variable to do that. It will make your code cleaner.
unit Unit1;
interface
uses
Winapi.Windows, System.SysUtils, System.Classes, System.Win.Registry,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.ComCtrls, Vcl.ExtCtrls;
type
TForm1 = class(TForm)
tmr2: TTimer;
stsbr: TStatusBar;
procedure FormCreate(Sender: TObject);
procedure tmr2Timer(Sender: TObject);
private
status: integer;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function GetUsersName: string;
var
Buf: array [0 .. MAX_PATH] of Char;
BufSize: longword;
begin
Buf[0] := #$00;
BufSize := MAX_PATH;
if Winapi.Windows.GetUserName(Buf, BufSize) then Result := Buf
else Result := '';
end;
function GetLocalPCName: string;
var
Buf: array [0 .. MAX_COMPUTERNAME_LENGTH] of Char;
BufSize: longword;
begin
Buf[0] := #$00;
BufSize := MAX_COMPUTERNAME_LENGTH;
if Winapi.Windows.GetComputerName(Buf, BufSize) then Result := Buf
else Result := '';
end;
function GetOSVersion: string;
begin
Result := TOSVersion.ToString;
end;
function GetCPUName: string;
var
Reg: TRegistry;
begin
Result := '';
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_LOCAL_MACHINE;
if Reg.OpenKeyReadOnly('\Hardware\Description\System\CentralProcessor\0') then
begin
Result := Reg.ReadString('ProcessorNameString');
Reg.CloseKey;
end;
finally
Reg.Free;
end;
end;
procedure TForm1.tmr2Timer(Sender: TObject);
begin
case status of
0 : stsbr.Panels[1].Text:= FormatDateTime('dddd' + ', ' + 'dd/mm/yyyy',date) + ', ' + TimeToStr(Time);
1 : stsbr.Panels[1].Text:= 'PC Owner: ' + GetUsersName + ' - ' + GetLocalPCName;
2 : stsbr.Panels[1].Text:= GetOSVersion;
else stsbr.Panels[1].Text:= GetCPUName;
end;
inc(status);
if status > 3 then status := 0;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
status := 0;
// this property can also be set through IDE form designer
tmr2.Enabled := true;
// show initial status data
tmr2Timer(Sender);
end;
end.
If you have two random files e.g. .txt, .csv, .jpg, and you wanted to open two of them and make them take up the screen 50% 50%.
How would you find the window handle that was opened so that you can re size the right one?
I have edited below to be closer to the answer thanks to suggestions from David Heffernan and Rob Kennedy
The code below kind of works if everything goes right but i'm sure there are ways to improve the code.
Using ShellExecuteEx can return a process ID, you can get a window handle off the process ID by using EnumWindows checking against the process id. Then if everything works you can re size the form using MoveWindow
i have an example in the unit uFileStuff below
There are a few issues that i'm not sure can be resolved
Files can be opened in the same application e.g. notepad++.
ShellExecuteEx may not return a process id
EnumWindows may not find the window
Unit uFileStuff
unit uFileStuff;
interface
uses Winapi.Windows, System.SysUtils, Generics.Collections, shellapi, Winapi.Messages, Vcl.Dialogs, Vcl.Forms;
type
PWindowSearch = ^TWindowSearch;
TWindowSearch = record
TargetProcessID: DWord;
ResultList: TList<HWnd>;
end;
TMyFile = class
private
sFileNameAndPath : String;
MyProcessID : DWord;
MyParentProcessID : Dword;
Procedure OpenFile(sFile: String);
procedure UpdateWindowListByProcessID;
public
WindowsLinkedToProcessID : TList<HWnd>;
function GetWindowInformation(Wnd: HWnd) : String;
function GetAllWindowInformation : String;
property ProcessID : Dword read MyProcessID;
property ParentProcessID : Dword read MyParentProcessID;
constructor Create(sFile : String);
destructor Destroy; override;
end;
implementation
constructor TMyFile.Create(sFile: String);
begin
MyProcessID := 0;
MyParentProcessID := 0;
sFileNameAndPath := sFile;
WindowsLinkedToProcessID := TList<HWnd>.Create;
if (sFile <> '') and FileExists(sFile) then
OpenFile(sFileNameAndPath);
end;
destructor TMyFile.Destroy;
begin
WindowsLinkedToProcessID.Free;
Inherited;
end;
function TMyFile.GetAllWindowInformation: String;
var i : Integer;
sMessage : String;
begin
result := '';
for I := 0 to WindowsLinkedToProcessID.Count -1 do begin
sMessage := sMessage + #13#10 + GetWindowInformation(WindowsLinkedToProcessID[i]);
end;
result := result + sMessage;
end;
function TMyFile.GetWindowInformation(Wnd: HWnd): String;
var Buffer: array[0..255] of char;
begin
result := inttostr(Wnd);
SendMessage(Wnd, WM_GETTEXT, 255, LongInt(#Buffer[0]));
if Buffer <> '' then begin
result := result + ', ' + Buffer;
end;
end;
procedure TMyFile.OpenFile(sFile: String);
var i : Integer;
SEInfo: TShellExecuteInfo;
ExitCode: DWORD;
ExecuteFile, ParamString, StartInString, sMessage: string;
begin
ExecuteFile:=sFile;
FillChar(SEInfo, SizeOf(SEInfo), 0);
SEInfo.cbSize := SizeOf(TShellExecuteInfo);
with SEInfo do
begin
fMask := SEE_MASK_NOCLOSEPROCESS;
Wnd := Application.Handle;
lpFile := PChar(sFile);
nShow := SW_SHOWNORMAL;
end;
if ShellExecuteEx(#SEInfo) then
begin
if SEInfo.hProcess > 0 then begin
Sleep(100);
WaitForInputIdle(SEInfo.hProcess, 10000 );
MyProcessID := GetProcessId( SEInfo.hProcess );
UpdateWindowListByProcessID;
end else begin
ShowMessage('No Process ' + SysErrorMessage(GetLastError) );
end;
end else
ShowMessage('Error starting "'+ sFile +'"' + #13#10 + SysErrorMessage(GetLastError));
end;
procedure TMyFile.UpdateWindowListByProcessID;
function SelectWindowByProcessID(Wnd: HWnd; Param: LParam): Bool; stdcall;
var
pSearchRec: PWindowSearch;
WindowPid: DWord;
begin
pSearchRec := PWindowSearch(Param);
Assert(Assigned(pSearchRec));
GetWindowThreadProcessID(Wnd, WindowPid);
if (WindowPid = pSearchRec.TargetProcessID) and IsWindowVisible(Wnd) then
pSearchRec.ResultList.Add(Wnd);
Result := True;
end;
var
SearchRec: TWindowSearch;
begin
if MyProcessID > 0 then begin
SearchRec.TargetProcessID := MyProcessID;
SearchRec.ResultList := WindowsLinkedToProcessID;
EnumWindows(#SelectWindowByProcessID, LParam(#SearchRec));
end;
end;
end.
Form Creating Files on create - has button to open them
unit fFileOpen;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TfrmFileOpen = class(TForm)
btn1: TButton;
procedure btn1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
sApplicationPath : String;
sFile1, sFile2 : String;
public
{ Public declarations }
end;
var
frmFileOpen: TfrmFileOpen;
implementation
uses uFileStuff;
{$R *.dfm}
procedure TfrmFileOpen.btn1Click(Sender: TObject);
var File1 : TMyFile;
File2 : TMyFile;
begin
File1 := TMyFile.Create( sFile1 );
try
if sFile2 <> sFile1 then
File2 := TMyFile.Create( sFile2 )
else
File2 := TMyFile.Create( '' );
try
if (File1.ProcessID > 0) and (File2.ProcessID > 0) then begin
if (File1.ParentProcessID > 0) and (File2.ParentProcessID > 0) and (File1.ParentProcessID = File2.ParentProcessID) then begin
showmessage('Both Files opened in same process');
end else if (File1.WindowsLinkedToProcessID.Count > 0) and (File2.WindowsLinkedToProcessID.Count > 0) then begin
if File1.WindowsLinkedToProcessID.Count > 1 then
ShowMessage('Warning returned more than 1 window Moving the first window' + #13#10 + File1.GetAllWindowInformation);
MoveWindow(File1.WindowsLinkedToProcessID[0], 0, 0, Trunc(Screen.WorkAreaWidth / 2), Screen.WorkAreaHeight, True);
if File2.WindowsLinkedToProcessID.Count > 1 then
ShowMessage('Warning returned more than 1 window Moving the first window' + #13#10 + File2.GetAllWindowInformation);
MoveWindow(File2.WindowsLinkedToProcessID[0], Round(Screen.WorkAreaWidth / 2)+1, 0, Trunc(Screen.WorkAreaWidth / 2), Screen.WorkAreaHeight, True);
end;
end;
finally
File2.Free;
end;
finally
File1.Free;
end;
end;
procedure TfrmFileOpen.FormCreate(Sender: TObject);
var slTemp : TStringList;
img : TBitmap;
begin
ReportMemoryLeaksOnShutdown := true;
sApplicationPath := ExtractFileDir(application.ExeName);
sFile1 := IncludeTrailingPathDelimiter( sApplicationPath ) + 'File1.txt';
sFile2 := IncludeTrailingPathDelimiter( sApplicationPath ) + 'File2.csv';
{
if not FileExists( sFile1 ) then begin
img := TBitmap.Create;
img.SetSize(300,300);
img.SaveToFile( sFile1 );
img.Free;
end; }
if not FileExists(sFile1) then begin
slTemp := TStringList.Create;
slTemp.Add('File1');
slTemp.SaveToFile(sFile1);
slTemp.Free;
end;
if not FileExists(sFile2) then begin
slTemp := TStringList.Create;
slTemp.Add('File2');
slTemp.SaveToFile(sFile2);
slTemp.Free;
end;
end;
end.
Can someone give me the code to encrypt and decrypt a Unicode strings in delphi firemonkey Mobile?
I've tried everything with xor with other libraries , and nothing.
There are always characters that are not recognized as the euro symbol € .
If someone could help me , would be appreciated.
Edit:
Thank you Hans, but always I have the same problem with stringstream . This code works perfectly in windows , but ios gives me this error : "No mapping for the Unicode character exists in the target multibyte code page"
unit UMain;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, ElAES,
FMX.StdCtrls, FMX.Layouts, FMX.Memo, Math;
type
TForm2 = class(TForm)
ToolBar1: TToolBar;
Label1: TLabel;
Label2: TLabel;
Memo1: TMemo;
Layout1: TLayout;
Button1: TButton;
Button3: TButton;
procedure Button1Click(Sender: TObject);
procedure Button3Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
const
PASSWORD = '1234';
var
Form2: TForm2;
implementation
{$R *.fmx}
{$R *.iPhone.fmx IOS}
function StringToHex(S: string): string;
var
i: integer;
begin
Result := '';
// Go throught every single characters, and convert them
// to hexadecimal...
for i := 1 to Length( S ) do
Result := Result + IntToHex( Ord( S[i] ), 2 );
end;
function HexToString(S: string): string;
var
i: integer;
begin
Result := '';
// Go throught every single hexadecimal characters, and convert
// them to ASCII characters...
for i := 1 to Length( S ) do
begin
// Only process chunk of 2 digit Hexadecimal...
if ((i mod 2) = 1) then
Result := Result + Chr( StrToInt( '0x' + Copy( S, i, 2 )));
end;
end;
procedure TForm2.Button1Click(Sender: TObject);
var
Source: TStringStream;
Dest: TStringStream;
Key: TAESKey128;
begin
try
Source := TStringStream.Create( Memo1.Text );
Dest := TStringStream.Create('');
FillChar( Key, SizeOf(Key), 0 );
Move( PChar(PASSWORD)^, Key, Min( SizeOf( Key ), Length( PASSWORD )));
EncryptAESStreamECB( Source, 0, Key, Dest );
//Memo1.Lines.BeginUpdate;
Memo1.Text := Dest.DataString;
//Memo1.Lines.EndUpdate;
Label2.Text := 'Texto Encriptado';
Source.Free;
Dest.Free;
except on E: Exception do
begin
ShowMessage(e.ToString);
Source.Free;
Dest.Free;
Memo1.Text :='';
end;
end;
end;
procedure TForm2.Button3Click(Sender: TObject);
var
Source: TStringStream;
Dest: TStringStream;
Key: TAESKey128;
Size: integer;
begin
try
Source := TStringStream.Create(Trim(Memo1.Text) );
Dest := TStringStream.Create('');
Size := Source.Size;
Source.ReadBuffer(Size, SizeOf(Size));
FillChar(Key, SizeOf(Key), 0);
Move(PChar(PASSWORD)^, Key, Min(SizeOf(Key), Length(PASSWORD)));
Source.Position := 0;
DecryptAESStreamECB(Source, Source.Size - Source.Position, Key, Dest);
Memo1.Text := Trim(Dest.DataString);
Label2.Text := 'Texto Original';
Source.Free;
Dest.Free;
except on E: Exception do
begin
ShowMessage(e.ToString);
Source.Free;
Dest.Free;
Memo1.Text :='';
end;
end;
end;
end.
I've also tried to create stringstream with this:
Source := TStringStream.Create(Trim(Memo1.Text) , TEncoding.Unicode) ;
and sometimes works well and sometimes gives me the following error:"Los surrogate char without a preceding high surrogate char at index: 8. chaeck that the string is encoded properly.
Any ideas?
Use standardized libraries instead of trying to make your own encryption solution. There are for example several implementation of AES encryption available for Delphi (e.g. Eldos which is included in the NativeXML library).
Write your string (MyString) to a stream and encrypt it:
var
lSourceStream: TStringStream;
lDestinationStream: TMemoryStream;
begin
lSourceStream := TStringStream.Create(MyString);
lDestinationStream := TMemoryStream.Create;
AESencrypt(lSourceStream,lDestinationStream);
lDestinationStream.SaveToFile(<filename>);
end;
As the title states, having a bit of trouble mostly with the algorithm. I have the algorithm down to parse through and print ALL the values, but I need to be able to stop on each value and display it in a label. I tried using a global variable 'count', thinking I may be able to stop it. Any ideas? This is what I have so far:
unit Unit4;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm4 = class(TForm)
Createtree: TButton;
Current: TLabel;
RecursiveNext: TButton;
Close: TButton;
IterativeNext: TButton;
Iterative: TLabel;
procedure CloseClick(Sender: TObject);
procedure CreatetreeClick(Sender: TObject);
procedure RecursiveNextClick(Sender: TObject);
procedure IterativeNextClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
nodeptr = ^nodetype;
nodetype = record
id : integer ;
left,right : nodeptr ;
threaded : Boolean;
end;
var
Form4: TForm4;
inf : textfile;
root,c : nodeptr;
count : integer;
implementation
{$R *.dfm}
function Emptytree(var root : nodeptr) : Boolean;
begin
Result := False;
if root^.id <> NULL then Result := True;
end;
procedure TForm4.CloseClick(Sender: TObject);
begin
application.Terminate;
end;
function recursiveinorder(c : nodeptr) : integer;
begin
if c^.left <> nil then recursiveinorder(c^.left);
Result := c^.id;
if (c^.right <> nil) and (count > 0) then recursiveinorder(c^.right);
count := count - 1;
end;
function iterativeinorder(c : nodeptr) : integer;
var done : boolean;
begin
c := root;
while c <> nil do c := c^.left;
begin
done := false;
while done = False do
begin
Result := c^.id;
if (c^.threaded = false) and (count > 0) then
begin
c := c^.right;
done := true;
end
else
begin
c := c^.right;
if c = nil then done := true;
end;
end;
end;
count := count - 1;
end;
procedure TForm4.CreatetreeClick(Sender: TObject);
var
parent, knew, c : nodeptr;
begin
count := 0;
new(parent);
new(c);
new(root);
assignfile(inf, 'treedata.txt');
reset(inf);
readln(inf,root^.id);
while not eof(inf) do
begin
new(knew);
readln(inf,knew^.id);
if not Emptytree(root) then
begin
c:= root;
while c <> nil do
begin
parent := c;
if knew^.id < c^.id then
c:=c^.left
else c:= c^.right;
end;
if knew^.id < parent^.id then
begin
parent^.left := knew;
knew^.threaded := True;
knew^.right := parent;
end
else begin
knew^.right := parent^.right;
knew^.threaded := parent^.threaded;
parent^.right := knew;
parent^.threaded := false;
end;
end;
if Emptytree(root) then root := knew;
end;
end;
procedure TForm4.IterativeNextClick(Sender: TObject);
begin
count := count + 1;
Iterative.Caption := inttostr(iterativeinorder(root));
end;
procedure TForm4.RecursiveNextClick(Sender: TObject);
begin
count := count + 1;
Current.Caption := inttostr(recursiveinorder(root));
end;
end.