How to initalize and populate TWideStringDynArray in Pascal? - delphi

I am calling a function for SOAP service and one of the required parameters is TWideStringDynArray. How do I initialize and populate this type of array? Or is there another way to convert a normal array to this type?
This is how it's defined in SOAP class
...
ArrayOfString = TWideStringDynArray;
...
PrsDataGet = class(TRemotable)
private
FsRetVal: WideString;
FsInParam: ArrayOfString;
published
property sRetVal: WideString read FsRetVal write FsRetVal;
property sInParam: ArrayOfString read FsInParam write FsInParam;
end;
If i set FsInParam as a array of WideString it says incompatible types
procedure TForm1.Button1Click(Sender: TObject);
var
QuotePrice : Real;
rezultat:WideString;
mnozica:array [0..4] of integer;
parametri: array of WideString;
obstaja:boolean;
PrsGet:PrsDataGet;
PrsFind:PrsDataGet;
begin
PrsGet.sRetVal :=rezultat;
SetLength(parametri, 4);
parametri[0]:= '1234';
parametri[1]:= 'wsprsinfotest';
parametri[2]:= 'efwefawf';
parametri[3]:= 'PRS_MN_P';
PrsGet.sInParam :=parametri;
If i change type of parametri to ArrayOfString it doesn't complain of incompatible types anymore but then it throws an Access Violation at SetLength(parametri, 4);
EDIT
To reproduce the problem:
I import WSDL definition into delphi from https://wwwt.ajpes.si/wsPrsInfo/PrsInfo.asmx?WSDL
Code for the service call:
unit UnKlicServisa;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Rio, SoapHTTPClient,Types,
UnWsPrsInfo_1, UnWsPrsInfo_2;
type
TForm1 = class(TForm)
HTTPRIO1: THTTPRIO;
Edit1: TEdit;
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
rezultat:WideString;
parametri: ArrayOfString;
obstaja:boolean;
PrsGet:PrsDataGet;
begin
PrsGet.sRetVal :=rezultat;
parametri[0]:= '1234';
parametri[1]:= 'wsprsinfotest';
parametri[2]:= '3423445';
parametri[3]:= 'PRS_MN_P';
PrsGet.sInParam :=parametri;
if Trim(Edit1.Text) <> '' then
begin
(HTTPRIO1 as PrsInfoSoap).PrsDataGet(PrsGet);
end
else
begin
MessageDlg('Enter a Valid ISBN code',mtInformation,[mbOk],0);
Edit1.SetFocus;
end;
end;
end.

Note FPC 3.2.0+ also allows array constructors
{$mode Delphi}
uses types;
var arr : TWideStringDynArray;
begin
arr:=TWideStringDynArray.Create('1234','wsprsinfotest','efwefawf','PRS_MN_P');
end.

Related

Incompatible types when add unit in main unit

