I have a simple TCP server application that I have copied from the internet.
unit TCPServer;
interface
uses
System.SysUtils, System.Variants, System.Classes,
FMX.Forms, FMX.Dialogs, IdBaseComponent, IdComponent, IdSocketHandle,
IdCustomTCPServer, IdTCPServer, IdContext;
type
TForm2 = class(TForm)
Tserver: TIdTCPServer;
procedure FormCreate(Sender: TObject);
procedure FormShow(Sender: TObject);
procedure TserverExecute(AContext: TIdContext);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form2: TForm2;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
Binding : TIdSocketHandle;
begin
Tserver.DefaultPort := 16000;
Tserver.Bindings.Clear;
Binding := Tserver.Bindings.Add;
Binding.IP := '0.0.0.0';
Binding.Port := 16000;
end;
procedure TForm1.FormShow(Sender: TObject);
begin
Tserver.Active := True;
end;
procedure TForm1.TserverExecute(AContext: TIdContext);
Var
C : String;
begin
C:= AContext.Connection.Socket.ReadLn();
if C = 'TESTSTRING' then
begin
AContext.Connection.Socket.Writeln('SENT');
end;
end;
end.
I am attempting to get it to compile on a Android device. I have created a new multidevice form Unit2 (Unit2.pas and Unit2.fmx )and pasted the above file inside Unit2.pas.
When I compile the project I get the error
E1026 File not found Unit2.dfm
I am brand new to delphi programming. I would like to know what is a dfm file. How is it different from a fmx file which seems to be the correct way to create a Form on my IDE.I am using Embarcadero® Delphi 10 Seattle
Based on what I see in your code, you are working with a VCL Forms Application, which is for Windows only. Yet you say you are targeting Android. There are multiple issues here, so I'll start from the top.
E1026 File not found Unit2.dfm
I do not see any reference to Unit2 in your code. There is nothing here which suggests that such a file even exists in the first place. Your unit above is TCPServer.pas and I don't see Unit2 in any uses clause. If I had to guess, this unit was originally named Unit2.pas but you modified the name at the very top of the unit to TCPServer without instructing the project of your name change.
I am attempting to get it to compile on a Android device.
You are going about the wrong way if this is your intention. Your above unit is for a VCL application, but VCL is for Windows only. It will not work on any mobile platform, period.
I would like to know what is a dfm file. How is it different from a fmx file
A DFM file is tied to a VCL application, which is how I know that's what your current project is targeting. An FMX file is tied to a Firemonkey application, which is what you will need to target multi-platform. You cannot mix the two together in the same application if you're targeting mobile platforms. Both DFM and FMX are the files which contain the form's design (as opposed to the code), and it differs between the two frameworks.
I am brand new to delphi programming.
I highly advise that you step back and go read up on Delphi first before you try to begin coding, especially if you're targeting a mobile platform.
On another note, your unit refers to FMX units in its uses clause, but look at the line which says {$R *.dfm}. This means it's looking for a DFM file instead. I don't know how you acquired this code, but it's a huge mix-up and is impossible to compile under either framework.
Related
AS. since closing related questions - more examples added below.
The below simple code (which finds a top-level Ie window and enumerates its children) works Ok with a '32-bit Windows' target platform. There's no problem with earlier versions of Delphi as well:
procedure TForm1.Button1Click(Sender: TObject);
function EnumChildren(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
const
Server = 'Internet Explorer_Server';
var
ClassName: array[0..24] of Char;
begin
Assert(IsWindow(hwnd)); // <- Assertion fails with 64-bit
GetClassName(hwnd, ClassName, Length(ClassName));
Result := ClassName <> Server;
if not Result then
PUINT_PTR(lParam)^ := hwnd;
end;
var
Wnd, WndChild: HWND;
begin
Wnd := FindWindow('IEFrame', nil); // top level IE
if Wnd <> 0 then begin
WndChild := 0;
EnumChildWindows(Wnd, #EnumChildren, UINT_PTR(#WndChild));
if WndChild <> 0 then
..
end;
I've inserted an Assert to indicate where it fails with a '64-bit Windows' target platform. There's no problem with the code if I un-nest the callback.
I'm not sure if the erroneous values passed with the parameters are just garbage or are due to some mis-placed memory addresses (calling convention?). Is nesting callbacks infact something that I should never do in the first place? Or is this just a defect that I have to live with?
edit:
In response to David's answer, the same code having EnumChildWindows declared with a typed callback. Works fine with 32-bit:
(edit: The below does not really test what David says since I still used the '#' operator. It works fine with the operator, but if I remove it, it indeed does not compile unless I un-nest the callback)
type
TFNEnumChild = function(hwnd: HWND; lParam: LPARAM): Bool; stdcall;
function TypedEnumChildWindows(hWndParent: HWND; lpEnumFunc: TFNEnumChild;
lParam: LPARAM): BOOL; stdcall; external user32 name 'EnumChildWindows';
procedure TForm1.Button1Click(Sender: TObject);
function EnumChildren(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
const
Server = 'Internet Explorer_Server';
var
ClassName: array[0..24] of Char;
begin
Assert(IsWindow(hwnd)); // <- Assertion fails with 64-bit
GetClassName(hwnd, ClassName, Length(ClassName));
Result := ClassName <> Server;
if not Result then
PUINT_PTR(lParam)^ := hwnd;
end;
var
Wnd, WndChild: HWND;
begin
Wnd := FindWindow('IEFrame', nil); // top level IE
if Wnd <> 0 then begin
WndChild := 0;
TypedEnumChildWindows(Wnd, #EnumChildren, UINT_PTR(#WndChild));
if WndChild <> 0 then
..
end;
Actually this limitation is not specific to a Windows API callbacks, but the same problem happens when taking address of that function into a variable of procedural type and passing it, for example, as a custom comparator to TList.Sort.
http://docwiki.embarcadero.com/RADStudio/Rio/en/Procedural_Types
procedure TForm2.btn1Click(Sender: TObject);
var s : TStringList;
function compare(s : TStringList; i1, i2 : integer) : integer;
begin
result := CompareText(s[i1], s[i2]);
end;
begin
s := TStringList.Create;
try
s.add('s1');
s.add('s2');
s.add('s3');
s.CustomSort(#compare);
finally
s.free;
end;
end;
It works as expected when compiled as 32-bit, but fails with Access Violation when compiled for Win64. For 64-bit version in function compare, s = nil and i2 = some random value;
It also works as expected even for Win64 target, if one extracts compare function outside of btn1Click function.
This trick was never officially supported by the language and you have been getting away with it to date due to the implementation specifics of the 32 bit compiler. The documentation is clear:
Nested procedures and functions (routines declared within other routines) cannot be used as procedural values.
If I recall correctly, an extra, hidden, parameter is passed to nested functions with the pointer to the enclosing stack frame. This is omitted in 32 bit code if no reference is made to the enclosing environment. In 64 bit code the extra parameter is always passed.
Of course a big part of the problem is that the Windows unit uses untyped procedure types for its callback parameters. If typed procedures were used the compiler could reject your code. In fact I view this as justification for the belief that the trick you used was never legal. With typed callbacks a nested procedure can never be used, even in the 32 bit compiler.
Anyway, the bottom line is that you cannot pass a nested function as parameter to another function in the 64 bit compiler.
This code is working in a Firemonkey Windows app, but doesn't work in Android app, i get Goodbye instead of Welcome, what is wrong?
Edit8 Text : 162496 //Computer unique code
Edit9 Text : 1564224593 //serial #
procedure TForm2.Button5Click(Sender: TObject);
var
f2,f1:textfile;
i,j:byte;
s1,s2,s3,c:string;
F: TextFile;
begin
j:=0;
s2 := Edit8.Text;
for i:=1 to Length(s2) do
if (s2[i]>='0') and (s2[i]<='9') then
s3:=s3+s2[i];
for i:=1 to Length(s3)-1 do
if (edit9.Text[i*2-1]<>s3[i]) or (abs(strtoint(s3[i+1])-strtoint(s3[i]))<> strtoint(edit9.Text[i*2])) then
inc(j);
if j=0 then
ShowMessage('Welcome')
else
ShowMessage('Goodbye');
end;
Delphi mobile compilers use zero-based strings.
You have three choices:
As #Günter_the_Beautiful points out, your best choice is to rewrite your code to use string helpers (which are always 0-based)
Rewrite your code to use 0-based indexing: for I := 0 to ...
If you need a quick fix, turn it off locally for your code snippet using the {$ZEROBASEDSTRINGS OFF} directive (and revert it back with {$ZEROBASEDSTRINGS ON} again).
For options 2. and 3., if you need your code to be cross-platform, consider using appropriate platform conditional defines. This is what makes option 1. compelling: no need to clutter your code with conditional defines.
I am using these two helper routines:
FUNCTION GetChar(CONST S : STRING ; OneBasedIndex : LongWord) : CHAR;
BEGIN
{$IF CompilerVersion>=24 }
Result:=S[SUCC(OneBasedIndex-LOW(S))]
{$ELSE }
Result:=S[OneBasedIndex]
{$IFEND }
END;
PROCEDURE SetChar(VAR S : STRING ; OneBasedIndex : LongWord ; NewChar : CHAR);
BEGIN
{$IF CompilerVersion>=24 }
S[SUCC(OneBasedIndex-LOW(S))]:=NewChar
{$ELSE }
S[OneBasedIndex]:=NewChar
{$IFEND }
END;
This way, you can continue working with strings as 1-based (which is the logical choice :-)) as long as you always access the strings as characters using these two functions.
I'm using Delphi 10 Seattle trail version for developing mobile application. And I tried to create new android mobile application which contains only TEditBox. And then compiled by setting the option as "Release". Then, generated the .apk file and then provided the file to the user. And when the user tried to click the edit box, the application raises the error message that "The Appname is not responding".
The user is using the Lennova A5000 and the Os is Android 5.0.2.
And the same application is running in my Moto g2 (5.0.2) and Micromax Yureka.
Please provide me is there any solution.
Also, I have updated the app in google app store. Then, it is showing as incompatible application for this device (Lennova A5000).
And also I have updated all the android SDK packages. After that also, it is raising the same issue.
I think this may be problem to Embarcadreo Delphi or any missing packages? Dont know what to do.
Thanks in advance.
Atlast I got the solution from Embarcadreo website. Please follow the mentioned steps.
1.Copy FMX.Platform.Android.pas to the project folder from source/fmx folder
and add the copied files to the project.
Then, do the changes in the following procedures.
procedure TPlatformAndroid.RunOnUIThread(Proc: TThreadProcedure);
procedure TPlatformAndroid.RunOnUIThread(Proc: TThreadProcedure);
begin
//MainActivity.runOnUiThread(TSimpleProcedureRunner.Create(Proc));
CallInUIThread(
procedure()
begin
Proc;
end);
end;
procedure TPlatformAndroid.SynchronizeOnUIThread(Proc: TThreadProcedure);
procedure TPlatformAndroid.SynchronizeOnUIThread(Proc: TThreadProcedure);
var
Runner: TSimpleProcedureRunner;
begin
// CallInUIThread(
// procedure()
// begin
// Runner := TSimpleProcedureRunner.Create(Proc);
// MainActivity.runOnUiThread(Runner);
// Runner.Event.WaitFor;
// end);
CallInUIThreadAndWaitFinishing(
procedure()
begin
Proc;
end);
end;
procedure TPlatformAndroid.SetClipboard(Value: TValue);
procedure TPlatformAndroid.SetClipboard(Value: TValue);
var
Setter: TClipboardSetter;
begin
Setter := TClipboardSetter.Create(Value.ToString);
CallInUIThread(
procedure()
begin
SharedActivity.runOnUiThread(Setter);
end);
Setter.Done.WaitFor(INFINITE);
end;
function TPlatformAndroid.GetClipboard: TValue;
function TPlatformAndroid.GetClipboard: TValue;
var
Getter: TClipboardGetter;
begin
Getter := TClipboardGetter.Create;
CallInUIThread(
procedure()
begin
SharedActivity.runOnUiThread(Getter);
end);
Getter.Done.WaitFor(INFINITE);
Result := Getter.Value;
end;
Then, Rebuild the project. After doing this every thing is working fine.
While trying to implement Notifications at my project, Delphi Seattle can't reference FMX.Notification properly.
This is what I get:
[DCC Fatal Error] UnitMain.pas(27): F2613 Unit 'FMX.Notification' not found.
And then it makes an automatically reference to System.Notification, however it crashes my Android app when trying to use a object from this class.
How can I correctly implement Notifications on Delphi Seattle?
Note: It must run on both iOS and Android.
According to Embarcadero's official Seattle changes:
The FMX.Notification unit has been replaced by System.Notification.
The TNotificationCenter component now supports Windows 8 and later Windows versions. This component has also undergone some minor changes:
It provides a Loaded property to check whether the notification center is ready to use or not.
The type of ApplicationIconBadgeNumber has changed from Word to Integer.
Its Supported method is no longer necessary and has been removed.
The TBaseNotificationCenter class has replaced the IFMXNotificationCenter interface. Classes that used to implement the IFMXNotificationCenter interface must become subclasses of TBaseNotificationCenter and implement the virtual abstract methods of their parent class.
Hereby how I figured out to display notifications now:
procedure TForm_Master.showNotification(Sender: TObject);
var
MyNotification: TNotification;
begin
MyNotification := NotificationCenter1.CreateNotification;
try
MyNotification.Name := 'NotificationName';
MyNotification.AlertBody :=
'Here goes your message';
MyNotification.FireDate := Now;
// Send notification to the notification center
NotificationCenter1.ScheduleNotification(MyNotification);
finally
MyNotification.Free;
end;
end;
I am a newbie to Delphi XE5 and currently developing Android platform applications on my Windows desktop using Delphi XE5.
I have two forms(Form1 and Form2) and tried to show Form2 in modal way on Form1 according to the way showed in Marco's RAD Blog(http://blog.marcocantu.com/blog/xe5_anonymous_showmodal_android.html).
But result was not as expected.
procedure TForm1.Button1Click(Sender: TObject);
var
frm2: TForm2;
begin
frm2 := TForm2.Create(nil);
ShowMessage('before frm2.ShowModal...');
frm2.ShowModal (
procedure(ModalResult: TModalResult)
begin
if ModalResult = mrOK then
if frm2.ListBox1.ItemIndex >= 0 then
edit1.Text := frm2.ListBox1.Items [frm2.ListBox1.ItemIndex];
frm2.DisposeOf;
end
);
ShowMessage('after frm2.ShowModal...');
end;
I wrote above code and run the application on an Android device.
I clicked the Button1, then I got the messagebox "before frm2.ShowModal... ", next "after frm2.ShowModal...", and then Form2 was showed.
I expect that the order should be 1)"before frm2.ShowModal... " message, 2) Form2 being showed, and 3) "after frm2.ShowModal..." message.
What's wrong with me?
The call to the anonymous ShowModal is not blocking, which means that any code after the ShowModal will be executed first.
One note here. Calling frm2.DisposeOf is wrong.
You must use this pattern:
declare
procedure TFrm2.FormClose(Sender: TObject; var Action: TCloseAction);
begin
Action := TCloseAction.caFree;
end;
See http://www.malcolmgroves.com/blog/?p=1585.
The documentation has been corrected in XE7, Using FireMonkey Modal Dialog Boxes, but this pattern can be used in all Delphi versions.
Conclusion: if you want to execute code after the modal dialog is closed, put that code inside the anonymous method.