using TfrxRect and GetRealBounds in a script - fastreport

i am tying to use the script
procedure BarCodeResize(Bc : TfrxBarCodeView);
var
r : TfrxRect;
begin
bc.Text := '1234567890 test 1234567890 asdf 1234567890';
r := bc.GetRealBounds;
bc.Zoom := bc.Width/(r.Right - r.Left);
frxReport1.ShowReport();
end;
but it shows me an error at line 'r : TfrxRect;'

Explanation:
If I understand your question correctly, you may consider the following:
If you want to use a type, you need to call AddType() method of TfsScript class.
In FastScript, only published methods of added classes are implemented. If you want to use public method (GetRealBounds for example), you need to use AddMethod() method of TfsScript.
The problem with your script (even if you add type TfrxRect) is, that TfrxRect is packed record, and FastScript do not support records. So, you may define your own TfrxBarCodeView class with appropriate methods.
Тхе example белов demonstrates one possible way to solve your problem (it uses Delphi, but I was able to reproduce and solve your error).
Delphi part:
unit FMainBarcode;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, frxClass, fs_iinterpreter, frxBarcode;
type
TForm1 = class(TForm)
Button1: TButton;
frxReport1: TfrxReport;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TfrxMyBarCodeView = class(TfrxBarCodeView)
public
constructor Create(AOwner: TComponent); override;
function GetRealBoundsRight: double;
function GetRealBoundsLeft: double;
end;
type
TFunctions = class(TfsRTTIModule)
private
function CallMethod(Instance: TObject; ClassType: TClass; const MethodName: String; Caller: TfsMethodHelper): Variant;
public
constructor Create(AScript: TfsScript); override;
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
// Show report
frxReport1.ShowReport;
end;
{ TMyfrxBarCodeView }
constructor TfrxMyBarCodeView.Create(AOwner: TComponent);
begin
inherited Create(AOwner);
end;
function TfrxMyBarCodeView.GetRealBoundsLeft: double;
begin
Result := GetRealBounds.Left;
end;
function TfrxMyBarCodeView.GetRealBoundsRight: double;
begin
Result := GetRealBounds.Right;
end;
{ TFunctions }
constructor TFunctions.Create(AScript: TfsScript);
begin
inherited Create(AScript);
// Add type
AScript.AddType('TfrxRect', fvtVariant);
// Add public method
with AScript.FindClass('TfrxBarCodeView') do begin
AddMethod('function GetRealBounds: TfrxRect', CallMethod);
end{with};
// Add class and public methods
with AScript.AddClass(TfrxMyBarCodeView, 'TfrxBarCodeView') do begin
AddMethod('function GetRealBoundsRight: double', CallMethod);
AddMethod('function GetRealBoundsLeft: double', CallMethod);
end{with};
end;
function TFunctions.CallMethod(Instance: TObject; ClassType: TClass; const MethodName: String; Caller: TfsMethodHelper): Variant;
begin
Result := 0;
if (ClassType = TfrxBarCodeView) and (MethodName = 'GETREALBOUNDS') then begin
//Result := TfrxBarCodeView(Instance).GetRealBounds;
end{if};
if (ClassType = TfrxMyBarCodeView) and (MethodName = 'GETREALBOUNDSRIGHT') then begin
Result := TfrxMyBarCodeView(Instance).GetRealBoundsRight;
end{if};
if (ClassType = TfrxMyBarCodeView) and (MethodName = 'GETREALBOUNDSLEFT') then begin
Result := TfrxMyBarCodeView(Instance).GetRealBoundsLeft;
end{if};
end;
initialization
fsRTTIModules.Add(TFunctions);
end.
Fast report code:
procedure BarCodeResize(Bc : TfrxMyBarCodeView);
var
R, L : double;
begin
Bc.Text := '1234567890 test 1234567890 asdf 1234567890';
R := Bc.GetRealBoundsRight * fr1CharX / fr01cm;
L := Bc.GetRealBoundsLeft * fr1CharX / fr01cm;
Bc.Zoom := Bc.Width / (R - L);
ShowMessage('Right: ' + FloatToStr(R) + ', Left: ' + FloatToStr(L));
end;
procedure Page1OnBeforePrint(Sender: TfrxComponent);
begin
BarCodeResize(TfrxMyBarCodeView(BarCode1));
end;
begin
end.
Notes:
Tested with Delphi 7, FastReport 4.7, FastScript 1.7.

Related