I'm a new Delphi developer and there are strange happening problems. I have to add an unit called Filters in my main called Unit1 (default name). But, alaways when I try to run the code: [Error] Unit1.pas(48): Incompatible types. If I remore Filters from "uses", the code run. Someone knows how to solve this.
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, Menus, StdCtrls;
type
ArrayOfInteger = array of integer;
TMain = class(TForm)
MainMenu1: TMainMenu;
options: TMenuItem;
checkResult: TMenuItem;
GerarJogos1: TMenuItem;
exit: TMenuItem;
edtGame: TEdit;
mmoResult: TMemo;
btnConfirm: TButton;
procedure btnConfirmClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Main: TMain;
implementation
uses Utils, Error, Filters;
{$R *.dfm}
procedure TMain.btnConfirmClick(Sender: TObject);
var utils: TUtils;
var filter: TFilters;
var error: Errors;
var num: ArrayOfInteger;
var nstr, str: string;
begin
SetLength(num, 6);
FillChar(str, SizeOf(str), #0);
FillChar(nstr, SizeOf(nstr), #0);
utils := TUtils.Create;
filter := TFilters.Create;
error := Errors.Create;
str := edtGame.Text;
num := utils.strInput(str);
Line 48: num := utils.strInput(str);
unit Filters;
interface
uses Classes, SysUtils, Math;
type
ArrayOfInteger = array of integer;
TFilters = class
private
protected
public
Constructor Create;
// function isPair(number: integer): Boolean;
//function fSum(numbers: ArrayOfInteger): Boolean;
//function fNLNPNO(numbers: ArrayOfInteger): integer;
end;
implementation
Constructor TFilters.Create;
begin
Inherited Create;
end;
You have ArrayOfInteger defined in two different units, which is causing this error. Remove the definition from one of the units or specify in var num: ArrayOfInteger which definition you want you use, like: TMain.ArrayOfInteger

Saving record to file Error 'file access denied'

When I run my code an select the save button which i created. The record doesnt save but i get an error 'file access denied'.
my code :
The code i split into 2 units MainUnit and AddTenantUnit.
I think the problem lies within the procedure at the end of the code. If you scroll down I made it clear which procedure (TAddTenantForm.SaveButtonClick).
unit MainUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TMainForm = class(TForm)
AddTenantButton: TButton;
procedure FormCreate(Sender: TObject);
procedure AddTenantButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
TTenantRecord = record
FirstName : string[20];
LastName : string[20];
end;
var
MainForm: TMainForm;
Tenant : TTenantRecord;
TenantFile : file of TTenantRecord;
implementation
uses AddTenantUnit;
{$R *.dfm}
procedure TMainForm.AddTenantButtonClick(Sender: TObject);
begin
AddTenantForm.ShowModal;
end;
procedure TMainForm.FormCreate(Sender: TObject);
begin
assignfile (TenantFile, 'Tenant.dat');
if not fileexists ('Tenant.dat')
then
begin
rewrite (TenantFile);
closefile (TenantFile)
end
{endif};
end;
end.
unit AddTenantUnit;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, MainUnit, StdCtrls;
type
TAddTenantForm = class(TForm)
MainFormButton: TButton;
FirstNameLabel: TLabel;
FirstNameEdit: TEdit;
LastNameLabel: TLabel;
LastNameEdit: TEdit;
SaveButton: TButton;
ClearButton: TButton;
procedure SaveButtonClick(Sender: TObject);
procedure LastNameEditChange(Sender: TObject);
procedure ClearButtonClick(Sender: TObject);
procedure FirstNameEditChange(Sender: TObject);
procedure MainFormButtonClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
AddTenantForm: TAddTenantForm;
implementation
{$R *.dfm}
procedure TAddTenantForm.MainFormButtonClick(Sender: TObject);
begin
AddTenantForm.Close;
end;
procedure TAddTenantForm.FirstNameEditChange(Sender: TObject);
begin
Tenant.FirstName := FirstNameEdit.Text;
end;
procedure TAddTenantForm.ClearButtonClick(Sender: TObject);
begin
FirstNameEdit.Clear;
LastNameEdit.Clear;
end;
procedure TAddTenantForm.LastNameEditChange(Sender: TObject);
begin
Tenant.LastName := LastNameEdit.Text;
end;
// This is where the problem lies when I run this piece of
// code. This represents the Save button being clicked.
procedure TAddTenantForm.SaveButtonClick(Sender: TObject);
begin
assignfile (TenantFile, 'Tenant.dat');
write(TenantFile, Tenant);
closefile (TenantFile);
end;
end.
You are trying to write data into not opened file.
procedure TAddTenantForm.SaveButtonClick(Sender: TObject);
begin
assignfile (TenantFile, 'Tenant.dat');
// Rewrite(TenantFile) or Reset(TenantFile) missed here
write(TenantFile, Tenant);
closefile (TenantFile);
end;

Delphi sort Stringlist with two fields on first

I'm implementing a local cache to speed up DNS lookups (IP->hostname).
The cache is loaded from a CSV file("1.1.1.1host.example.com") into a TStringList with two fields:
TStringList[0] := IPAddress;
TStringList[1] := HostName;
Since I will be querying TStringList via the IP, I obliously want the first field to be sorted:
TStringList.sorted := True;
Will that take care of it so that I can find faster with
IPResolved:=TStringList[TStringList.IndexOf('1.1.1.1'),1];
?
Thanks!
Disclaimer:
This won't answer you how to sort a string list or how to load your data into a string list. It will offer you to use a hash table, which is more efficient than using a string list for your purpose (40k name, value pairs with the search by name).
Alternative:
Since you have Delphi XE2, you can use generics TDictionary class. It will contain IP address as the key and host name as the value. In the following code is shown, how to fill a dictionary and how to search for the value (host name) by a given key (IP address):
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Generics.Collections;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
IPList: TDictionary<string, string>;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
begin
// create the TDictionary instance
IPList := TDictionary<string, string>.Create;
// here you will read your CSV file and add the items in a loop
// I've used here some of the major IP addresses for Sweden
IPList.Add('77.244.224.0', 'Insat Net AB');
IPList.Add('79.138.128.0', 'Hi3G Access AB');
IPList.Add('62.181.192.0', 'DGC Access AB');
IPList.Add('81.216.128.0', 'TDC Swerige AB');
IPList.Add('80.252.176.0', 'Phonera Networks AB');
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
// release a dictionary instance
IPList.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
HostName: string;
begin
// and how to search by the IP address and get the host name if found
if IPList.TryGetValue('81.216.128.0', HostName) then
ShowMessage(HostName)
else
ShowMessage('IP address not found!');
end;
end.
Extension:
The above solution you can then simply extend to use a structure to store more than only a host name, e.g. also a host location:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Generics.Collections;
type
TIPData = record
HostName: string;
HostLocation: string;
end;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
procedure FormCreate(Sender: TObject);
procedure FormDestroy(Sender: TObject);
private
IPList: TDictionary<string, TIPData>;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
IPData: TIPData;
begin
IPList := TDictionary<string, TIPData>.Create;
IPData.HostName := 'Broadnet Europe France';
IPData.HostLocation := 'France';
IPList.Add('78.155.128.0', IPData);
IPData.HostName := 'DNA Palvelut Oy';
IPData.HostLocation := 'Finland';
IPList.Add('62.113.160.0', IPData);
IPData.HostName := 'CD-Telematika a.s.';
IPData.HostLocation := 'Czech republic';
IPList.Add('89.203.128.0', IPData);
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
IPList.Free;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
IPData: TIPData;
begin
if IPList.TryGetValue('89.203.128.0', IPData) then
ShowMessage('Provider ' + IPData.HostName + ' from ' + IPData.HostLocation)
else
ShowMessage('IP address not found!');
end;
end.

Error passing a generic type from a class to another

I am using Delphi 2010
I get the error: E2506 Method of parameterized type declared in interface section must not use local symbol.
Is there a way to accomplish this task?
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, Rtti;
type
MyFormType<T: TForm> = class
class procedure SpecialOpen(var FormVar: T; Params: array of TValue);
end;
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure ShowForm<T1: TForm>(var aForm: T1);
end;
var
Form1: TForm1;
implementation
uses Unit2;
{$R *.dfm}
procedure TForm1.ShowForm<T1>(var aForm: T1);
begin
if aForm = nil then
MyFormType<T1>.SpecialOpen(aForm, [Self]) // <-- Error
else
aForm.Show;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
ShowForm<TForm2>(Form2)
end;
{ MyFormType<T> }
class procedure MyFormType<T>.SpecialOpen(var FormVar: T; Params: array of TValue);
var lRttiContext: TRttiContext;
begin
FormVar := lRttiContext.GetType(TClass(T)).GetMethod('Create').Invoke(TClass(T), Params).AsType<T>;
FormVar.Show;
end;
end.
Tanks and sorry for my english.
This is one of a great many generics bugs in Delphi 2010. Your code compiles in XE2. Your options are to look for a workaround that works in 2010, or to upgrade. Delphi XE and XE2 do include a great many fixes for generics compiler bugs and so if you are serious about making use of generics, Delphi 2010 is not a great choice.

How to fix naming and scoping conflict?

Error: Types of actual and formal var parameters must be identical
unit unAutoKeypress;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Memo1: TMemo;
Button1: TButton;
Memo2: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure SimulateKeyDown(Key:byte);
begin
keybd_event(Key,0,0,0);
end;
procedure SimulateKeyUp(Key:byte);
begin
keybd_event(Key,0,KEYEVENTF_KEYUP,0);
end;
procedure doKeyPress(var KeyValue:byte);
begin
SimulateKeyDown(KeyValue);
SimulateKeyUp(KeyValue);
end;
procedure TForm1.Button1Click(Sender: TObject);
const test = 'merry Christmas!';
var m: byte;
begin
Memo2.SetFocus();
m:=$13;
doKeyPress(m); // THIS IS WHERE ERROR
end;
end.
always error in function doKeyPress(m);
a simple question, why?
I know something wrong with types, but all types are similar, everywhere is byte, strange
for me and I cant run a program.
TForm is inherited from TWinControl and there is a method called DoKeyPress declared in TWinControl, which is in the current scope of the compiler inside the ButtonClick event.
The problem is that doKeyPress is a method of TForm1 (inherited from TWinControl) and so when you write doKeyPress inside a TForm1 method the compiler wants to use TForm1.doKeyPress rather than the local function. The class scope is nearer than the local function scope.
Possible solutions include:
Renaming the local function to avoid the clash.
Using a fully qualified name, unAutoKeypress.doKeyPress.
The former is a better solution in my opinion.

Resources