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.
Related
I need for some reason the codepage of the language set by the currently selected keyboard layout in the current process. (I use Win10 with per app language settings)
getThreadLocale does not change when UI language changes. It gives back the default locale of the process.
getProcessInformation/getThreadInformation does not contain any information about the current language/locale.
I think the chain of the needed information is:
selected language => matching locale => codepage
if I have the current locale id (matching to the selected language) then I can fetch its codepage by:
getLocaleInfoW( idLocale, LOCALE_IDEFAULTANSICODEPAGE, buff, buffSize );
Is(Are) there any winapi call(s) to get the information described above?
The TLabel caption sets to the CodePage associated with the current keyboard language by the TButton.OnClick event handler.
unit Unit3;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls;
type
TForm3 = class(TForm)
Label1: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
implementation
{$R *.dfm}
procedure TForm3.Button1Click(Sender: TObject);
var
tid : word;
lid : word;
ndxLocale, buffSize : integer;
localeName : string;
buff : pchar;
begin
tid := getCurrentThreadID;
lid := getKeyboardLayout( tid );
ndxLocale := languages.IndexOf( lid );
localeName := languages.LocaleName[ndxLocale];
buffSize := getLocaleInfoEx( pchar( localeName ), LOCALE_IDEFAULTANSICODEPAGE, NIL, 0 );
getMem( buff, buffSize*sizeOf(char) );
try
getLocaleInfoEx( pchar( localeName ), LOCALE_IDEFAULTANSICODEPAGE, buff, buffSize );
label1.caption := strPas( buff );
finally
freeMem( buff );
end;
end;
GetACP() returns "ansi" code page...lol, not really ansi, but that's what windows calls it. Can also use GetCPInfo() to get additional information after you call GetACP(). Things get trickier for Japanese, Chinese, and other far east languages that use double byte character set. I still work on an application that is MBCS. Would be nice if we could convert to Unicode, but it's not happening and it won't be my problem soon.
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;
Am trying to launch quake from its directory using code below but the pathname is more then 50+ chars so its not working due to limits is there anyway to do larger pathnames?
Pathname example
C:\Users\USERNAME\AppData\Roaming\Gaming\Toys\Q2\quake2.exe
procedure TAutoLauncher.OKBtnClick(Sender: TObject);
var
parameters : string;
toydir : string;
begin
if FileFound then
begin
ToyDir := ExtractFilePath(ExtractFileDir(Application.ExeName)) + 'Toys\';
FolderNameEdit.Text := ToyDir + 'Q2\quake2.exe';
//
parameters := Trim('');
case ModeBox.ItemIndex of
0: parameters := ' +connect '+ HostEdit.Text +':'+ PortEdit.Text +' +password '+ PasswordEdit.Text;
1: parameters := ' +set game ctf';
2: parameters := ' +set game rogue';
3: parameters := ' +set game xatrix';
end;
//
ShellExecute(handle, 'open', PChar(FolderNameEdit.Text), PChar(parameters), nil, SW_SHOWNORMAL);
end else
begin
Application.MessageBox('Quake2 files not found.', 'Game Not Found', MB_OK OR MB_ICONQUESTION);
end;
end;
Thanks
ShellExecute does not contain such a limitation; the limit is the INTERNET_MAX_URL_LENGTH (around 2048) unless you're running on windows 95 (in which case it's MAX_PATH), according to Raymond Chen of Microsoft.
Here's a sample XE8 application compiled as a 32-bit app and run on Windows 10 to demonstrate. (And, even though ShellExecute is the wrong way to launch an executable as Remy said, it also shows you the correct way to use it and test the return value for errors.)
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, 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;
var
Form1: TForm1;
implementation
{$R *.dfm}
uses
ShellAPI;
procedure TForm1.Button1Click(Sender: TObject);
var
FilePath: string;
Res: Integer;
begin
FilePath := 'C:\Users\Ken\Documents\Embarcadero\Studio\Projects\Seattle\Android\AndroidBase\Debug\Styles.xml';
// This works, using the long (96 character) string as the parameters
Res := ShellExecute(0, 'open', 'notepad.exe', PChar(FilePath), nil, SW_NORMAL);
if Res < 32 then
RaiseLastOSError(GetLastError);
// This works, using the long (96 character) string as the filename
Res := ShellExecute(0, 'open', PChar(FilePath), nil, nil, SW_NORMAL);
if Res < 32 then
RaiseLastOSError(GetLastError);
end;
end.
It appears from your comments above that it is Quake itself that is generating the error. In that case, the issue is not a programming issue, but something you should address with the software vendor.
I am trying to create a TCP server using Delphi firemonkey application.
My resource file Unit1.fmx is given below.
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 480
ClientWidth = 640
FormFactor.Width = 320
FormFactor.Height = 480
FormFactor.Devices = [Desktop]
OnActivate = FormShow
OnCreate = FormCreate
DesignerMasterStyle = 0
object Label1: TLabel
Position.X = 504.000000000000000000
Position.Y = 248.000000000000000000
Text = 'Label1'
end
object TCPServer: TIdTCPServer
Bindings = <>
DefaultPort = 0
OnAfterBind = TCPServerAfterBind
OnConnect = TCPServerOnConnect
OnExecute = TCPserverExecute
Left = 560
Top = 184
end
end
My code file is given here.
unit Unit1;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Platform,
IdCustomTCPServer, IdTCPServer, IdBaseComponent, IdComponent, IdUDPBase, IdContext,
IdSocketHandle, IdUDPServer, FMX.Controls.Presentation, FMX.StdCtrls, Windows;
type
TForm1 = class(TForm)
TCPServer: TIdTCPServer;
Label1: TLabel;
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure TCPserverExecute(AContext: TIdContext);
procedure TCPServerOnConnect(AContext: TIdContext);
procedure TCPServerAfterBind(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
type
TMyLoggingService = class(TInterfacedObject, IFMXLoggingService)
public
procedure Log(const Format: string; const Params: array of const);
end;
var
Form1: TForm1;
MyLoggingService: IFMXLoggingService;
implementation
{$R *.fmx}
procedure TMyLoggingService.Log(const Format: string; const Params: array of const);
begin
// do whatever you want...
OutputDebugString(PChar(Format));
end;
procedure TForm1.FormCreate(Sender: TObject);
var
Binding : TIdSocketHandle;
begin
MyLoggingService := TMyLoggingService.Create;
if TPlatformServices.Current.SupportsPlatformService( IFMXLoggingService ) then
TPlatformServices.Current.RemovePlatformService( IFMXLoggingService );
// register my service
TPlatformServices.Current.AddPlatformService( IFMXLoggingService, MyLoggingService );
TCPServer.DefaultPort := 16000;
TCPServer.Bindings.Clear;
Binding := TCPServer.Bindings.Add;
Binding.IP := '127.0.0.1';
Binding.Port := 16000;
//if Assigned(MyLoggingService) then
MyLoggingService.Log('FormCreate!',[]);
end;
procedure TForm1.FormShow(Sender: TObject);
begin
TCPServer.Active := True;
end;
procedure TForm1.TCPServerAfterBind(Sender: TObject);
begin
MyLoggingService.Log('After Bind',[]);
end;
procedure TForm1.TCPServerOnConnect(AContext: TIdContext);
begin
MyLoggingService.Log('Recieved Connection From Client ',[]);
end;
procedure TForm1.TCPServerExecute(AContext: TIdContext);
Var
C : String;
begin
C:= AContext.Connection.Socket.ReadLn();
//if Assigned(MyLoggingService) then
MyLoggingService.Log('TserverExecute !',[]);
MyLoggingService.Log('Recived Data !',[C]);
if C = 'TESTSTRING' then
begin
AContext.Connection.Socket.Writeln('SENT');
end;
end;
end.
My problem is In the system log where I am logging my messages this is what I get.
Debug Output: FormCreate! Process Project1.exe (3340)
Debug Output: After Bind Process Project1.exe (3340)
Debug Output: Recieved Connection From Client Process Project1.exe (3340)
First chance exception at $759BC42D. Exception class EIdSocketError with message
'Socket Error # 10054
Connection reset by peer.'.
Process Project1.exe (3340)
connection reset by peer. My question is why is this happening with my code ? What am I doing wrong and what can I do to fix it?
At the remote end I have a very simple python script which is given below.
import socket
# Set up a TCP/IP socket
s = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
# Connect as client to a selected server
# on a specified port
s.connect(('127.0.0.1',16000))
# Protocol exchange - sends and receives
data = 'TESTSTRING';
s.send(data.encode())
while True:
resp = s.recv(1024)
if resp == "":
print('NOTHING')
break
else:
print(resp)
break
# Close the connection when completed
s.close()
print("\ndone")
There is nothing wrong with the server code. The client is disconnecting on its end, and the debugger on the server side is simply detecting the exception that Indy raises to itself to handle the disconnect. This is normal behavior.
There is, however, a problem with your client code. On the server side, you are using TIdIOHandler.ReadLn(), which is expecting the client to send a line of text delimited by a LF character (or a CRLF string). But your client script is not sending that delimiter at all, so ReadLn() does not exit, and the server consequently does not call TIdIOHandler.WriteLn() to send a response. On the client side, s.recv() is likely failing (since no response is being received) and the script ends, disconnecting the socket, causing ReadLn() on the server side to raise an exception back to TIdTCPServer so it can stop the thread close the socket.
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.