-
Content Count
2982 -
Joined
-
Last visited
-
Days Won
134
Everything posted by Remy Lebeau
-
In the JSON you want to create, "registration_ids" is an array of strings. But in your code, you are creating "registration_ids" as an array of objects instead. To get the result you want, between WriteStartArray() and WriteEndOfArray(), you need to get rid of WriteStartObject(), WritePropertyName(''), and WriteEndObject(): wrtString := TStringWriter.Create(); wrtJSON := TJsonTextWriter.Create(wrtString); try wrtJSON.Formatting := TJsonFormatting.Indented; wrtJson.WriteStartObject; wrtJson.WritePropertyName('registration_ids'); wrtJson.WriteStartArray; //wrtJson.WriteStartObject; // <-- //wrtJson.WritePropertyName(''); // <-- wrtJson.WriteValue(strToken); //wrtJson.WriteEndObject; // <-- wrtJson.WriteEndArray; wrtJson.WritePropertyName('notification'); wrtJSON.WriteStartObject; wrtJSon.WritePropertyName('title'); wrtJson.WriteValue(edtBaslik.Text); wrtJson.WritePropertyName('body'); wrtJson.WriteValue(edtMesaj.Text); wrtJSON.WriteEndObject; wrtJSON.WriteEndObject; That said, why are you using TJsonTextWriter at all? This would be much more straight-forward if you used TJSONObject and TJSONArray instead, eg: uses ..., System.JSON; var arr: TJSONArray; notif, obj: TJSONObject; strJSON: string; begin obj := TJSONObject.Create; try arr := TJSONArray.Create; try arr.Add(strToken); obj.AddPair('registration_ids', arr); except arr.Free; raise; end; notif := TJSONObject.Create; try notif.AddPair('title', 'Hi'); notif.AddPair('body', 'Notification test'); obj.AddPair('notification', notif); except notif.Free; raise; end; strJSON := obj.ToJSON; // or, obj.Format finally obj.Free; end; // use strJSON as needed... end;
-
POP, IMAP and SMPT with OAuth2 (Microsoft Office 365 services)
Remy Lebeau replied to Milo G's topic in Network, Cloud and Web
The simplest solution that will not require any code changes is to have your Outlook/Exchange users turn on 2-step verification in their accounts and then generate application-specific passwords. Otherwise, you will have to update your code to use OAuth2 when connected to Outlook/Exchange. -
Delphi TClientSocket can't connect to Arduino server, Socket error: 10061
Remy Lebeau replied to Dominik99's topic in VCL
TClientSocket is just a thin wrapper around a TCP socket. WebSocket is a protocol that runs on top of TCP. Thus, TClientSocket will happily connect to a WebSocket server... when used correctly, which you are not... But that is not the IP you are trying to connect to... This is where your mistake is. The Host and Address properties are mutually exclusive, you can only use one of them. The Host has priority over the Address. So, you are trying to connect to 'localhost' (ie 127.0.0.1) only. That is why you are getting an error, since there is no TCP server running on port 6667 on your local machine. You need to either 1) drop the assignment of the Host altogether and just use the Address, or else 2) set the Host to the Arduino's hostname/IP: Client.Host := '192.168.0.24'; As far as TClientSocket is concerned, there is no difference. It only knows about TCP, not any protocols on top of TCP (Ethernet is underneath), so if you continue using TClientSocket then you are going to have to implement the WebSocket protocol yourself from scratch. Read RFC 6455 for that. Otherwise, there are plenty of 3rd party WebSocket libraries available that you can use instead. -
No. Embarcadero changed their versioning scheme in 11.0: https://blogs.embarcadero.com/rad-studio-11-is-coming-new-version-announcement-and-beta-invite-for-update-subscription-customers/ Unlike with 10.x, which were all major versions where 10.x.y were minor updates, 11.x are minor updates to 11.0, which is the major version. Only major versions can co-exist. Minor updates do not. Yes. No.
-
Change parameters when compiling using dcc32.exe
Remy Lebeau replied to PenelopeSkye's topic in RTL and Delphi Object Pascal
What kind of credentials, exactly? The IDE doesn't require credentials to compile a project. Chances are, there is something inside your project itself that is requiring credentials when the app starts running. Maybe a database? Or maybe the project contains a UAC manifest that requires the app to run elevated with admin permissions? There is not enough info provided to diagnose this. Can you provide a screenshot of the credential prompt? Then someone can tell you whether it is a native OS dialog or not, at least. -
POP, IMAP and SMPT with OAuth2 (Microsoft Office 365 services)
Remy Lebeau replied to Milo G's topic in Network, Cloud and Web
There is currently a sasl-oauth branch in Indy's GitHub repo which adds new SASL components for using OAuth tokens with the POP3/IMAP4/SMTP clients. Yes, because each service provides their OAuth tokens in their own ways. So, outside of POP3/IMAP4/SMTP, you have to first obtain the tokens for the particular services you want to talk to, and then you can give those tokens to Indy for POP3/IMAP4/SMTP authentication. -
I'm sorry, but I don't understand what you are asking for. Please clarify.
-
any body can write this code by delphi?
Remy Lebeau replied to mofareh's topic in Network, Cloud and Web
If you are using Indy, the code would look something like this: uses ..., System.Classes, IdCoderMIME, IdHTTP, System.IOUtils; procedure Main(const filename, MassegeTxt, mobileNo: String); var WebRequest: TIdHTTP; SSL: TIdSSLIOHandlerSocketOpenSSL; bytes: TBytes; file: String; instance_id: String; token: String; mobile_number: String; postdata: TStringList; begin // send image as base64 bytes := TFile.ReadAllBytes(filename); file := TIdEncoderMIME.EncodeBytes(bytes); instance_id := 'instance xxxxx'; token := 'xxxxxxx'; mobile_number := mobileNo; WebRequest := TIdHTTP.Create; try SSL := TIdSSLIOHandlerSocketOpenSSL.Create(WebRequest); SSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; WebRequest.IOHandler := SSL; WebRequest.Request.ContentType := 'application/x-www-form-urlencoded'; postdata := TStringList.Create; try postdata.Add('token=' + token); postdata.Add('to=' + mobileNo); postdata.Add('image=' + file); postdata.Add('caption=' + MassegeTxt); Writeln(WebRequest.Post('https://api.ultramsg.com/' + instance_id + '/messages/image', postdata)); finally postdata.Free; end; finally WebRequest.Free; end; end; If you want to use Embarcadero's TNetHTTPClient, the code would look something like this instead: uses ..., System.Classes, System.SysUtils, System.NetEncoding, System.Net.HTTPClientComponent, System.IOUtils; procedure Main(const filename, MassegeTxt, mobileNo: String); var WebClient: TNetHTTPClient; bytes: TBytes; file: String; instance_id: String; token: String; mobile_number: String; postdata: TStringList; begin // send image as base64 bytes := TFile.ReadAllBytes(filename); file := TNetEncoding.Base64.EncodeBytesToString(bytes); instance_id := 'instance xxxxx'; token := 'xxxxxxx'; mobile_number := mobileNo; WebClient := TNetHTTPClient.Create(nil); try WebClient.ContentType := 'application/x-www-form-urlencoded'; postdata := TStringList.Create; try postdata.Add('token=' + token); postdata.Add('to=' + mobileNo); postdata.Add('image=' + file); postdata.Add('caption=' + MassegeTxt); Writeln(WebClient.Post('https://api.ultramsg.com/' + instance_id + '/messages/image', postdata, nil, TEncoding.UTF8, nil).ContentAsString()); finally postdata.Free; end; finally WebClient.Free; end; end; Or, using Embarcadero's THTTPClient instead: uses ..., System.Classes, System.SysUtils, System.IOUtils, System.NetEncoding, System.Net.HttpClient; procedure Main(const filename, MassegeTxt, mobileNo: String); var WebClient: THTTPClient; bytes: TBytes; file: String; instance_id: String; token: String; mobile_number: String; postdata: TStringList; begin // send image as base64 bytes := TFile.ReadAllBytes(filename); file := TNetEncoding.Base64.EncodeBytesToString(bytes); instance_id := 'instance xxxxx'; token := 'xxxxxxx'; mobile_number := mobileNo; WebClient := THTTPClient.Create; try WebClient.ContentType := 'application/x-www-form-urlencoded'; postdata := TStringList.Create; try postdata.Add('token=' + token); postdata.Add('to=' + mobileNo); postdata.Add('image=' + file); postdata.Add('caption=' + MassegeTxt); Writeln(WebClient.Post('https://api.ultramsg.com/' + instance_id + '/messages/image', postdata, nil, TEncoding.UTF8, nil).ContentAsString()); finally postdata.Free; end; finally WebClient.Free; end; end; -
C++ has __FILE__ and __LINE__ macros. There is no equivalent in Delphi. Is that what you are asking for? If not, then please clarify your question.
-
Floating Form Designer option gone in Delphi Alexandria?
Remy Lebeau replied to PeterPanettone's topic in Delphi IDE and APIs
I wouldn't hold my breath on that. They made the intentional decision to remove the Floating Designer in 10.4.1. It is gone. I don't see them ever adding it back in, they want people to use the docked editor instead. But, as the blog article explained, you can still have multiple editors open, across multiple monitors. -
You should be able to simply replace this: Date_SentIdx := Cursor.GetColumnIndex(StringToJstring('date_sent')); with this: Date_SentIdx := Cursor.GetColumnIndex(StringToJstring('report_date'));
-
Known issue, at least for "date_sent". You need to use "report_date" instead. See SMS messages date_sent on Android with Delphi XE5 always returns 0? on StackOverflow. Not sure if there is a similar workaround for "status".
-
You don't necessarily need threading, you just need to return control to the main UI message loop periodically. For instance, break up your loop into chunks that you can trigger with TThread.ForceQueue() for each iteration, eg: procedure TMyForm.DoProcessing(i: Integer); begin Label1.Text := 'Step ' + IntToStr(i); Inc(i); if i <= 10 then TThread.ForceQueue(nil, procedure begin DoProcessing(i); end, 200) else TThread.ForceQueue(nil, AfterProcessing); end; procedure TMyForm.StartProcessing; begin DoProcessing(1); end; procedure TMyForm.AfterProcessing; begin ... end;
-
Agreed. For lengthy periods, I would ask the OS to notify me when the desired time has been reached, rather than polling the time regularly.
-
The TTmer interval is expressed in milliseconds, not microseconds. Yes. 4,294,967,295 milliseconds is 49.7 days. What EXACTLY is not working for you?
-
<sigh> Turns out they are methods of TControl, which TForm is not derived from in FMX, unlike in VCL. And this is just one of a hundred reasons why I hate FMX and will never ever use it.
-
Then what does this have to do with Delphi/FMX?
-
Did you try Repaint()'ing the Form itself, rather than just the TLabel?
-
No, but Repaint() is. In any case, depending on what the rest of your loop is doing, it may be worthwhile to move the logic into a worker thread, and then have that post updates to the UI thread when it wants to display something.
-
TRichEdit in Delphi 11.1 ("Courier New" font broken with formatting characters))
Remy Lebeau replied to Denis Dresse's topic in VCL
Just a small nitpick - you can change this: RE.SelStart := Perform(EM_LINEINDEX, myLineIndex, 0); RE.SelLength := length(Lines[myLineIndex]); To this instead: var myLineStart: Integer; myLineStart := Perform(EM_LINEINDEX, myLineIndex, 0); RE.SelStart := myLineStart; RE.SelLength := Perform(EM_LINELENGTH, myLineStart, 0); That will avoid a memory allocation trying to retrieve the actual String of the line in question. The RichEdit already knows how many characters are on the line without needing to access the actual characters. An then to can be optimized further by using EM_EXSETSEL directly instead of the SelStart/Length properties: var rng: CHARRANGE; rng.cpMin := Perform(EM_LINEINDEX, myLineIndex, 0); rng.cpMax := rng.cpMin + Perform(EM_LINELENGTH, rng.cpMin, 0); Perform(EM_EXSETSEL, 0, LParam(@rng)); Further speed optimizations can be accomplished using EM_SETCHARFORMAT/2 and EM_SETEVENTMASK messages, see Faster Rich Edit Syntax Highlighting for details.- 17 replies
-
- trichedit
- delphi 11.1
-
(and 1 more)
Tagged with:
-
Getting of dynamic array from Variant
Remy Lebeau replied to lookin030577's topic in RTL and Delphi Object Pascal
I honestly can't see how it could ever work to assign a pointer-to-pointer-to-data to a dynamic-array. What is the original code trying to accomplish, exactly? If it just wants to access the elements of the Variant array, there is no need to assign the raw data pointer to a dynamic array at all. I seriously doubt that. I would have to see how the original code is populating the Variant, and how it is accessing the dynamic array that is assigned from the Variant's raw data. Because it is not supposed to work (and you should not have relied on it to begin with, even if it did compile. Just because something compiles doesn't make it right). Embarcadero intentionally changed the compiler in a later version to no longer allow assigning a raw untyped Pointer to a dynamic array. -
Getting of dynamic array from Variant
Remy Lebeau replied to lookin030577's topic in RTL and Delphi Object Pascal
By default, an array inside a Variant is stored as (a pointer to) a COM SAFEARRAY: https://docs.microsoft.com/en-us/archive/msdn-magazine/2017/march/introducing-the-safearray-data-structure https://docs.microsoft.com/en-us/windows/win32/api/oaidl/ns-oaidl-safearray Which is completely different than a Delphi-style dynamic array. The two are not compatible with each other. The only way to store a Delphi-style dynamic array AS-A dynamic array in a Variant is to use a custom Variant type: https://docwiki.embarcadero.com/RADStudio/en/Defining_Custom_Variants That being said, Delphi 10.2 and later are more strict about NOT letting you assign a raw Pointer to a dynamic array, as you are trying to do: So, you would have to type-cast the raw Pointer in order to get the old Delphi 6 behavior, eg: type TDoubleArray = array of double; var Value: Variant; //this variable is assigned to a dynamic array somewhere in the code ... procedure DoSomething; var rv: TDoubleArray; begin rv := TDoubleArray(TVarData(Value).VArray.Data); // note, no @ used here. Why would you want to assign a PPointer to a dynamic array??? ... end; But, it never made sense to me why anyone would ever want to do this, since this makes the dynamic array point at something that is not a valid dynamic array, and can't be accessed as a dynamic array. The structure of a SAFEARRAY and a dynamic array are completely different. In any case, to safely access the raw data of a SAFEARRAY, you MUST use the SafeArrayAccessData() function, which Delphi's RTL has wrapped in the VarArrayLock() function, eg: var Value: Variant; //this variable is assigned to a dynamic array somewhere in the code ... procedure DoSomething; var rv: PDouble; begin rv := PDouble(VarArrayLock(Value)); try // use rv as needed... finally VarArrayUnlock(Value); end; end; -
That line is effectively setting SSLOptions.Method to sslvSSLv23 and SSLOptions.SSLVersions to [sslvTLSv1_1, sslvTLSv1_2]. Which means, if the server does not support TLS 1.1 or TLS 1.2 then the TLS handshake will fail. Typically, that line should be as follows instead: SSL.SSLOptions.SSLVersions := [sslvSSLvTLS1, sslvTLSv1_1, sslvTLSv1_2]; The sslvSSLv23 flag has special meaning for Indy's internals and should not be used directly in most cases. The only valid use for using sslvSSLv23 in your code is to enable all supported SSL/TLS versions, eg: SSL.SSLOptions.Method := sslvSSLv23; // will set Method=sslvSSLv23 and SSLVersions=[sslvSSLv2,sslvSSLv3,sslvTLSv1,sslvTLSv1_1,sslvTLSv1_2] SSL.SSLOptions.SSLVersions := [sslvSSLv23]; // will set Method=sslvSSLv23 and SSLVersions=[sslvSSLv2,sslvSSLv3,sslvTLSv1,sslvTLSv1_1,sslvTLSv1_2] Otherwise, just ignore that the sslvSSLv23 flag exists, and set the SSLVersions property to just the desired protocol versions (2/3 is not a protocol version, it is a wildcard).
-
That is because your code is not using the IdSSL variable for anything. You are creating the TIdSSLIOHandlerSocketOpenSSL instance and assigning it directly to the TIdHTTP.IOHandler property, thus leaving all of the IOHandler's sub-properties at their default values. Assign the instance to the IdSSL variable first, and then you can customize its properties as needed, eg: lHTTP := TIdHTTP.Create(nil); try IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(lHTTP); IdSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; // set other properties as needed... lHTTP.IOHandler := IdSSL; ... finally lHTTP.Free; end;
-
TObject, InstanceSize and thread synchronization
Remy Lebeau replied to chkaufmann's topic in RTL and Delphi Object Pascal
The extra bytes are for a hidden pointer to a System.TMonitor instance (not to be confused with Vcl.Forms.TMonitor). Every TObject instance has an optional TMonitor instance associated with it. The actual TMonitor instance is created the 1st time it is accessed, but the parent TObject's InstanceSize includes space for a pointer to the TMonitor instance. AFAIK, yes. Read the TMonitor documentation, and also see What is TMonitor in Delphi System unit good for? You could, yes. You would simply call TMonitor.Enter(obj) and TMonitor.Exit(obj) where needed.