I have a problem while loading procedures from a dll, either when loading it dynamically or statically. When I put procedures from dll to my unit, everything works fine. When I try to do it with dll it gives me
First chance exception at $00526399. Exception class $C0000005 with message 'access violation at 0x00526399: read of address 0x00000390'. Process Project1.exe (21988)
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ComCtrls,Unit2;
type
TForm1 = class(TForm)
ListView1: TListView;
Button1: TButton;
Button2: TButton;
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Refresh;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
type
plist = ^element;
element = record
artist,title,genre: string[20];
year,grade: integer;
wsk: plist;
end;
database = file of element;
var
base: database;
first: plist;
handler: HModule;
{$R *.dfm}
procedure TForm1.Refresh();
var
current: plist;
begin
ListView1.Clear;
current:= first;
while current<>nil do
begin
with ListView1.Items.Add do
begin
Caption:=current^.artist;
SubItems.Add(current^.title);
SubItems.Add(current^.genre);
SubItems.Add(IntToStr(current^.year));
SubItems.Add(IntToStr(current^.grade));
end;
current:=current^.wsk;
end;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
var Save: procedure;
begin
handler:=LoadLibrary('lib.dll');
try
#Save:=GetProcAddress(handler, PChar(2));
if #Save = nil then raise Exception.Create('Load nie dziala');
Save();
finally
FreeLibrary(handler);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Load: procedure;
begin
handler:=LoadLibrary('lib.dll');
try
#Load:=GetProcAddress(handler, PChar(1));
if #Load = nil then raise Exception.Create('Load nie dziala');
Load();
finally
FreeLibrary(handler);
end;
Refresh();
end;
procedure TForm1.Button1Click(Sender: TObject);
var
el: element;
Add: procedure(el:element);
begin
el.artist:=Edit1.Text;
el.title:=Edit2.Text;
el.genre:=Edit3.Text;
el.year:=StrToInt(Edit4.Text);
el.grade:=StrToInt(Edit5.Text);
handler:=LoadLibrary('lib.dll');
try
#Add:=GetProcAddress(handler, PChar(3));
if #Add = nil then raise Exception.Create('Load nie dziala');
Add(el);
finally
FreeLibrary(handler);
Refresh();
{Form2:=TForm2.Create(Form1);
Form2.ShowModal;
Form2.Free;}
end;
end;
end.
The dll file looks like this:
library lib;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
System.SysUtils,
System.Classes;
{$R *.res}
type plist = ^element;
element = record
artist,title,genre:string[20];
year,grade:integer;
wsk: plist;
end;
database = file of element;
var
first: plist;
base: database;
procedure add(el: element); stdcall;
var current,tmp: plist;
begin
New(current);
current^ := el;
current^.wsk := nil;
if first = nil then
begin
first:=current;
end else
begin
tmp:=first;
while tmp^.wsk<>nil do
begin
tmp:=tmp^.wsk;
end;
tmp^.wsk:=current;
end;
end;
procedure load();stdcall;
var
el: element;
i: integer;
begin
AssignFile(base, 'baza.dat');
if not FileExists('baza.dat') then
begin
Rewrite(base);
end else
begin
Reset(base);
for i := 0 to FileSize(base)-1 do
begin
read(base, el);
add(el);
end;
end;
CloseFile(base);
end;
procedure save();stdcall;
var
current: plist;
el: element;
begin
AssignFile(base, 'baza.dat');
Rewrite(base);
current:=first;
while current<>nil do
begin
el:=current^;
el.wsk:=nil;
write(base, el);
current:= current^.wsk;
end;
end;
exports
add index 1,
load index 2,
save index 3;
begin
end.
It also shows me an error:
Expected ';' but received and identifier 'index' at line 91
But exports are done like I red on web.
The obvious errors are:
You don't perform much error checking. You assume that the calls to LoadLibrary always succeed.
The calling conventions don't match. You use stdcall in the DLL and register in the executable.
The ordinals don't match. In the DLL it is add (1), load (2) and save (3). In the executable you have add (3), load (1) and save (2).
You load and unload the DLL every time you call functions from the DLL. That means that the global variables in the DLL that hold your state are lost each time the DLL is unloaded.
Frankly this code is a real mess. I suggest that you do the following:
Switch to load time linking using the function names rather than ordinals. This means to use the external keyword in the executable. This will greatly simplify your code by removing all those calls to LoadLibrary, GetProcAddress etc. If runtime linking is needed, you can add it later using the delayed keyword.
Stop using global state in the DLL and instead pass information back and forth between modules. Remove all global variables. But make sure you don't pass Delphi objects back and forth.
Use PChar rather than short strings across the module boundary.
Stop using linked lists and dynamic allocation. That's hard to get right. Use TList<T> in the DLL to store the list of elements.
Related
I am attempting to reference a procedure as a parameter of another procedure and am having trouble understanding the documentation.(http://docwiki.embarcadero.com/RADStudio/Sydney/en/Procedural_Types_(Delphi))
From what I understood I need to create a new type for the procedure..
type
TCallback = procedure of object;
and declare the higher order procedure as
procedure HigherOrder(pProc: TCallback);
I receive the compilation error " E2010 Incompatible types: 'TCallBack' and 'procedure, untyped pointer or untyped parameter' " when attempting to call the function(when the button is clicked)
type
TCallBack = procedure of object;
TfrmMain = class(TForm)
btnAct: TButton;
procedure btnActClick(Sender: TObject);
private
procedure HigherOrder(pProc: TCallback);
procedure Callback();
{ Private declarations }
public
{ Public declarations }
end;
var
frmMain: TfrmMain;
implementation
{$R *.dfm}
{ TfrmMain }
procedure TfrmMain.btnActClick(Sender: TObject);
begin
HigherOrder(Callback()); <--Error occurs here
end;
procedure TfrmMain.Callback;
begin
//Do some stuff
end;
procedure TfrmMain.HigherOrder(pProc: TCallback);
begin
//Do some other stuff
pProc();
end;
end.
Any help is greatly appreciated. I am quite new to programming in delphi.
The problem is that you are calling Callback() first and then trying to pass its return value (which, it doesn't have one) to HigherOrder(), but that is not what HigherOrder() is expecting, which is why you are getting the error. In other words, your code is roughly equivalent to this:
procedure TfrmMain.btnActClick(Sender: TObject);
begin
//HigherOrder(Callback());
var res := Callback();
HigherOrder(res);
end;
Except that the type of res is undefined since Callback() is a procedure and not a function.
When calling HigherOrder(), you need to remove the trailing () parenthesis from Callback() in order to pass Callback itself (well, its memory address, anyway) as the value of the pProc parameter, eg:
procedure TfrmMain.btnActClick(Sender: TObject);
begin
HigherOrder(Callback);
end;
Yes, you can also drop the parenthesis when calling a procedure without passing any parameters to it. But, in this case, the compiler is smart enough to know that the parenthesis-omitting Callback identifier is being assigned to a closure type and so will pass it as-is and not call it.
I'm backing up a database using Devart controls. Everything runs fine until I try to free the form having the backup component(TDump) and a progress bar on it. I moved the call to the finally portion of code and checked if it was assigned before trying to free it still same problem
procedure TfrmMain.CmdBackupExecute(Sender: TObject);
var
SaveDialog: TSaveDialog;
QRYString: String;
frmBackup: TfrmBackup;
Password: String;
BackupPassword: String;
MasterPassword: String;
CurrentFrame: TFrameType;
OldUser: String;
OldPassword: String;
begin
dmVintage.tblSettings.Open;
BackupPassword:= dmVintage.tblSettings.FieldByName('BackupRestorePWord').AsString;
MasterPassword:= dmVintage.tblSettings.FieldByName('MasterPWord').AsString;
InputPassword('Enter Backup Password', Password);
if Password = BackupPassword then
try
try
//close current frame and change to root
CurrentFrame:= FrameManager.CurrentFrameType;
FrameManager.Clear;
dmVintage.connMain.LoginPrompt:= False;
OldUser:= dmVintage.connMain.Username;
OldPassword:= dmVintage.connMain.Password;
dmVintage.connMain.Connected:= False;
dmVintage.connMain.Username:= 'root';
dmVintage.connMain.Password:= MasterPassword;
dmVintage.connMain.Connect;
SaveDialog:= TsaveDialog.Create(frmMain);
SaveDialog.Filter := 'SQL file|*.sql';
SaveDialog.DefaultExt:= '.sql';
SaveDialog.FileName:= 'VintageData';
if SaveDialog.Execute then
begin
frmBackup:= TfrmBackup.Create(frmMain);
frmBackup.Show;
frmBackup.mdVintage.BackupToFile(AddTimestampToFilename(SaveDialog.FileName), QryString);
//FreeAndNil(frmBackup);
//FreeAndNil(SaveDialog);
dlgI('Backup Seccessful');
end;
Except on E: Exception do
dlgW2('TfrmMain.CmdBackupExecute', E.Message);
end;
finally
//ShowMessage('Finally');
if Assigned(frmBackup) then
FreeAndNil(frmBackup);
if Assigned(SaveDialog) then
FreeAndNil(SaveDialog);
//reset connection and load old frame
dmVintage.connMain.Connected:= False;
dmVintage.connMain.Username:= OldUser;
dmVintage.connMain.Password:= OldPassword;
dmVintage.connMain.Connect;
dmVintage.connMain.LoginPrompt:= True;
FrameManager.LoadFrame(CurrentFrame);
end
else
dlgE('Invalid Backup Password');
end;
function TfrmMain.AddTimestampToFilename(Value: String): String;
var
Extension: String;
FileName: String;
FormattedDataTime: String;
begin
Extension:= ExtractFileExt(Value);
FileName:= ChangeFileExt(Value, '');
DateTimeToString(FormattedDataTime, 'yyyymmdd_hhmm', Now);
FileName:= FileName + '_' + FormattedDataTime;
Result:= ChangeFileExt(FileName, Extension);
end;
The Backup form is very simple with a few labels the TDump component and a progress bar.
unit uBackup;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, uDataVintage, DADump, MyDump,
Vcl.ComCtrls, Vcl.StdCtrls;
type
TfrmBackup = class(TForm)
mdVintage: TMyDump;
lblBackingUpTable: TLabel;
lblTable: TLabel;
Label3: TLabel;
pbBackup: TProgressBar;
procedure mdVintageBackupProgress(Sender: TObject; ObjectName: string;
ObjectNum, ObjectCount, Percent: Integer);
private
{ Private declarations }
public
{ Public declarations }
end;
implementation
{$R *.dfm}
procedure TfrmBackup.mdVintageBackupProgress(Sender: TObject;
ObjectName: string; ObjectNum, ObjectCount, Percent: Integer);
begin
Application.ProcessMessages;
if lblTable.Caption <> ObjectName then
lblTable.Caption:= ObjectName;
pbBackup.Position:= Percent;
end;
end.
Use frmBackup.Release when freeing (especially non-modal) forms.
Freeing forms
The form is not shown in a modal way, and you couldn't, because you're controlling it from the main form, which wouldn't work on a modal form.
I think you get the access violation because you 'bluntly' free the form, while the form itself is also still visible and handling messages. Because of that, the form code (the general TForm code) might at some point still try to do something to the form, even though your instance has already been cleaned up. Even when you call 'Close` in the code, you have this issue, because closing is also not a synchronous process, and requires the form to handle messages.
In general the solution is to call frmBackup.Release instead of frmBackup.Free. That way, the form queues a message for itself. It will first handle the other stuff it has to do, and at some point encounter this message and start a graceful clean-up procedure before it finally frees itself. This is typically the way to close a form from, say, a button-click event on the form itself, but I think it will get you out of this pickle as well.
General tips on Free and FreeAndNil
You don't need to call FreeAndNil in most cases, and especially not on a local variable for which you know exactly when it was assigned a value or not. The only thing FreeAndNil does, is make your reference nil, which is not needed at all for a variable that goes out of scope three lines later anyway.
There is no need at all to call if assigned before calling FreeAndNil, or even Free. Assigned is only checking if the reference is nil, which is what Free also does internally. That right: This is valid code that won't throw errors:
var
o: TObject;
begin
o := nil;
o.Free;
I have a web service that I have created using Delphi and I want to connect to sql server with it so I have added to the project an ADO Connection and ADOQuery had both of them configured and ready to use, there was only a small problem, there are two units on my project and those objects were added to Unit1 and I am working with my ImplUnit whitch is another unit, and can`t find a way to reference or include one unit inside the other unit.
unit1
{ SOAP WebModule}
unit Unit1;
interface
uses
SysUtils, Classes, HTTPApp, InvokeRegistry, WSDLIntf, TypInfo,
WebServExp, WSDLBind, XMLSchema, WSDLPub, SOAPPasInv, SOAPHTTPPasInv,
SOAPHTTPDisp, WebBrokerSOAP, DB, ADODB;
type
TWebModule1 = class(TWebModule)
HTTPSoapDispatcher1: THTTPSoapDispatcher;
HTTPSoapPascalInvoker1: THTTPSoapPascalInvoker;
WSDLHTMLPublish1: TWSDLHTMLPublish;
ADOConnection1: TADOConnection;
ADODataSet1: TADODataSet;
ADOQuery1: TADOQuery;
procedure WebModule1DefaultHandlerAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
private
{ Private declarations }
public
{ Public declarations }
end;
var
WebModule1: TWebModule1;
implementation
{$R *.dfm}
procedure TWebModule1.WebModule1DefaultHandlerAction(Sender: TObject;
Request: TWebRequest; Response: TWebResponse; var Handled: Boolean);
begin
WSDLHTMLPublish1.ServiceInfo(Sender, Request, Response, Handled);
end;
end.
My unit
unit UTImplementacao;
interface
uses
InvokeRegistry,DB, ADODB;
type
IInterface = interface(IInvokable)
['{EFF30FFA-DA0C-433A-832A-0BA057B55103}']
function ReceiveUser(username : String; password : String) :
Boolean; stdcall;
end;
TImplementacao = class(TInvokableClass, IInterface)
public
function ReceiveUser(username : String; password : String) :
Boolean; stdcall;
end;
implementation
{ TImplementacao }
function TImplementacao.ReceiveUser(username, password: String): Boolean;
var
ADOConnection1: TADOConnection;
ADOQuery1: TADOQuery;
begin
try
ADOConnection1 := TADOConnection.Create(nil);
ADOConnection1.LoginPrompt := False;
ADOConnection1.ConnectionString:= 'Provider=SQLOLEDB.1;Integrated Security=SSPI;' +
'Persist Security Info=False;' +
'User ID=Diego;'+
'Catalog=OnlineShopping;' +
'Data Source=DIEGO-PC\SQLEXPRESS'+
';Use Procedure for Prepare=1;' +
'Auto Translate=True;Packet Size=4096;'+
'Workstation ID=DIEGO-PC;'+
'Use Encryption for Data=False;'+
'Tag with column collation when possible=False;';
ADOConnection1.Connected := True;
ADOQuery1.Connection := ADOConnection1;
ADOQuery1.SQL.Add('select username,upassword from Users '+
'where username = :usernamep and upassword = '+
':upasswordp');
ADOQuery1.Parameters.ParamByName('upasswordp').Value := password;
ADOQuery1.Parameters.ParamByName('usernamep').Value := username;
ADOQuery1.ExecSQL;
Result := True;
finally
ADOQuery1.Free;
if ADOConnection1.Connected then
ADOConnection1.Close;
ADOConnection1.Free;
end;
Result := False;
end;
initialization
InvRegistry.RegisterInvokableClass(TImplementacao);
InvRegistry.RegisterInterface(TypeInfo(IInterface));
end.
please disregard the ADOConnection and ADOQuery that I have added to my unit i got a little desperate ad duplicade the code... Yeah, I know yachs!!!!
#SilverWarrior
If declare Unit1 inside the uses of UTImplementacao will I have access to the componemts below:
type
ADOConnection1: TADOConnection;
ADODataSet1: TADODataSet;
ADOQuery1: TADOQuery;
or should I declare for each one of the types variable inside var clause ?
If you want to access objects declared in Unit1 from other units in your project you need to add Unit1 into interface uses section (the one at top) of those units.
unit ImplUnit;
interface
uses
SysUtils, Classes, ... , Unit1;
...
That is the same way as Delphi automatically adds other units like Sysutils, Classes, etc.
Also I would strongly recomend you change the name of your unit to somethng more meaningfull so that when you will be looking at your code after some time you will quickly know what code does that unit contains and what it is used for.
EDIT: Based on your edit of the question I suspect you want to acces the components from your Unit1 directly by calling:
Unit1.AdoConnection1
That won't work. Why? Becouse the components are declared within the scope of the TWebModule1 class.
So you need to access them like this:
Unit1.WebModule1.AdoConnection1;
NOTE: If Unit1 is added into interface uses section of your UTImplementacao unit you can also directly call:
WebModule1.AdoConnection1
You don't have to prefix every command with Unit1. I have written this in such way to be hopefully more understandable which unit mebers are you accessing. Especially for other people which might be reading this thread and not knowing the structure of your program.
in XE5 I have made application where by default is loaded small txt file with multiple lines using OnShow function.
Also there is 1 TEdit1 field with default value=300 and TButton to save file in defined directory.
How would be possible to:
1.) using value in TEdit1 field to get that amount of files in that specified dir, value could be changed if needed;
2.) all generated files should be like: 1.txt, 2.txt, 3.txt... etc.
Now buttons funcion is:
procedure TForm1.GenerateClick(Sender: TObject);
var
dirName : String;
begin
// Create a new directory
dirName := 'gen';
if DirectoryExists(dirName)
then
Memo1.Lines.SaveToFile('gen\default.txt')
else
CreateDir(dirName);
Memo1.Lines.SaveToFile('gen\default.txt');
end;
Best regards,
G
the whole working code:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, IOUtils, Vcl.ComCtrls;
type
TForm1 = class(TForm)
Edit1: TEdit;
SaveFile: TButton;
Generate: TButton;
Memo1: TMemo;
procedure LoadFile(Sender: TObject);
procedure SaveFileClick(Sender: TObject);
procedure GenerateClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.LoadFile(Sender: TObject);
begin
if FileExists('template.txt')then begin
Memo1.Lines.LoadFromFile('template.txt');
end
else
ShowMessage('Cant find template.txt, add text and use SAVE button !!!');
end;
procedure TForm1.SaveFileClick(Sender: TObject);
begin
Memo1.Lines.SaveToFile('template.txt');
end;
procedure TForm1.GenerateClick(Sender: TObject);
var
dirName, fName : String;
i, max: integer;
begin
// Create a new directory
dirName := 'gen';
if NOT DirectoryExists(dirName) then
CreateDir(dirName);
fName := Edit1.Text;
max := StrToInt(fName);
for i := 1 to max do begin
fName := dirName + '\'+ IntToStr(i) + '.txt';
Memo1.Lines.SaveToFile( fName );
end;
end;
end.
Thank you :)
The very idea to store data in visual components is somewhat smelly. But if you insist - then you can just store integer values in .TAG property. But okay, learn few casual functions below and learn begin and end keywords:
Update: converting relative name to fully qualified for ForceDirectories. Used functions:
http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.ForceDirectories
http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.ExpandUNCFileName
http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.GetCurrentDir
http://docwiki.embarcadero.com/Libraries/XE2/en/System.SysUtils.IncludeTrailingPathDelimiter
The code with using Delphi-provided ready-made function then becomes:
procedure TForm1.GenerateClick(Sender: TObject);
var
dirName, fName : String;
i, max: integer;
begin
// Create a new directory
dirName := 'gen';
dirName := ExpandUNCFileName(dirName);
// converting possible relative path to absolute
dirName := IncludeTrailingPathDelimiter(GetCurrentDir) + dirName;
// yet another way to do the same, as above
// GCD function would return paths like "C:\" or like "C:\Users\Name\Documents"
// so we don't know in advance if there would be slash at the end or not
ForceDirectories(dirName);
fName := EditField.Text;
max := StrToInt(fName);
// even better: max := EditField.Tag; and change TAG property, not TEXT in IDE
for i := 1 to max do begin
fName := dirName + PathDelimiter + IntToStr(i) + '.txt';
MemoField.Lines.SaveToFile( fName );
end;
end;
One can also call TDirectory.CreateDirectory(dirName); instead of ForceDirectories but I cannot check now if the former works with relative paths or also requires path expansion before being called. If it can - then the non-changed dirName would be valid parameter to call the function directly.
http://docwiki.embarcadero.com/Libraries/XE2/en/System.IOUtils.TDirectory.CreateDirectory
You original function has a weird, broken structure actually. Double-save. I make below a proper structure of your original code for easy reading:
procedure TForm1.GenerateClick(Sender: TObject);
var
dirName : String;
begin
// Create a new directory
dirName := 'gen';
if DirectoryExists(dirName)
then
Memo1.Lines.SaveToFile('gen\default.txt')
else
CreateDir(dirName);
Memo1.Lines.SaveToFile('gen\default.txt');
end;
I am using Embarcadero RAD Studio XE2 Update 4 and the Indy package shipped with it.
My intention is to find a server in LAN with broadcast from a TIdUDPClient that waits for a response from the server to get its IP. Receiving the data works fine if I use the TIdUDPClient method ReceiveString with no arguments.
But when I try to use the overloaded version found in the Indy 10 Documentation version 10.5.8.3 coming with RAD Studio, it does not compile and shows 'E2250: There is no overloaded version of 'ReceiveString' that can be called with these arguments'.
Here is my code:
unit Client;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, IdBaseComponent, IdComponent, IdUDPBase,
IdUDPClient, Vcl.StdCtrls, IdGlobal;
type
TFormLC = class(TForm)
UDPClient: TIdUDPClient;
LServer: TLabel;
Label2: TLabel;
Label3: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private-Deklarationen }
public
{ Public-Deklarationen }
end;
var
FormLC: TFormLC;
implementation
{$R *.dfm}
function findServer:string;
var ans, ip : string;
port: TIdPort;
begin
with FormLC.UDPClient do begin
Active := True;
BroadcastEnabled:=True;
Broadcast('ServerRequest', 1234);
ans := ReceiveString(ip, port);
Active := False;
end;
if SameText(ans, 'ServerAccept') then
result := ip
else
result := '';
end;
procedure TFormLC.Button1Click(Sender: TObject);
var ans:string;
begin
LServer.Caption := findServer;
end;
end.
I noticed that the online documentation of Indy differs from the documentation that comes with the IDE and tried it as described there, without succes.
Any help would be great!
Your issue is caused by the with statement, you are passing the port property of the TIdUDPClient instead of the local variable port to the ReceiveString method.
function findServer:string;
var ans, ip : string;
port: TIdPort;
begin
with FormLC.UDPClient do begin
....
ans := ReceiveString(ip, port);//here you are passing the port property
Active := False;
end;
....
end;
As workaround rename you port local variable like so :
function findServer:string;
var ans, ip : string;
vport: TIdPort;
begin
with FormLC.UDPClient do begin
....
ans := ReceiveString(ip, vport);//now will work
Active := False;
end;
end;
or even better don't use the with statement.
TIdUDPClient has 2 overloads for ReceiveString():
function ReceiveString(const AMSec: Integer = IdTimeoutDefault; AByteEncoding: TIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ADestEncoding: TIdTextEncoding = nil{$ENDIF}): string; overload;
function ReceiveString(var VPeerIP: string; var VPeerPort: TIdPort; const AMSec: Integer = IdTimeoutDefault; AByteEncoding: TIdTextEncoding = nil{$IFDEF STRING_IS_ANSI}; ADestEncoding: TIdTextEncoding = nil{$ENDIF}): string; overload;
When you call ReceiveString() without parameters, you are calling the first overload. When trying to call the second overload, your code fails to compile because your with statement is passing the TIdUDPClient.Port property to the second parameter, instead of your local port variable. The compile will not allow you to pass a property to a var parameter.
You need to remove the with statement and/or rename your port variable to resolve the conflict.