Jump to content

Remy Lebeau

Members
  • Content Count

    2335
  • Joined

  • Last visited

  • Days Won

    94

Everything posted by Remy Lebeau

  1. Remy Lebeau

    TTimer limit..

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

    TTimer limit..

    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?
  3. Remy Lebeau

    How to force update to label during a loop

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

    sorry for the newbe question...

    Then what does this have to do with Delphi/FMX?
  5. Remy Lebeau

    How to force update to label during a loop

    Did you try Repaint()'ing the Form itself, rather than just the TLabel?
  6. Remy Lebeau

    How to force update to label during a loop

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

    Getting of dynamic array from Variant

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

    Getting of dynamic array from Variant

    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;
  10. Remy Lebeau

    No protocols available

    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).
  11. Remy Lebeau

    SMS Sending - Still valid??

    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;
  12. 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.
  13. Remy Lebeau

    SMS Sending - Still valid??

    Does the server require TLS 1.1+, by chance? By default, TIdSSLIOHandlerSocketOpenSSL enables only TLS 1.0 (issue #181). Have you tried enabling TLS 1.1 and/or TLS 1.2 in the TIdSSLIOHandlerSocketOpenSSL.SSLOptions.SSLVersions property? Define "latest". TIdSSLIOHandlerSocketOpenSSL requires OpenSSL 1.0.2 or earlier, it does not support OpenSSL 1.1+. For that, you need to use this SSLIOHandler instead.
  14. Remy Lebeau

    Conditional Define for Packages

    No, there is not. You would have to create your own conditional in the Package's project options. Yes.
  15. Remy Lebeau

    How to synchronize splitters?

    When using an Interposer, this is the way I would have do it, too.
  16. Remy Lebeau

    TIdHTTPProxyServer hangs when used with RemObjects

    I have merged it now.
  17. Remy Lebeau

    How to synchronize splitters?

    Simply move the logic into another procedure that you can pass the Sender into, eg: procedure TForm1.MoveOtherSplitterImpl(Sender: TSplitter; var aMsg: TMessage); begin if (fMovingControl = nil) or (fMovingControl = Sender) then case aMsg.Msg of WM_MOUSEFIRST..WM_MOUSELAST: begin fMovingControl := Sender; try if Sender = Splitter1 then Splitter2.Perform(aMsg.Msg, aMsg.WParam, aMsg.LParam) else if Sender = Splitter2 then Splitter1.Perform(aMsg.Msg, aMsg.WParam, aMsg.LParam); finally fMovingControl := nil; end; end; end; end; end; procedure TForm1.MoveOtherSplitter(var aMsg: TMessage); begin MoveOtherSplitterImpl(Splitter1, aMsg); fOriginalWindowProc(aMsg); end; procedure TForm1.MoveOtherSplitter2(var aMsg: TMessage); begin MoveOtherSplitterImpl(Splitter2, aMsg); fOriginalWindowProc2(aMsg); end; If you really want something more generic, then you should link the two Splitters together, such as by their Tag properties, eg: procedure TForm1.FormCreate(Sender: TObject); begin Splitter1.Tag := NativeInt(Splitter2); fOriginalWindowProc := Splitter1.WindowProc; Splitter1.WindowProc := MoveOtherSplitter; Splitter2.Tag := NativeInt(Splitter1); fOriginalWindowProc2 := Splitter2.WindowProc; Splitter2.WindowProc := MoveOtherSplitter2; end; procedure TForm1.MoveOtherSplitterImpl(Sender: TSplitter; var aMsg: TMessage); begin if (fMovingControl = nil) or (fMovingControl = Sender) then case aMsg.Msg of WM_MOUSEFIRST..WM_MOUSELAST: begin fMovingControl := Sender; try TSplitter(Sender.Tag).Perform(aMsg.Msg, aMsg.WParam, aMsg.LParam); finally fMovingControl := nil; end; end; end; end; end; procedure TForm1.MoveOtherSplitter(var aMsg: TMessage); begin MoveOtherSplitterImpl(Splitter1, aMsg); fOriginalWindowProc(aMsg); end; procedure TForm1.MoveOtherSplitter2(var aMsg: TMessage); begin MoveOtherSplitterImpl(Splitter2, aMsg); fOriginalWindowProc2(aMsg); end;
  18. Can you show the actual code that is populating the TRichEdit and the formatting of its lines?
  19. Remy Lebeau

    Sending Email via GMail Using OAuth 2.0 via Indy

    That branch is still a work in progress, there has been some hiccups in it this week related to Microsoft's use of OAuth2 (see the discussion on https://github.com/IndySockets/Indy/issues/192).
  20. Remy Lebeau

    Exception call stacks on Windows with only a few LOCs

    It is a nice start. But from experience, having just the raw stack addresses is still a PITA to debug. I took a similar approach in one of my projects (not using the build-in Exception.StackTrace callbacks, though), but I also output the module name that each address belongs to, and for a DLL module I also worked out the exported function name+offset based on the address. I was planning on adding logic to resolve non-DLL function names using the generated MAP file, but I didn't get that far before the project was discontinued.
  21. There is no functional difference between those two.
  22. Remy Lebeau

    Disable Event

    Another option is to leave the event handler assigned and use a separate variable instead, eg: private IgnoreExit: Boolean; ... procedure TMyForm.DoSomething; begin IgnoreExit := True; try ... finally IgnoreExit := False; end; end; ... procedure TMyForm.Edit1Exit(Sender: TObject); begin if IgnoreExit then Exit; ... end; I like using the component's Tag property for this task, if it is not being used for something else, eg: procedure TMyForm.DoSomething; begin Edit1.Tag := 1; try ... finally Edit1.Tag := 0; end; end; ... procedure TMyForm.Edit1Exit(Sender: TObject); begin if Edit1.Tag <> 0 then Exit; ... end;
  23. This happens when the module that declares MyAttributeOne is different than the module that is using MyAttributeOne, and the two modules have been compiled separately to have different RTTIs for the same type. In this case, you would need to move MyAttributeOne into a separate Package that both modules can share with Runtime Packages enabled, so that only 1 RTTI exists for MyAttributeOne between the modules.
  24. Remy Lebeau

    Newbie question about data types/structures

    In C/C++, the '->' operator is primarily just a combination of the '*' dereference and '.' member-access operators (but can be overloaded in C++ for other purposes), eg the above can be written as follows instead and the functionality would be exactly the same: p = (*p).next; In Delphi, those same operations are expressed individually, there is no combination operator, eg: p := p^.next; However, in this case, the '^' is optional when accessing a member via a pointer, eg: p := p.next; There is no such thing as inline type declarations in Delphi, so you must use the traditional 'type' section to define the local record type, eg: procedure TElements.Button1Click(Sender: TObject); type AllElements = record Number: integer; // Atomic number Symbol: string; // Element symbol Name: string; // Element name AlphaSeq: integer; // Name alphabetical sequence number Mass: double; // Atomic number Oxidation: string; // Oxidation states end; begin ... end; Or, move the declaration outside of the procedure, eg: type AllElements = record Number: integer; // Atomic number Symbol: string; // Element symbol Name: string; // Element name AlphaSeq: integer; // Name alphabetical sequence number Mass: double; // Atomic number Oxidation: string; // Oxidation states end; procedure TElements.Button1Click(Sender: TObject); begin ... end; That being said, I also notice is that you are using inline const/variable declarations when you don't actually need to. You are not using inline declarations the way they were intended to be used, so you may as well just use the more traditional 'const' and 'var' sections instead, eg: procedure TElements.Button1Click(Sender: TObject); type ... const // No element has more than 10 oxidation states MAX_OXIDATION_STATES: integer = 10; TOTAL_ELEMENTS: integer = 118; var AtomicNumber1: integer; AtomicNumber2: integer; leastCommonMultiple: integer; ratio1: integer; ratio2: integer; Combinations: integer; valence1: integer; valence2: integer; v1: integer; v2: integer; errorMessage: string; begin AtomicNumber1 := 0; AtomicNumber2 := 0; ... end;
  25. It really depends on what you are using the strings for. If the strings are mostly for interacting with Embarcadero's RTL/VCL/FMX frameworks, then stick with UnicodeString/System::String, and convert to other string types only when needed. If the strings are mostly for interacting with external libraries, then use whatever type is most suitable for those libs, and convert to/from UnicodeString only when needed.
×