Category: Firemonkey

How detect the mouse back and forward buttons in a Delphi FMX Windows form?

Has anyone found a way to detect the mouse back and forward buttons in a Delphi FMX form in Windows (and only Windows)? I understand this works fine in a VCL application, using procedure WMAppCommand(var Msg: Winapi.Messages.TMessage); message WM_APPCOMMAND; but this has no effect in an FMX application. If anyone already has worked out a solution to this, would very much appreciate a hint (or the code they used, of course).
Read More

Why is recorded microphone audio sometimes truncated in Delphi?

My Delphi 10-4-2 FMX project captures microphone audio to a file using this code: private fFileIncrement: Integer; fMic: TAudioCaptureDevice; procedure TForm1.RecordButtonClick(Sender: TObject); begin fMic := TCaptureDeviceManager.Current.DefaultAudioCaptureDevice; if fMic <> nil then begin RecordButton.Enabled := False; StopButton.Enabled := True; Inc(fFileIncrement); fMic.FileName := 'TESTWAVE-' + fFileIncrement.ToString + '.wav'; fMic.StartCapture; end; end; procedure TForm1.StopButtonClick(Sender: TObject); begin if (fMic <> nil) and (fMic.State = TCaptureDeviceState.Capturing) then begin fMic.StopCapture; StopButton.Enabled := False; RecordButton.Enabled := True; end; end; Intermittently, approximately one out of three audio files are missing the last half second of the recording. What would cause the end of the audio to be cut?
Read More

FMX: Setting TScrollBar thumb size by changing ViewportSize reduces maximum settable value

Add a TScrollBar to a form in FMX and leave min and max at the default values 0 and 100. Set ViewportSize to 50. This makes the thumb half then length of the scroll bar. However, the value for the scroll bar can now only be set between 0 and 50. It seems that the maximum settable value is Max - ViewportSize. How can I set the thumb size while still being able to set values between 0 and Max? I know that I could set Max to 150 (Max + ViewportSize) but I'm already using Max in my code to determine what the maximum settable value is. I note that the documentation is no help at all. It doesn't mention this issue and also incorrectly states that ViewportSize is in pixels.
Read More

Android Socket Timeout Exception during Rest data connection, only during mobile network connection