Delphi: Setting timestamp of file wrong by one hour for some files

I want to correct the last-access time of some files.
Doing that programmatically fails - by one hour - for some files.
Mysterious, as I do correct the date according to the timezone.
Here is an example, tested for Delphi2010 (that's where I would need code for) and Delphi10.3
function GetTempDir : string;
var path : array[0..MAX_PATH] of char;
PathStr : string;
begin
GetTempPath(MAX_PATH, path);
PathStr:=path;
result:=IncludeTrailingPathDelimiter(PathStr);
end;
function UTCDateTimeFromLocalDateTime(const LocalDateTime: TDateTime): TDateTime;
var
LocalSystemTime: TSystemTime;
UTCSystemTime: TSystemTime;
LocalFileTime: TFileTime;
UTCFileTime: TFileTime;
begin
DateTimeToSystemTime(LocalDateTime, UTCSystemTime);
SystemTimeToFileTime(UTCSystemTime, UTCFileTime);
if LocalFileTimeToFileTime(UTCFileTime, LocalFileTime)
and FileTimeToSystemTime(LocalFileTime, LocalSystemTime) then begin
Result := SystemTimeToDateTime(LocalSystemTime);
end else begin
Result := LocalDateTime;
end;
end;
function SetFileTimesHelper(const FileName: string; DateTime: TDateTime): Boolean;
var
Handle: THandle;
FileTime: TFileTime;
SystemTime: TSystemTime;
begin
Result := False;
Handle := CreateFile(PChar(FileName), GENERIC_WRITE, FILE_SHARE_READ, nil,
OPEN_EXISTING, 0, 0);
if Handle <> INVALID_HANDLE_VALUE then
try
DateTime:=UTCDateTimeFromLocalDateTime(DateTime);
DateTimeToSystemTime(DateTime, SystemTime);
FileTime.dwLowDateTime := 0;
FileTime.dwHighDateTime := 0;
if SystemTimeToFileTime(SystemTime, FileTime) then
begin
Result := SetFileTime(Handle, nil, nil, #FileTime);
end;
finally
CloseHandle(Handle);
end;
end;
procedure TForm1.CreateDateFile(Dat : TDateTime);
var FileName : string;
begin
FileName:=GetTempDir+FormatDateTime('yyyymmdd hhnnss', Dat)+'.txt';
Memo1.Lines.Add(FileName);
with TStringList.Create do
begin
text:='1';
SaveToFile(FileName);
Free;
end;
SetFileTimesHelper(FileName, Dat);
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
CreateDateFile(StrToDateTime('23.02.2013 11:11:11'));
CreateDateFile(StrToDateTime('06.05.2014 22:22:22')); // file dated 23:22:22
end;
The first file is correctly dated, the 2nd is displayed in the windows explorer with 23:22:22.
What do I miss?
Thanks brian for the comment.
Digging that way I found a solution:
GetTimeZoneInformation(tz);
SystemTimeToTzSpecificLocalTime(#tz, UTCSystemTime, LtSystemTime);
I didn't find the inverse function of SystemTimeToTzSpecificLocalTime, but I used this function and then see the resulting offset. Then I invert the offset and the time is properly corrected.

Call to a volatile function in interfering context is not allowed in SPARK

I'm currently learning Ada during a university course on real-time programming languages and have a question about SPARK.
I'm working on a project with a task that monitors an off-grid power supply. This task is crucial for machine safety and should therefore be as error free as possible, say proven with SPARK. I was able to get a few things running with other questions on stackoverflow but I still run into errors that I was not able to fix with quick searches in the user guide.
The error is call to a volatile function in interfering context is not allowed in SPARK with reference to the line if monitoring_interface.is_all_config_set then ... in
task body monitoring_task is
next_time : Time;
begin
-- Initialisation of next execution time
next_time := Clock;
-- Superloop
loop
Put_Line ("Run task monitoring");
-- Load monitor configuration
monitor_pfc_voltage.config := monitoring_interface.get_monitor_pfc_voltage_config;
monitor_pfc_current.config := monitoring_interface.get_monitor_pfc_current_config;
monitor_output_voltage.config := monitoring_interface.get_monitor_output_voltage_config;
monitor_output_current.config := monitoring_interface.get_monitor_output_current_config;
-- Check if module has been configured correctly
-- Don't do anything otherwise
if monitoring_interface.is_all_config_set then -- <= erroneous line
do_monitoring;
end if;
next_time := next_time + TASK_PERIOD;
delay until next_time;
end loop;
end monitoring_task;
The function is_all_config_set is defined within a protected type that I use for inter task communication.
package PSU_Monitoring is
... Declaration of some types (Monitor_Config_T) ...
protected type Monitoring_Interface_T is
function is_all_config_set return Boolean;
procedure set_monitor_pfc_voltage_config (new_monitor_config : in Monitor_Config_T);
function get_monitor_pfc_voltage_config return Monitor_Config_T;
procedure set_monitor_pfc_current_config (new_monitor_config : in Monitor_Config_T);
function get_monitor_pfc_current_config return Monitor_Config_T;
procedure set_monitor_output_voltage_config (new_monitor_config : in Monitor_Config_T);
function get_monitor_output_voltage_config return Monitor_Config_T;
procedure set_monitor_output_current_config (new_monitor_config : in Monitor_Config_T);
function get_monitor_output_current_config return Monitor_Config_T;
private
-- Configuration for PFC intermediate voltage
monitor_pfc_voltage_config : Monitor_Config_T;
monitor_pfc_voltage_config_set : Boolean := False;
-- Configuration for PFC inductor current
monitor_pfc_current_config : Monitor_Config_T;
monitor_pfc_current_config_set : Boolean := False;
-- Configuration for output voltage
monitor_output_voltage_config : Monitor_Config_T;
monitor_output_voltage_config_set : Boolean := False;
-- Configuration for output inductor current
monitor_output_current_config : Monitor_Config_T;
monitor_output_current_config_set : Boolean := False;
end Monitoring_Interface_T;
monitoring_interface : Monitoring_Interface_T;
private
... Declaration of a task and some private constants and subprograms ...
end PSU_Monitoring
The respective body is
package body PSU_Monitoring is
protected body Monitoring_Interface_T is
function is_all_config_set return Boolean is
begin
return monitor_pfc_voltage_config_set and monitor_pfc_current_config_set and monitor_output_voltage_config_set and monitor_output_current_config_set;
end is_all_config_set;
procedure set_monitor_pfc_voltage_config (new_monitor_config : in Monitor_Config_T) is
begin
monitor_pfc_voltage_config := new_monitor_config;
monitor_pfc_voltage_config_set := True;
end set_monitor_pfc_voltage_config;
function get_monitor_pfc_voltage_config return Monitor_Config_T is
begin
return monitor_pfc_voltage_config;
end get_monitor_pfc_voltage_config;
procedure set_monitor_pfc_current_config (new_monitor_config : in Monitor_Config_T) is
begin
monitor_pfc_current_config := new_monitor_config;
monitor_pfc_current_config_set := True;
end set_monitor_pfc_current_config;
function get_monitor_pfc_current_config return Monitor_Config_T is
begin
return monitor_pfc_current_config;
end get_monitor_pfc_current_config;
procedure set_monitor_output_voltage_config (new_monitor_config : in Monitor_Config_T) is
begin
monitor_output_voltage_config := new_monitor_config;
monitor_output_voltage_config_set := True;
end set_monitor_output_voltage_config;
function get_monitor_output_voltage_config return Monitor_Config_T is
begin
return monitor_output_voltage_config;
end get_monitor_output_voltage_config;
procedure set_monitor_output_current_config (new_monitor_config : in Monitor_Config_T) is
begin
monitor_output_current_config := new_monitor_config;
monitor_output_current_config_set := True;
end set_monitor_output_current_config;
function get_monitor_output_current_config return Monitor_Config_T is
begin
return monitor_output_current_config;
end get_monitor_output_current_config;
end Monitoring_Interface_T;
... Definition of the remaining subprograms defined in the specification file ...
end PSU_Monitoring;
What is the problem here?
As Jeffrey was saying, we need to see the part of the program where the error is flagged. In general, this is related to functions with side effects, see reference manual:
http://docs.adacore.com/spark2014-docs/html/lrm/packages.html#external-state-variables
The same error message can be observed if you use the Clock function from the Real-Time package in the "wrong" way:
with Ada.Real_Time; use Ada.Real_Time;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main with SPARK_Mode is
Last : Time := Clock;
begin
-- some stuff happening here ...
if Clock > Last + Milliseconds(100) then
Put_Line("Too late");
end if;
end Main;
Clock is a function that has side effects (it returns different values every time you call it), and in this example the function is used in what's called an "interfering context" (see link above for a definition).
The solution would be to rewrite your code slightly:
with Ada.Real_Time; use Ada.Real_Time;
with Ada.Text_IO; use Ada.Text_IO;
procedure Main with SPARK_Mode is
Last : Time := Clock;
begin
-- some code
declare
now : Time := Clock;
begin
if now > Last + Milliseconds(100) then
Put_Line("Too late");
end if;
end;
end Main;
So, basically, what you do is isolate calls to functions with side effects into a separate statement, saving the result in a variable, and then use the variable where you had your call before. This trick should help with your call to the protected object, as well.

what is the best approach to make an http request

what is the best method to do this: i have a txt file filled with web addresses, i have to check all of them using idHTTP component,only a simple check from a web server, downloading the html and finding a match, i want it to to be fast, there is different types of threads and i am not sure what is the best to use, a TParallel for or Task threads or regular threads?
i tried before TParallel for and i got stuck at AV, also i've tried Task threads but its not fast, the http request becomes slower by time, i tried also the regular threads and i didnt know how to use it because its complicated to use.
note: Please do NOT downvote i just need advice from the experts. Thank you
First advice: do not use Indy. Use THTTPClient (unit System.Net.HttpClient) -- native for Delphi XE?+
I am still using old TThreads. I could make suggestion only with TThread.
Workflow:
Main thread -- reading your TXT file line by line.
After line was readed, you create NEW thread, which are downloading information from WWW.
Sample of application:
unit ufmMain;
interface
uses
Winapi.Windows, Winapi.Messages,
System.SysUtils, System.Variants,
{ TThread }
System.Classes,
Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TLoad = class(TThread)
protected
FURL,
FOutputFileName: String;
procedure Execute; override;
public
constructor Create(const AURL, AOutputFileName: String); overload;
end;
HTTP = class
public
class procedure Get(const AURL: String; out AOutputStream: TMemoryStream);
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
{ THTTPClient }
System.Net.HttpClient;
procedure TForm1.Button1Click(Sender: TObject);
var
LLoad: TLoad;
LFile: TextFile;
LCycle: Integer;
LUrl: String;
begin
LCycle := 0;
AssignFile(LFile, 'urls.txt');
try
Reset(LFile);
while not Eof(LFile) do
begin
{ Using for generate file name. All file names must be unique }
Inc(LCycle);
{ Read next URL }
ReadLn(LFile, LUrl);
{ Create new thread }
LLoad := TLoad.Create(LUrl, 'Output file No ' + LCycle.ToString + '.htm');
LLoad.FreeOnTerminate := True;
LLoad.Start;
end;
finally
CloseFile(LFile);
end;
end;
{ TLoad }
constructor TLoad.Create(const AURL, AOutputFileName: String);
begin
inherited Create(True);
FURL := AURL;
FOutputFileName := AOutputFileName;
end;
procedure TLoad.Execute;
var
LResponse: TMemoryStream;
begin
inherited;
LResponse := TStringStream.Create;
try
HTTP.Get(FURL, LResponse);
{ Save result to file }
LResponse.SaveToFile(GetCurrentDir + PathDelim + FOutputFileName);
finally
LResponse.Free;
end;
end;
{ HTTP }
class procedure HTTP.Get(const AURL: String; out AOutputStream: TMemoryStream);
var
LStream: TStream;
LHTTPClient: THTTPClient;
begin
LHTTPClient := THTTPClient.Create;
try
LStream := LHTTPClient.Get(AURL).ContentStream;
AOutputStream.CopyFrom(LStream, LStream.Size);
finally
LHTTPClient.Free;
end;
end;
end.
Why I against Indy:
1) THTTPClient do not required additional DLL for works with SSL protocol
2) THTTPClient is modern from Delphi XE8
3) My subjective opinion: THTTPClient works much more smoothly (with less issues) then Indy library. I used Indy for last 10 years, but now all my supported project moved to THTTPClient.
You can use TTask and Indy (TIdHTTP). Example:
function GetUrl(const aUrl: string): ITask;
begin
Result := TTask.Run(
procedure
var
FOutput: string;
FHTTP: TIdHTTP;
begin
FHTTP:=TIdHTTP.Create(nil);
try
try
FOutput:=FHTTP.Get(aUrl);
except
// handle errors
end;
finally
FHTTP.Free;
end;
TThread.Synchronize(nil,
procedure
begin
ProcessOutput(FOutput); // send your output/result to main thread
end);
end );
end;
procedure TForm1.Button1Click(Sender: TObject);
var
i: Integer;
list: TStringList;
begin
list:=TStringList.Create;
try
list.LoadFromFile('yourfile.txt');
// get all your urls
// you should control how many threads run at the same time
for i := 0 to list.Count-1 do
GetUrl(list[i]);
finally
list.Free;
end;
end;

