-
Content Count
2914 -
Joined
-
Last visited
-
Days Won
130
Everything posted by Remy Lebeau
-
How know interface GUID from generic anonymous function using RTTI
Remy Lebeau replied to jairgza's topic in Algorithms, Data Structures and Class Design
There is nothing in the RTTI system for that purpose, no. You would have to manually parse the TRttiField.FieldType.QualifiedName of the field in question (in this case, yielding 'TFunc<ITestA>') to extract what is between the brackets ('ITestA'), and then get the TRttiType of that type from TRttiContext and typecast it to TRttiInterfaceType to get its GUID. Except that you can't use Generic types for DFM-streamable classes. The DFM system doesn't handle Generic class types. -
It SHOULD NOT be decoding the unparsed param like that. The encoded hex shown is valid UTF-8, and I can decode it manually into the two Emojis (U+1F605 and U+1F923). But the parsed param you have shown is the result of Indy storing the raw UTF-8 bytes into the String rather than decoding the UTF-8 bytes into Unicode text. Like I said earlier, TIdHTTPServer decodes the params using UTF-8 by default, unless overridden by an explicit charset in the request. Oh, wait... (checks Indy's change history...) that was actually a recent fix made earlier this year. Prior to that fix, Indy would indeed decode the Param data using 8bit instead of UTF-8, unless the request explicitly stated UTF-8. So that would explain the behavior you are seeing, and why the IndyTextEncoding_UTF8.GetString() workaround was needed. You should update to the latest version of Indy. Then the param data should be decoded properly as UTF-8 by default and you won't need the IndyTextEncoding_UTF8.GetString() workaround anymore. No, it is not fine. It is broken, actually. But it was fix several months ago. Yes, but you shouldn't have had to resort to that if Indy were decoding the param data correctly to begin with. Which clearly your version is not.
-
Handlin the message and returning value to the caller
Remy Lebeau replied to Tommi Prami's topic in Windows API
Yes, that will work fine. The VCL's internal WndProc that will first receive the message (StdWndProc() in System.Classes.pas) copies the tagMSG values to a local TMessage, then dispatches that TMessage to handlers, and then returns the TMessage.Result value back to the original sender. -
Can you provide an example? And is the data coming from the URL querystring, or the request body? I assume the latter. It makes a difference to how HTTP behaves. The code you showed using UTF8.GetString() only makes sense if TIdHTTPServer were storing UTF-8 bytes as Chars in a String, which it should not be doing unless the request specifies a charset (in the Content-Type header) which Indy does not recognize/support. Otherwise, it should be using that charset properly, or if there is no charset specified then it uses UTF-8 (assuming you are using an up-to-date version of Indy). So, it is a little hard to imagine that if Twilio is sending UTF-8 data for the 'Body' text that TIdHTTPServer would not be decoding it properly by default. But, that goes back to my earlier comment about needing to see the raw HTTP requests. No. It will happily wait as long as it takes to read in the request data and send out the response data.
-
The Installer Detector looks at a lot of different things, not just the filename: https://docs.microsoft.com/en-us/previous-versions/windows/it-pro/windows-vista/cc709628(v=ws.10) Hmm... Right, because the Elevation error occurs while the process is being created, before it starts executing.
-
In what way? Did you verify that ALL of the server events stopped (OnConnect, OnHeadersAvailable, etc)? Or is it just the OnCommandGet event? Do you have a capture of the requests that don't respond? Then it can't be a deadlock issue, since the server is still responsive to shutdown and restart, and thus the socket threads are not blocking. So there has to be an issue in the request handling. But there is WAY too much code provided with no explanation or context, so it is very difficult to diagnose your issue. Your OnCommandGet handler is pretty massive (also, there are a lot of places where Exit statements are missing that would avoid unnecessary processing). Have you considered breaking up the code and then unit-testing the pieces individually? That MIGHT be part of the problem. You really should not be catching unhandled exceptions, but if you do, make sure to RE-RAISE any Indy exceptions you happen to catch (they all derive from EIdException). The server needs to handle those internally in order to close the socket and stop its owning thread (unless you close the socket manually in your except block). Why are you doing that? The Params have already been decoded before OnCommandGet is called (assuming TIdHTTPServer.ParseParams is true, which it is by default), and UTF-8 is the default charset used for that decode (unless the request specifies a charset in its Content-Type header). Why can't you use the Body text as-is? Text := aRequestInfo.Params.Values['Body']; Since the text was already pre-decoded, re-encoding it and decoding it as a potentially different charset is just asking for trouble. This is a good way to corrupt the text. If you absolutely need custom decoding, then you should instead parse the ARequestInfo.UnparsedParams, ARequestInfo.QueryParams, and/or ARequestInfo.FormParams properties as needed. That is the raw data where TIdHTTPServer decodes the ARequestInfo.Params data from. Are you sure all of your Engine code is thread-safe and non-blocking? What is that supposed to mean? You can absolutely have a space after the ';' character, that will work just fine: AResponseInfo.ContentType := 'text/html; charset="UTF-8"'; Alternatively, simply use the separate AResponseInfo.CharSet property instead: AResponseInfo.ContentType := 'text/html'; AResponseInfo.CharSet := 'UTF-8'; This is wrong. One, because the LoadFromFile() is a potential exception waiting to happen, since you appear to be loading files using relative paths. ALWAYS use absolute paths instead! But more importantly, you are freeing the TMemoryStream that is assigned to the AResponseInfo.ContentStream property. Indy DOES NOT make a copy of that stream, so you are leaving behind a dangling pointer that will crash when the server tries to write the stream data to the client after the OnCommandGet handler exits. The AResponseInfo object will free the ContentStream for you when it is no longer needed, so DO NOT free it manually. Unless you really need to free the stream manually (which you don't in this cae), in which case you would have to call AResponseInfo.WriteHeader() and AResponseInfo.WriteContent() manually before freeing the stream. You should consider using Indy's TextIsSame() function for case-insensitive string comparisons. Same here. Also, is the SMS and Twilio code thread-safe and non-blocking? This is NOT thread-safe! You CANNOT access UI controls from outside of the context of the main UI thread. The OnCommandGet event is fired in the context of a worker thread, so you MUST synchronize with the main UI thread here. Otherwise, I would suggest storing the CheckBox value in a global Boolean variable at server startup, and then you can read that variable without synchronizing it (as long as you don't alter the vaiable's value while the server is running, otherwise you do need to synchronize it). If your OnCommandGet code encounters an unhandled exception, you are catching it to send a response to the client, but you are not setting an appropriate ResponseCode, like 500. So the default 200 will likely be sent instead. Is that what you really want? By default, TIdHTTPServer already sends a 500 response to the client for unhandled exceptions, so this code is largely unnecessary. But since you are customizing the ContextText, then at the very least I would suggest getting rid of the outer try/except altogether and move this code into the OnCommandError event instead.
-
What is the actual project name? Sounds like UAC's "Installer Detection" feature kicking in. You absolutely should have a UAC manifest in the project to avoid that, not relying on the persistence of the "Run as Administrator" option on the EXE properties.
-
Why compiler allows this difference in declaration?
Remy Lebeau replied to Mike Torrettinni's topic in RTL and Delphi Object Pascal
No, it is not a flaw. This is how Delphi (probably even Pascal in general) has always worked. A parameter's default value is required only in the function's declaration. At any site where the function is called, the compiler needs to know what the default value is, if the calling code decides to omit the parameter. Duplicating the default value in the function's implementation is optional. The implementation doesn't care whether the caller omitted parameters or not, because the compiler will have filled in any missing values on the call stack before the implementation is entered. -
TCustomInifile.ReadSubSections in older Delphi versions
Remy Lebeau replied to dummzeuch's topic in RTL and Delphi Object Pascal
Actually, maybe you could try something like this: type ICanReadSubSections = interface ['{...}'] procedure ReadSubSections(const Section: string; Strings: TStrings; Recurse: Boolean = False); end; TdzMemIniFile = class(TMemIniFile, ICanReadSubSections) public {$IF CompilerVersion < 14} procedure ReadSubSections(const Section: string; Strings: TStrings; Recurse: Boolean = False); {$IFEND} end; TdzRegistryIniFile = class(TRegistryIniFile, ICanReadSubSections) public {$IF CompilerVersion < 14} procedure ReadSubSections(const Section: string; Strings: TStrings; Recurse: Boolean = False); {$IFEND} end; {$IF CompilerVersion < 14} procedure TdzMemIniFile.ReadSubSections(const Section: string; Strings: TStrings; Recurse: Boolean = False); begin // add to Strings as needed... end; procedure TdzRegistryIniFile.ReadSubSections(const Section: string; Strings: TStrings; Recurse: Boolean = False); begin // add to Strings as needed... end; {$IFEND} And then you can do this: procedure doSomething(_Ini: TCustomIniFile); var Intf: ICanReadSubSections; sl: TStringList; begin if not Supports(_Ini, ICanReadSubSections, Intf) then raise Exception.Create('ICanReadSubSections not implemented'); sl := TStringList.Create; try Intf.ReadSubSections('Section', sl); for i := 0 to sl.Count - 1 do begin doSomethingWith(sl[i]); end; finally sl.Free; end; end; -
TCustomInifile.ReadSubSections in older Delphi versions
Remy Lebeau replied to dummzeuch's topic in RTL and Delphi Object Pascal
I really don't think you are going to be able to avoid that if you want to support different versions with varying implementations, eg: procedure doSomething(_Ini: TCustomIniFile); var sl: TStringList; {$IF CompilerVersion >= 14} procedure ReadSubSections(const Section: string; Strings: TStrings; Recurse: Boolean = False); begin _Ini.ReadSubSections(Section, Strings, Recurse); end; {$ELSE} procedure ReadMemSubSections(_MIni: TMemIniFile; const Section: string; Strings: TStrings; Recurse: Boolean = False); begin // add to Strings as needed... end; procedure ReadRegistrySubSections(_RIni: TRegistryIniFile; const Section: string; Strings: TStrings; Recurse: Boolean = False); begin // add to Strings as needed... end; procedure ReadSubSections(const Section: string; Strings: TStrings; Recurse: Boolean = False); begin if (_Ini is TMemIniFile) then ReadMemSubSections(TMemIniFile(_Ini), Section, Strings, Recurse) else if (_Ini is TRegistryIniFile) then ReadRegistrySubSections(TRegistryIniFile(_Ini), Section, Strings, Recurse) else raise Exception.Create('Only TMemIniFile or TRegistryIniFile are supported'); end; {$IFEND} begin sl := TStringList.Create; try ReadSubSections('Section', sl); for i := 0 to sl.Count - 1 do begin doSomethingWith(sl[i]); end; finally sl.Free; end; end; -
No StringHelper for saving to file?
Remy Lebeau replied to PeterPanettone's topic in RTL and Delphi Object Pascal
A "collection of strings" and a single "basic string type" are two completely different concepts. -
No StringHelper for saving to file?
Remy Lebeau replied to PeterPanettone's topic in RTL and Delphi Object Pascal
No, it is a collection of strings. It is not itself a string. Period. A basic string type would be just a sequence of characters, maybe a length, encoding, etc but certainly not more than that. Because it simply doesn't make sense for something like that to exist, since 99% of people won't be using it. It is a waste of coding, when the same task is easily accomplished using other readily-available code. So, I repeat my earlier question again. -
Enums always start at 0 implicitly, unless explicitly stated otherwise. Nothing wrong with using an enum for array indexes, provided the enum elements start at 0 and are sequential, which is the default behavior. type myEnum = (meFirst, meSecond, meThird); myUnitExternalButCompiledArray[meFirst] myUnitExternalButCompiledArray[meSecond] myUnitExternalButCompiledArray[meThird] var e: myEnum; e := ...; myUnitExternalButCompiledArray[Ord(e)]
-
That does not produce the requested effect. That is used for creating multi-column menus, not multi-group menus. This is the correct solution. Menus simply have no concept of groups, so "group headers" is accomplished by custom-drawing those menu items differently.
-
upcoming language enhancements?
Remy Lebeau replied to David Schwartz's topic in RTL and Delphi Object Pascal
Please, please, please, with sugar and cherries on top!! I remember way back in the day when Borland actually did release things "when they are ready". I miss those days. -
No StringHelper for saving to file?
Remy Lebeau replied to PeterPanettone's topic in RTL and Delphi Object Pascal
Why should it? Do you know ANY framework where a basic string type has such a method? -
upcoming language enhancements?
Remy Lebeau replied to David Schwartz's topic in RTL and Delphi Object Pascal
Of course they do. But nothing that can be talked about publicly at this time. -
upcoming language enhancements?
Remy Lebeau replied to David Schwartz's topic in RTL and Delphi Object Pascal
Which enhancements are you referring to exactly? The only new language enhancements added recently were Managed Records in 10.4, and Inline Variables and Type Inference in 10.3. If there are any other new language enhancements pending on the horizon, they are not public knowledge. If you want to know if there is anything new coming up, then you should join the Delphi 11 beta. IIRC, Embarcadero has already gone on record stating Nullable Types are not being added to the language anytime soon. But, they can be implemented manually using managed records now (well, more efficiently then the original approach from years past, anyway). -
OK. Makes sense, if Twilio sends a request to ProjectOne, which sends a request to your server, which fails, then ProjectOne would send an error back to Twilio. Are you sure it is Twilio and not ProjectOne? In any case, a connection reset during a TLS handshake usually means the server did not like something in the client's (Twilio/ProjectOne) handshake data, so it simply chose to abort the connection rather than send a TLS alert back to the client explaining the reason. Hang/deadlock rather than timeout, but yes, if you are not careful with it.
-
Which specific release of 10.6.2 exactly? Are you using a stock version that shipped pre-installed in a specific IDE version? Or have you updated to the latest trunk version from Indy's GitHub repo? Indy's versioning has been broken since Indy switched from SVN to GitHub, so 10.6.2.0 does not accurately reflect the real version. That should be OK. Any specific details about the errors? OK. Any synchronization between threads being done?
-
That has nothing to do with the ports you are using, unless you have a firewall that is blocking them. Which version of Indy and OpenSSL DLLs are you using? Yes. Though, you don't need to set the DefaultPort at all if you are just going to set each Binding.Port explicitly. If you are going to bind to multiple ports, then the DefaultPort should be 0. Just make sure you are handling the OnQuerySSLPort event correctly, especially for non-standard ports, like 8080. Also, make sure you are using an up-to-date version of Indy, since the behavior of default HTTPS handling did change a few years ago. No, that is perfectly fine, even expected and intentionally designed for. You SHOULD be throwing exceptions on unexpected failures. The server will catch and process uncaught exceptions internally. For socket I/O errors, it will close the socket and stop the socket's owning thread, triggering OnDisconnect and OnException events. For most other exceptions, it will instead trigger the OnCommandError event, then send an error response to the client if a response has not already been sent, and then carry on with normal HTTP KeepAlive handling - closing the socket or waiting for the next HTTP request, as needed. If an exception is raised and not caught, the server will catch it, and in most cases will overwrite those values with its own error values. There is no way to answer that with the limited information you have provided. Do you have KeepAlive enabled or disabled on the server? Once the server stops responding, are you getting OnConnect events for new connections, and getting OnHeadersAvailable events for new requests, at least? What do your OnCommand... events actually look like? Have you verified with a packet sniffer like Wireshark that your server machine is still receiving new HTTP requests from the network?
-
Should be easy to replicate that in a custom-drawn TDrawGrid/TStringGrid with its grid lines turned off, and an OnDrawCell event handler to draw the rounded blobs and text inside each cell.
-
There isn't one, at least not natively. You would have to write your own wrappers, either to wrap a myStream inside of an IStream and then call myIStream.CopyTo(), or else wrap myIStream inside of a System.IO.Stream and then call myStream.CopyTo(). A simpler way is to just call myIStream.Read() and myStream.Write() in a loop until there is nothing left to read from myIStream. No wrappers needed, just a local byte[] array.
-
Can I use the TidHTTP component for this?
Remy Lebeau replied to alank2's topic in Network, Cloud and Web
Well.... ultimately, the goal is to actually get Indy out of the IDE installation and into GetIt. It would still be available if you needed it, but wouldn't be forcibly installed and wasting space if you didn't need it. But, that dream is likely not to be realized anytime in the near future... Yes. And it doesn't rely on OpenSSL DLLs to do it, either. It uses OS-native APIs to handle that. -
Can I use the TidHTTP component for this?
Remy Lebeau replied to alank2's topic in Network, Cloud and Web
You could use TIdHTTP, but you would merely be substituting one library dependency for another. Indy is not a "built-in" library, it is a 3rd party library that just happens to be pre-installed in the IDE by default. A true native built-in solution would be to use Embarcadero's own THTTPClient/TNetHTTPClient instead, see Using an HTTP Client and What is the difference between THTTPClient and TNetHTTPClient?. Technically yes, for example: #include <IdAuthenticationNTLM.hpp> ... //set properties IdHTTP->ConnectTimeout = ...; IdHTTP->ReadTimeout = ...; IdHTTP->IOHandler = IdSSLIOHandlerSocketOpenSSL; IdHTTP->Request->ContentType = L"application/json"; IdHTTP->Request->Username = parms->Username; IdHTTP->Request->Password = parms->Password; try { IdHTTP->Post(parms->URL, parms->senddata, parms->recvdata); } catch (const Exception &exception) { ExceptionErrorMessage(const_cast<Exception*>(&exception), NULL, parms->exmsg, 1024); delete IdHTTP; goto fail2; }