After updating Delphi version 10.1 to 10.4.2, I encountered this problem, which only occurs on some Android 10 and 11 devices. The APP exchanges data with the server via TDSRestConnection. On the same phone, where previously the app developed with Delphi 10.1 worked correctly, stops working after the update to Delphi version 10.4.2 and after the Android upgrade on the phone. The APP works only over the WiFi network and no longer works over the mobile network, generating this error after a while (7/8 seconds): failed to connect to / (port 17200) from / (port 42050) after 300000ms: isConnected failed: ETIMEDOUT (Connection timed out). I also tried using different internet mobile providers (TIM, VODAFONE) The 4G connection is Very Good and the Internet speed test doesn't show any problem. The error doesn't appear immediately. The function GetSql is called more times in the sincro procedure, but unfortunately it stops working not at the same point. The code: Client side: const fConnectTimeout=300000; const fReadTimeout=300000; function TServerMethods1Client.GetSql(StringaSql: string; const ARequestFilter: string): TFDJSONDataSets; begin if FGetSqlCommand = nil then begin FGetSqlCommand := FConnection.CreateCommand; FGetSqlCommand.RequestType := 'GET'; FGetSqlCommand.Text := 'TServerMethods1.GetSql'; FGetSqlCommand.Prepare(TServerMethods1_GetSql); end; FGetSqlCommand.Parameters[0].Value.SetWideString(StringaSql); FGetSqlCommand.Execute(ARequestFilter); if not FGetSqlCommand.Parameters[1].Value.IsNull then begin FUnMarshal := TDSRestCommand(FGetSqlCommand.Parameters[1].ConnectionHandler).GetJSONUnMarshaler; try Result := TFDJSONDataSets(FUnMarshal.UnMarshal(FGetSqlCommand.Parameters[1].Value.GetJSONValue(True))); if FInstanceOwner then FGetSqlCommand.FreeOnExecute(Result); finally FreeAndNil(FUnMarshal) end end else Result := nil; end; function SelectSql(StringaSQL:String;tb:TFDMemTable;out Errore:String):Boolean; overload; var jds: TFDJSONDataSets; data: TFDAdaptedDataSet; begin try result:=false; if DMClientRestModule=nil then DMClientRestModule:=TDMClientRestModule.Create(nil); DMClientRestModule.DSRestConnection1.Port := STRTOINT(REST_PORT); DMClientRestModule.DSRestConnection1.Host := REST_HOST; DMClientRestModule.DSRestConnection1.SessionID := ''; DMClientRestModule.DSRestConnection1.HTTP.ConnectTimeout := fConnectTimeout; DMClientRestModule.DSRestConnection1.HTTP.ReadTimeout := fReadTimeout; jds :=TFDJSONDataSets.Create; jds := DMClientRestModule.ServerMethods1Client.GetSql(StringaSql); if (Assigned(jds)) then begin data := TFDJSONDataSetsReader.GetListValue(jds, 0); tb.AppendData(data); tb.Open; result:=true; end; try DMClientRestModule.Free; DMClientRestModule:=NIL; except end; except result:=false; Errore:=GetStrException(ExceptObject,ExceptAddr); end; END; Server side: function TServerMethods1.GetSql(StringaSql: String): TFDJSONDataSets; var dset:TFDJSONDataSets; begin try DbConn.Params.Clear; //TFDConnection DbConn.Params.Database:=fdb; DbConn.Params.UserName:=fuid; DbConn.Params.Password:=fpwd; DbConn.Params.Add('server='+fdbserver); DbConn.Params.Add('port='+fport); DbConn.Params.DriverID:='MySQL'; try q1.Close; // TFDQuery if not DbConn.Connected then DbConn.Connected:=true; if not DbConn.Connected then log('Not conneted to db') else log('Connected to db') ; with q1 do begin close; SQL.Clear; SQL.Add(StringaSql); Log(StringaSQl); open; end; Result := TFDJSONDataSets.Create; // The "TFDJSONDataSetsWriter" class provides static "ListAdd" method. // It uses reflection to convert results of the query into "TFDJSONDataSets". TFDJSONDataSetsWriter.ListAdd(Result,'',q1); except Log(GetStrException(ExceptObject,ExceptAddr)); Result := TFDJSONDataSets.Create; TFDJSONDataSetsWriter.ListAdd(Result,'',q1); end; finally DbConn.Connected:=false; end; end; update 16/04/2021 Investigating better, it seems that the Sql String that causes the exception is the following (Long text sql string) StringaSql :='SELECT PLANNING_ENGINEERS.DATE_JOB,NOT_A_JOB, '; StringaSql := StringaSql + ' PLANNING.PLAN_ID,PLANNING.CONTACTID,PLANNING.CALLER_REFER, '; StringaSql := StringaSql + ' PLANNING.COMPANYID,PLANNING.DEPOTID,CALLTYPEID,TIME_BEGIN,'; StringaSql := StringaSql + ' TIME_END,LOCATION_DESCRIPTION,ADDRESS1,ADDRESS2,ZIP,PLACE,PROVINCE,'; StringaSql := StringaSql + ' PHONE1,PHONE2,FAX,EMAIL,LATITUDE,LONGITUDE, '; StringaSql := StringaSql + ' PLANNING.DELETED,PLANNING.CLOSED,PLANNING.SUSPENDED,PLANNING.NOTES,'; StringaSql := StringaSql + ' PLANNING.INTERNAL_NOTES,PLANNING.SUGGESTION_NOTES,PLANNING.CLOSING_NOTES,ORDER_REFERENCE,EXTERNAL_REFERENCE, '; StringaSql := StringaSql + ' EXTERNAL_REFERENCE_DESC '; StringaSql := StringaSql + ' ,(SELECT CALLTYPES.DESCRIPTION FROM CALLTYPES WHERE CALLTYPES.CALLTYPE_ID=PLANNING.CALLTYPEID)'; StringaSql := StringaSql + ' AS CALL_DESC '; StringaSql := StringaSql + ' ,(SELECT CALLTYPES.TYPE FROM CALLTYPES WHERE CALLTYPES.CALLTYPE_ID=PLANNING.CALLTYPEID)'; StringaSql := StringaSql + ' AS CALL_TYPE '; StringaSql := StringaSql + ' ,(SELECT DEPOTS.NOTES FROM DEPOTS WHERE DEPOTS.DEPOT_ID=PLANNING.DEPOTID)'; StringaSql := StringaSql + ' AS DEPOT_NOTES '; StringaSql := StringaSql + ' FROM PLANNING,PLANNING_ENGINEERS WHERE '; StringaSql := StringaSql + ' PLANNING.DELETED=''N'' AND PLANNING.CLOSED=''N'' AND PLANNING.SUSPENDED=''N'' '; StringaSql := StringaSql + ' AND PLANNING_ENGINEERS.PLANID=PLAN_ID '; StringaSql := StringaSql + ' AND PLANNING_ENGINEERS.ENGINEERID='+'42'; StringaSql := StringaSql + ' AND PLANNING_ENGINEERS.DATE_JOB BETWEEN '+QuotedStr(FormatDateTime('YYYYMMDD', date))+' AND '+QuotedStr(FormatDateTime('YYYYMMDD',date))+' '; StringaSql := StringaSql + ' ORDER BY DATE_JOB,TIME_BEGIN'; During the WIFI connection, the call to the server works fine then it send back the result. Instead during the 4G connection, the call raise the exception below: Exception ENetHTTPClientException in module ... : Connection reset. I also reported a ticket RSP-33699 to Embarcadero Here the video: RestIssue
Read More

