Jump to content

Remy Lebeau

Members
  • Content Count

    3057
  • Joined

  • Last visited

  • Days Won

    139

Everything posted by Remy Lebeau

  1. I'm not sure this can be solved truly lock-free. Even if you make the low-part of the counter thread-local to avoid a lock, you still have the high-part of the counter to deal with to track rollovers, and that will have to be protected from concurrent access, either with a lock, or atomic access, etc. I really don't know if that will work or not. I think the entire counter needs to be global, so that new threads will start with the latest accumulated value properly. I'm thinking something like the following, based on ideas I've seen in other implementations: var TickCount64: UInt64 = 0; function GetTickCount64Emu: UInt64; var OrigValue, NewValue, TmpValue: UInt64; CurrTicks: UInt32; begin OrigValue := TInterlocked.Read(TickCount64); repeat CurrTicks := Windows.GetTickCount; NewValue := (OldValue and $FFFFFFFF00000000) + CurrTicks; if CurrTicks < UInt32(OrigValue and $FFFFFFFF) then Inc(NewValue, $100000000); TmpValue := TInterlocked.CompareExchange(TickCount64, NewValue, OrigValue); if OrigValue = TmpValue then Break; if TmpValue > NewValue then Exit(TmpValue); OrigValue := TmpValue; until False; Result := NewValue; end; Yes, that would be required no matter what. Oh yeah, I forgot about that. Well, then just call GetProcAddress() manually at startup (or first call) instead of using 'delayed'. This is what Indy does.
  2. Remy Lebeau

    Will getit work for C++ Builder

    Most likely, either Int8 or CSIDL_DESKTOP has already been #define'd to a value earlier, and that define is then interfering with these declarations.
  3. What about (in a loop) using TInterlocked to Read() the counter into a local variable, update the two halves of that variable as needed, and then CompareExchange() the result back into the counter until it takes effect for multi-threaded access? Looking at some other apps (Mozilla, etc), that is how they solved this problem years ago. How? Without using thread-local variables? Would still need a locking mechanism to avoid races across threads. Why?
  4. Here is an implementation of GetTickCount64() for Win2K and XP, it would be trivial to use the native GetTickCount64() on Vista+ and fallback to this version on pre-Vista systems: https://titanwolf.org/Network/Articles/Article?AID=7398e4d3-ddc2-42b0-ae21-d52654fe9287#gsc.tab=0
  5. Remy Lebeau

    FMX : TDialogService.MessageDialog wrong parameter ?

    Look at the actual declaration more carefully: class procedure MessageDialog( const AMessage: string; const ADialogType: TMsgDlgType; const AButtons: TMsgDlgButtons; const ADefaultButton: TMsgDlgBtn; const AHelpCtx: THelpContext; const ACloseDialogProc: TInputCloseDialogProc); The 3rd parameter is a TMsgDlgButtons, which is a Set of TMsgDlgBtn. But you were originally trying to pass a single TMsgDlgBtn. Also, the 5th parameter is a THelpContext, which is an integer, not a pointer. So, you can't pass nil to it. Try this: TDialogService.MessageDialog( 'You want to ........ ', TMsgDlgType.mtConfirmation, mbYesNo, // or [TMsgDlgBtn.mbYes, TMsgDlgBtn.mbNo] TMsgDlgBtn.mbNo, 0, procedure(const aResult: TModalResult) begin ... end );
  6. Remy Lebeau

    WinInet API 4095 How can I remove this limit?

    I can't answer that. I see nothing in your code that would limit the size of the download. But, I can address other problems I see with your code. For one thing, you are only downloading 11 bytes at a time, why so small a buffer? That is pretty inefficient for a network transfer. Also, when you are trying to decode the buffer, you are converting from AnsiChar[] to String, and then copying BufferLen characters. The conversion to String is wrong, as you don't take the BufferLen into account during that conversion. And there is no guarantee that the number of bytes in the buffer is the same as the number of characters in the String. You shouldn't even be using Copy() in this manner at all - use TEncoding.UTF.GetString() instead, which lets you pass in a byte array, starting index, and byte count. But most importantly, you simply can't UTF8-decode arbitrary byte buffers while you are downloading them. You should download all of the bytes first, then decode them as a whole. Otherwise, if you really want to decode on the fly, you must take codepoint boundaries into account properly, decoding only complete byte sequences, and saving incomplete sequences for future iterations to complete and decode. Otherwise you risk corrupting the decoded data. And all of this assumes the data is even encoded in UTF-8 to begin with, which your code is not checking for by looking at the HTTP Content-Type response header or the body content (in case of a <meta> charset tag in HTML, etc).
  7. Remy Lebeau

    How to connect to office 365 using proxy server

    Without seeing a trace log of what's going on over the wire, I honestly could not tell you what is going on. Can you get a Wireshark capture of the connect attempt?
  8. What is the actual value of MMFName on both sides? What error does OpenExisting() report when it fails? Is that call actually succeeding or failing? Also, why are you hard-coding $FFFFFFFF instead of using the INVALID_HANDLE_VALUE constant, like the documentation says to use? Are you compiling for 32bit or 64bit? For 64bit, $FFFFFFFF extended to $00000000FFFFFFFF would be the wrong value to use, it would need to be $FFFFFFFFFFFFFFFF instead. I don't think that is going to make a difference, since MemoryMappedFile.OpenExisting() just calls the Win32 OpenFileMapping() API (you can see the source code for yourself at https://referencesource.microsoft.com/#system.core/System/IO/MemoryMappedFiles/MemoryMappedFile.cs) Not enough information to diagnose that..
  9. Remy Lebeau

    TPopupMenu with group headers

    No, it was introduced in XE2.
  10. Remy Lebeau

    Indy HTTP server

    I can't answer that. There simply has not been enough information provided yet to diagnose the root cause. You are just going to have to continue debugging deeper.
  11. Remy Lebeau

    Indy HTTP server

    Did you do what Twilio said to do? Did you verify the TLS version and ciphers being used by your server during a failed connection? Have you tried sniffing the TLS handshake with Wireshark to see what is actually going on inside of it? The only time I have ever seen that happen is when OpenSSL decides to reject a handshake and just closes the TCP connection abruptly without first sending a TLS alert to explain why it is being rejected. That usually implies that something in the handshake is corrupted or incorrect, and OpenSSL is just being cautious about sending anything, but that is not always the case. I doubt it, since this is not really an Indy issue, it is more of an OpenSSL TLS issue, but whether it is on the client side or the server side is still TBD.
  12. Remy Lebeau

    Get Parent process Current directory

    https://devblogs.microsoft.com/oldnewthing/20091125-00/?p=15923 https://devblogs.microsoft.com/oldnewthing/20090223-00/?p=19063 https://stackoverflow.com/a/14018468/65863
  13. Remy Lebeau

    Open PDF File

    Technically, when double-clicking on a file, Explorer will ask the Shell to invoke the file's default action, and the Shell will execute the "open" action if that is the default action. Sure, this may be the case in 99% of the cases, but there is always that 1% that will be different. Double-clicking on a file, and calling ShellExecute/Ex("open") on the file, are NOT the same thing, even if they usually end up performing the same action on most systems, But they are still logically different operations.
  14. Remy Lebeau

    Open PDF File

    Technically, the OP asked how to execute a specific command, so shouldn't be using ShellExecute/Ex() at all. Double-clicking a file in Explorer executes the default action. And 'open' is not always the default action. It depends on the file extension's actual registration in the Registry, whether the user or 3rd-party apps have customized it, etc (for instance, on my system, the default action of a PDF file is to pass the file to AVG for scanning before then opening it). Which is incorrect, because that is not what Explorer does. Maybe on YOUR system, perhaps. Not 100% guaranteed on every other system.
  15. Remy Lebeau

    Open PDF File

    The correct way to invoke the default action is to set the verb parameter to nil, not 'open'.
  16. 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.
  17. Remy Lebeau

    Indy HTTP server

    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.
  18. Remy Lebeau

    Handlin the message and returning value to the caller

    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.
  19. Remy Lebeau

    Indy HTTP server

    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.
  20. Remy Lebeau

    Delphi demands elevation?

    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.
  21. Remy Lebeau

    Indy HTTP server

    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.
  22. Remy Lebeau

    Delphi demands elevation?

    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.
  23. 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.
  24. 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;
  25. 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;
×