Could not convert variant of type (Unknown) into type (Dispatch)

I´m trying to port the following javascript code to inno-setup pascal script:
var adminManager = new ActiveXObject('Microsoft.ApplicationHost.AdminManager');
var appPoolsSection = adminManager.GetAdminSection('system.applicationHost/applicationPools', 'MACHINE/WEBROOT/APPHOST');
var appPoolsCollection = applicationPoolsSection.Collection;
for (var i = 0; i < appPoolsCollection.Count; i++)
{
var appPool = appPoolsCollection.Item(i);
// doing someting with the application pool
}
This code has been translated to this:
var AdminManager, AppPoolsSection, AppPoolsCollection, AppPool: Variant;
i: Integer;
begin
AdminManager := CreateOleObject('Microsoft.ApplicationHost.AdminManager');
AppPoolsSection := AdminManager.GetAdminSection('system.applicationHost/applicationPools', 'MACHINE/WEBROOT/APPHOST');
AppPoolsCollection := AppPoolsSection.Collection;
for i := 0 to AppPoolsCollection.Count-1 do begin
AppPool := AppPoolsCollection.Item(i);
// doing someting with the application pool
end;
end;
But it is raising the following error on line AppPoolsCollection := AppPoolsSection.Collection:
Exception: Could not convert variant of type (Unknown) into type (Dispatch).
There are any thing that I can do to inform to pascal scritp that the AppPoolsSection object is an IDispach and not only an IUnknown?
I found a solution that works and is simpler than "import" the interfaces definition.
All the COM components used in this code implements IDispatch (it is required to use on VBScript or JScript), then I imported the VariantChangeType function to cast the IUnknown reference to an IDispatch reference (as it appears not to be supported on pascal script).
Follow the final code:
function VariantChangeType(out Dest: Variant; Source: Variant; Flags, vt: Word): HRESULT; external 'VariantChangeType#oleaut32.dll stdcall';
function VarToDisp(Source: Variant): Variant;
begin
Result := Unassigned;
OleCheck(VariantChangeType(Result, Source, 0, varDispatch));
end;
procedure EnumerateAppPools(AppPools: TStrings);
var AdminManager, Section, Collection, Item, Properties: Variant;
i: Integer;
begin
AdminManager := CreateOleObject('Microsoft.ApplicationHost.AdminManager');
Section := VarToDisp(AdminManager.GetAdminSection('system.applicationHost/applicationPools', 'MACHINE/WEBROOT/APPHOST'));
Collection := VarToDisp(Section.Collection);
for i := 0 to Collection.Count-1 do begin
Item := VarToDisp(Collection.Item(i));
Properties := VarToDisp(Item.Properties);
if (VarToDisp(Properties.Item('managedPipelineMode')).Value = 1) and
(VarToDisp(Properties.Item('managedRuntimeVersion')).Value = 'v4.0') then
AppPools.Add(VarToDisp(Properties.Item('name')).Value);
end;
end;

FreePascal: Free pointer to class

I'm writing a DLL to interface with Game Maker. (Game maker only deals in Doubles.)
Here's a function:
function CreateBitmap(W, H: Double): Double;
var
TBM: TBitmap;
begin
TBM := TBitmap.Create(Floor(W), Floor(H));
CreateBitmap := Double(Integer(#TBM));
end;
So when I get the Double back:
function DestroyBitmap(Handle: Double);
begin
<How do I free it?>
end;
How do I free this Double? I tried doing
function DestroyBitmap(Handle: Double);
var
Blittable: IBlittable;
begin
Blittable := Pointer(Floor(Handle))^
Blittable.Free;
end;
But since the pointer's type cannot be determined, the Blittable (IBlittable) cannot be set to "untyped".
How do I free this pointer without leaking memory?
Remove # - that gets the address of the local variable:
function CreateBitmap(W, H: Double): Double;
var
  TBM: TBitmap;
begin
  TBM := TBitmap.Create(Floor(W), Floor(H));
  CreateBitmap := Double(Integer(TBM));
end;
function DestroyBitmap(Handle: Double);
var
bmp: TBitmap;
begin
bmp := TBitmap(integer(Handle));
bmp.Free;
end;

Resources