GExperts bug: CTRL+V on FMX form designer inserts into secondary editor window

I got a bug report for GExperts and Delphi 10.4 that’s really curious: When a secondary editor window is open in the IDE and the FMX form designer is active, trying to insert a component from the clipboard into the form inserts the textual description of that component into the editor windows instead. I could immediately reproduce this but finding the culprit took quite a bit longer. Observation 1: It does not happen for the VCL form designer. Observation 2: It only happens, if you use CTRL+V to insert the component. The form designer’s context menu entry works fine. Observation 3: Even disabling all experts in GExperts did not solve this problems… Until you restart the IDE, then it’s gone, even if you then enabled the experts again… Until you restart the IDE again which brings it back. After a lot of trial and error I found that the cause are two of the GExperts editor experts: Goto Previous Modification Goto Next Modification Disabling these experts and restarting the IDE solves the problem. These are rather simple experts that only add entries to the editor window’s context menu for a functionality that already is part of the IDE. I added them to make that functionality more visible, and because I could. Since Delphi 10.3 I had to use a workaround to still be able to add entries to that menu because apparently it is being recreated in the OnPopup event. I think this code somehow activates the menu entries or their associated actions even if the editor window doesn’t have the focus. So for now until I find a real workaround: If you have this problem, disable these two experts. (The workaround might be to remove these experts altogether. They aren’t that useful anyway.) If you want to comment on this blog post, you can do so in the corresponding topic in the international Delphi Praxis forum.
Read More

How to fix a column in TStringGrid with delphi FMX?

I want to make a grid with FireMonkey where it has a fixed column, but neither TGrid nor TStringGrid have that property, nor allow me to change it dynamically at runtime, as far as I can see. Does anyone have a way to make this work? Imagine I have 500 columns (I don't, and I won't have that number, this is just for demonstration purposes). I want to lock the first column so its content will always be visible as the user scrolls the grid along the X axis (horizontally). Same thing as with the header row, it's always locked in place so when the user scrolls along the Y axis, the header is always visible. I am using Delphi 10.3.3 CE.
Read More

Turbo Boost Your Delphi Knowledge – Become a Docwiki Ninja Warrior

I’ve made a commitment to learn FireMonkey. My flagship software product, Zilch Standard, turned 30 years old in 2021 and I still don’t have a macOS version. I’ve got macOS consumers who buy my Windows® software and then email asking if there’s a version that runs on their mac computer. This inevitably leads to me issuing a refund. So, I have a pretty strong incentive to learn FireMonkey.

“I feel like I’m sitting in the cockpit of an airplane.”

I tried going down the FireMonkey learning path before but unfortunately I didn’t get very far. Learning FireMonkey makes me feel like I’m sitting in the cockpit of an airplane. There’s so many things that are unfamiliar. The scope of it all is overwhelming. There’s so much to learn. I don’t know where to look first. What about the help? It’s just an [F1] key press away!

I don’t know about you but I get frustrated at the built-in help that comes with Delphi? Does this sound familiar? You’re frantically trying to figure something out… pressing [F1] all over the place, and hopelessly rummaging around through Delphi’s built-in help. Sometimes it’s great and gives you what you exactly what are looking for. Often times you are left feeling like you just read a bunch of cryptic tea leaves. 

Where do you turn at this point? The Facebook Delphi groups? Stackoverflow?  Your stash of Delphi books? Delphi’s dockwiki?
In the past I’ve rarely thought to use Delphi’s docwiki. In my mind I felt like it was the same rehashed [F1], cryptic leaves stuff. Well, that changed last night. 
It turns out, I wasn’t using the dockwiki in a meaningful way. I figured out how to exploit the value of the docwiki. I’m going to show you how to become a docwiki ninja warrior.

“I’ll show you how to become a Delphi Docwiki Ninja”

