-
Content Count
2684 -
Joined
-
Last visited
-
Days Won
113
Everything posted by Remy Lebeau
-
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. -
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.
-
Conditional Define for Packages
Remy Lebeau replied to chkaufmann's topic in RTL and Delphi Object Pascal
No, there is not. You would have to create your own conditional in the Package's project options. Yes. -
When using an Interposer, this is the way I would have do it, too.
-
I have merged it now.
-
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;
-
TRichEdit in Delphi 11.1 ("Courier New" font broken with formatting characters))
Remy Lebeau replied to Denis Dresse's topic in VCL
Can you show the actual code that is populating the TRichEdit and the formatting of its lines?- 17 replies
-
- trichedit
- delphi 11.1
-
(and 1 more)
Tagged with:
-
Sending Email via GMail Using OAuth 2.0 via Indy
Remy Lebeau replied to Ugochukwu Mmaduekwe's topic in 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). -
Exception call stacks on Windows with only a few LOCs
Remy Lebeau replied to Fr0sT.Brutal's topic in I made this
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. -
Strange behavior with "is" operator
Remy Lebeau replied to Sonjli's topic in Algorithms, Data Structures and Class Design
There is no functional difference between those two. -
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;
-
Strange behavior with "is" operator
Remy Lebeau replied to Sonjli's topic in Algorithms, Data Structures and Class Design
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. -
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;
-
What is the best way to convert between UnicodeString and UTF-8
Remy Lebeau replied to alank2's topic in General Help
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. -
What is the best way to convert between UnicodeString and UTF-8
Remy Lebeau replied to alank2's topic in General Help
UTF8String exists in C++Builder too, and does the same implicit conversion to UTF-8 when assigned other string types. -
how to flush buffers on Indy components
Remy Lebeau replied to al17nichols's topic in Network, Cloud and Web
I really wanted to see your actual raw messages, not your code. But whatever. From the code you have shown, it appears that your messages are formatted as XML. In which case, you can recover from a non-socket error by reading bytes from the IOHandler until the next opening XML element is encountered. For instance, you can call IOHandler.WaitFor('<', False, False) to ignore all bytes until a '<' character becomes available at the front of the InputBuffer, and then call IOHandler.CheckForDataOnSource()+IOHandler.InputBuffer.Peek() to see if subsequent byte(s) contain a valid message name. If not, call IOHandler.Discard() to ignore the unwanted byte(s) and repeat the WaitFor()+Peek() until a valid message is detected. Eventually, you should encounter the beginning of a valid message in the InputBuffer and can then resume your normal reading. -
C++ Builder Indy error:1409442E:SSL routines:ssl3_read_bytes:tlsv1 alert protocol version
Remy Lebeau replied to BernardR's topic in Indy
There is no way to diagnose this without seeing your actual code AND a log of the raw TLS handshakes that are producing the "protocol version" alerts. Is it possible that the sites in question are requiring TLS 1.3? Are other web browsers able to connect without error? Have you tried comparing their TLS handshakes to the ones Indy produces? Have you tried the WIP SSLIOHandler for OpenSSL 1.1x/3.0 yet, and does it produce the same errors?- 1 reply
-
- c++ builder
- indy
-
(and 1 more)
Tagged with:
-
how to flush buffers on Indy components
Remy Lebeau replied to al17nichols's topic in Network, Cloud and Web
I have asked you multiple times now to please provide an example of your actual communication in action, but you have not done so yet. I CANNOT help you in implementing recovery logic without seeing a real example. For all I know, your protocol simply CAN'T recover the way you want. That is a likely possibility, many common protocols can't recover without disconnecting the socket and reconnecting. If you do not provide a real example of a file transfer with surrounding messaging, then there is nothing I can do. -
Using a .bpl in an app, you should NOT have to use the .pas/.dcu files directly in the app. If your app is still building/requiring the .pas/.dcu files, then it is not correctly referencing the component unit(s) from the .bpl.
-
how to flush buffers on Indy components
Remy Lebeau replied to al17nichols's topic in Network, Cloud and Web
Without knowing more about your actual protocol, I can't answer that. Is the transfer being done on the same connection that initiates the transfer (ala HTTP)? Or, is it on a separate connection (ala FTP)? How does the receiver know what the FileSize is? Does the receiver receive anything at the end of the transfer that it could look for if the transfer fails with a non-socket error? I ask this, because if the transfer fails with a non-socket error, you will only know how many bytes were written to the XferStream, but not necessarily how many bytes were actually consumed from the socket itself. So, the only way to recover without disconnecting/reconnecting is if you have a definitive way to identify where a new message begins after a transfer is finished. -
You are right, my bad. Didn't realize there was another person on this thread.