It all started last night when I wanted to learn more about FormFactor. I wrote a simple FMX application that displays the ClientHeight, ClientWidth, FormFactor.Width, and FormFactor.Height in the OnResize event handler. I was curious why the FormFactor values never changed as the form was resized, so I wanted to learn more. That’s when I highlighted FormFactor in the Object Inspector and pushed [F1] .

This brought up the built-in help for FormFactor. This is one of those cryptic tea leaves help reference.

This wasn’t very helpful. I may have done some googling to learn more but couldn’t find anything that talked about FormFactor. What it is? How it’s used? Why it’s used? So I posted my question to StackOverflow
Someone commented and said “Please look at the documentation. It seems to concern iOS only, and changes according target device selection.”
How did he find that docwiki page? That’s not the docwiki page I found. In fact I couldn’t find the dockwiki page referenced in StackOverflow comment. Turns out I didn’t understand how to use the dockwiki. I was stuck in the API library and couldn’t got out!

I was stuck in the API library and couldn’t get out!

That’s when I discovered how to become a Docwiki Ninja Warrior. This takes less than a minute to setup and I believe it will save you hours looking for information about Delphi that is relevant to what you are looking for. For illustrative purposes I’m sticking with my original pursuit of FormFactor.
Step 1: From inside the IDE click Help > RAD Studio Docwiki

Step 2: This opens the RAD Studio docwiki page in your default browser.
Step 3: Right-click on Library Reference > Open link in new tab 

Step 4: This opens the Library docwiki page in a new browser tab.
Step 5: Right-click on Code Example > Open link in a new tab.

At this point you should have three tabs in your browser each dedicated to separate docwiki sections. And now the fun begins.
Step 6: Enter FormFactor in the search box for tab 1 RAD Studio and press [Enter]. I’m horrible at spelling so I usually just copy and paste the value I need. I just highlighted FormFactor in the IDE code editor and pasted it in the search box. 

Step 7: Switch to tab 2 Library docwiki page and paste or enter FormFactor in the search box and press [Enter].

Step 8: Switch to tab 3 Code Examples docwiki and paste or enter FormFactor in the search box and press [Enter].

“So, what did I learn about FormFactor?”

FormFactor is only used used when you are creating an iOS application. This is awesome. I learned enough about FormFactor to satisfy my current need. Because I’m focused right now on learning how to use FireMonkey to create desktop applications for Windows and macOS, FormFactor is not relevant to my current situation. This lets me know I can skip a thorough investigation about FormFactor for the time being. It also lets me know that ignoring FormFactor at this time is okay since it does not apply to desktop applications for Windows or macOS. I don’t need to be concearned about FOMO (fear of missing out).

However, if FormFactor was something I was interested in, the three different docwikis provide ample links to related, relevant, information in one convenient location. 
Oh yeah, make sure to play around with the “Advanced” link on the special search results pages.
There you have it. I am so glad I stumbled upon this. It will surely be very helpful in my quest to learn FireMonkey. 
Semper Fi,
Gunny Mike

Read More

Read More

Specifying an IP Address in FireMonkey TWebBrowser

The FireMonkey TWebBrowser component allows the developer to specify a local file (file://...) or a URL (https://www...). However, there seems to be no way to get the browser to work by specifying an IP address (192.168.1... or http://192.168.1... etc.) The browser just appears to hang. Is it possible to specify an IP address with this component? I'm currently using Delphi 10.4.2 developing for iPhone. Safari on the same phone has no problem accessing 192.168.1... addresses.
Read More

Why does the FormFactor.Width and FormFactor.Height not change when a Delphi FMX application is resized?

I’m a long time Delphi VCL developer and I’m just starting to learn FMX. I created a simple little application that displays the ClientHeight, ClientWidth, FormFactor.Width, and FormFactor.Height inside a TMemo in the OnResize event handle… … Read More

Read More

SAPI (Microsoft Speech API) problems with the IDE

Every few months or so someone sends me an email telling me they’ve speech-enabled their application with SAPI (the Microsoft Speech API) after having read my old coverage (specifically that for SAPI 5.1) from conferences in 2002 and before. It’s always good to hear that stuff you’ve worked on is being found useful, even long after you did it, in this case frighteningly close to 2 decades ago (Ͼ˳Ͽ)..!!!

Sometimes the communications are asking how to do specific things with SAPI – unfortunately those are typically a dead end, as I haven’t used SAPI in anger for a long while.

Sometimes, though, people are stuck on the basics and I was recently contacted by someone with a fairly current version of RAD Studio saying they couldn’t get started at all. It turns out that the current version of SAPI, SAPI 5.4, cannot be imported into RAD Studio!

Uh-oh. Time to investigate…

The steps to import SAPI components into the IDE is as follows (and is documented here):

  • Choose Component, Import Component… (as mentioned in the documentation)
  • Select Import a Type Library and press Next
  • Choose the Microsoft Speech Object Library that is defined in sapi.dll:
  • Press Next and choose a target Palette Page:
  • Press Next, select Install to New Package and press Next again
  • Now choose a value for Package name (and optionally for Description) and press Finish:
At this point the package will be created and a type library import unit is added to it and the IDE will try to build the package. Immediately something is flagged up:

Click Yes and the package will start building proper. This is where it all falls apart:

Hmm, is there a massive FUBAR in the type library importer? Sure looks like that could be the case….
However, upon closer inspection the issue actually comes up thanks to the SAPI type library defining its own versions of the already-defined-in-Delphi interfaces IStream and ISequentialStream. In the SAPI versions they have extra methods, references to which then fail to compile thanks to those methods not being imported.

Why are those ‘known’ interfaces not imported? Well, the idea is to avoid ambiguity with having multiple definitions, but that’s not important as… we can override this omission!

In your RAD Studio bin folder (e.g. C:\Program Files (x86)\Embarcadero\Studio\21.0\bin) you will find a file called tlibimp.sym. This is the configuration file for the type library importers (both the one in the IDE we just used and also the command-line utility TLibImp.exe). The content of this configuration file affect the way type libraries get imported to help cover various trouble areas. In this case we need to remove some settings to get the SAPI type library imported.

Here is a part of tlibimp.sym:

;; Names listed in this section are skipped by the importer         
;; Built-in Interfaces, such as IUnknown and IDispatch are listed
;; here
We need to comment out the IStream and ISequentialStream lines to get a successful import. Once those 2 lines have semicolons inserted at their start we can try again.

Remove the emitted import unit SpeechLib_TLB.pas from the package still open in the IDE, then delete that unit from disk and close the package project.

Now you can go through the steps above again, but this time instead of selecting Install to New Package you select Install to Existing Package. After pressing Next you press Browse and locate the original DCLSAPI54.dpk project file, which by default will have been saved in the default Delphi projects folder (Embarcadero\Studio\Projects within your Documents folder tree).

This time the package will build and you will be told of the component wrappers now installed in the IDE:

If you want to import the SAPI type library from a RAD Studio Command Prompt you can run this command:

tlibimp -P -Yh+ -Yr+ -HpsActiveX C:\WINDOWS\System32\Speech\Common\sapi.dll

Note that the SAPI 5.x sample applications from the 2002 conference paper have been updated and are available along with an updated import package at these URLs:

Read More

Read More

Embeded VCL Form with FMX Form

I want to use TWindowsMediaPlayer in an FMX Form. I create it with this code, but playing media causes an error: Access violation at address 004080D6 in module 'Project1.exe'. Read of address 00000500 How can resolve it? var frmvlc: TFrmVlc; begin frmvlc := TFrmVlc.Create(self); frmvlc.WindowsMediaPlayer1.URL := filename; // it play video but not show it. self.AddObject(TFmxObject(frmvlc.Panel1)); error hier end;
Read More

Rad Studio 10.2.3 error when install apk: INSTALL_PARSE_FAILED_NO_CERTIFICATES

How can I solve this problem? C:\Users\user\Documents\Embarcadero\Studio\19.0\PlatformSDKs\android-sdk-windows\platform-tools>adb install Project.apk Performing Streamed Install adb: failed to install Project.apk: Failure [INSTALL_PARSE_FAILED_NO_CERTIFICATES: Failed collecting certificates for /data/app/vmdl1331479076.tmp/base.apk: Failed to collect certificates from /data/app/vmdl1331479076.tmp/base.apk: Attempt to get length of null array] I am using RAD STUDIO 10.2.3, with SDK 25 to compile an app on Android 11. Thank you!
Read More

ForegroundService can’t start – FMX

On newer version android I'm change startService to startForegroundService but service can't start.In manifest file is added ForegroundService permission. LIntent := TJIntent.Create; LIntent.setClassName(TAndroidHelper.Activity.getBaseContext, TAndroidHelper.StringToJString('myService')); TAndroidHelper.Activity.startForegroundService(LIntent); On android 5 work fine: LIntent := TJIntent.Create; LIntent.setClassName(TAndroidHelper.Activity.getBaseContext, TAndroidHelper.StringToJString('')); TAndroidHelper.Activity.startService(LIntent);
Read More