-
Content Count
3056 -
Joined
-
Last visited
-
Days Won
139
Everything posted by Remy Lebeau
-
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.
-
I have checked in a new branch with some changes for TIdHTTPProxyServer: https://github.com/IndySockets/Indy/tree/http-proxy-keepalive Let me know if it works OK, and then I'll merge it in to the main code. I also opened a ticket in Indy's repo: https://github.com/IndySockets/Indy/issues/425
-
No, there is not. Handling CONNECT is very different than handling other HTTP verbs. A CONNECT request just contains the target host/port to connect to, and then all data between client and target server is tunneled back and forth as-is. But any other HTTP verb is a full HTTP request sent to the proxy itself, containing the full target URL, so the proxy will have to parse and forward the request as needed. And HTTP proxies are allowed to modify a request and/or response as it wants (unless the client asks the proxy not to do that, but TIdHTTPProxyServer does not implement that feature at this time). If you want a straight passthrough, look at TIdMappedPortTCP instead. But to make that work as an HTTP proxy, you will have to parse the HTTP protocol manually in its OnConnect, OnExecute, and OnOutboundData events. That is the default behavior for HTTP 0.9 and HTTP 1.0, but in HTTP 1.1 onward the default behavior is to keep the connection open instead, and the client has to explicitly ask the remote server to close the connection after sending a response. Looking at TIdHTTPProxyServer again, I see it uses HTTP 1.0 when communicating with the remote server, so it closes the remote connection after each response, and it will also close the connection with the requesting client after each response too. I'll have to update TIdHTTPProxyServer to support keep-alives. A CONNECT is a straight passthrough of raw data back and forth, the proxy does not process the data in any way. That is not the case when handling other HTTP verbs. That is incorrect. If the response has a 'Connection: keep-alive' (HTTP 0.9/1.0) header, or does not have a 'Connection: close' (HTTP 1.1+) header, the HTTP client is required by the HTTP specs to parse the response to discover when it actually ends, specifically because a disconnect WON'T occur at the end in that situation. Makes sense, as TIdHTTPProxyServer does not support keep-alives at this time. I'll have to update it to handle the 'Connection' and 'Proxy-Connection' headers better. It shouldn't even be getting that far, since the disconnect should happen before TIdHTTPProxyServer attempts to read the next request from the client. Yes, because TIdCommandHandler.Disconnect is set to True in TIdHTTPProxyServer.InitializeCommandHandlers() for all proxy requests. Good to know, thanks. Currently, it does, because TIdHTTPProxyServer does not implement proper handling of HTTP keep-alives at this time. Though, it doesn't really seem to be handling non-keepalives properly, either. It should be passing around 'Connection: close' and 'Proxy-Connection: close' headers, at least until keep-alives can be implemented. Not that I can think of. The proxy uses HTTP 1.0 to communicate with the remote server, so it already disconnects from the server after each response. As long as the client wants to stay connected to the proxy, it could send subsequent requests to the proxy, and each one will be forwarded individually. Though, I would not suggest setting Disconnect=False in TIdHTTPProxyServer.InitializeCommandHandlers(). I would leave it as True as a fallback, and then set TIdCommand.Disconnect=False inside of TIdHTTPProxyServer.CommandPassThrough() after reading the client's request headers, so that the keep-alive can be handled on a per-request basis. I'll update TIdHTTPProxyServer to handle that. I know what CONNECT is. And what you describe is how TIdHTTPProxyServer handles CONNECT right now. But an HTTP client won't use CONNECT for HTTP verbs like HEAD/GET/POST unless they are used over HTTPS, since CONNECT is required to let the client negotiate SSL/TLS security directly with the remote server through an HTTP proxy. Otherwise, the client would be negotiating SSL/TLS with the proxy instead, which would then subject the requests to MITM attacks.
-
Playing an MP4 animation / unsupported media file / cppbuilder
Remy Lebeau replied to alank2's topic in FMX
Related: Getting exception HRESULT: 0x80040265 with MP4 Files -
It would be nice if the "Call started" and "Call completed" messages logged WHAT operation(s) were being started/completed. And also, if every Call start/sent/reading message included the PeerPort that is processing the Call, to better differentiate between Calls that overlap in parallel. The Call numbers alone are making it confusing which client is performing which action when Calls overlap. Logging the PeerPort instead on everything should remove that ambiguity. Is it possible/expected that any given "Call" in your client code produces multiple HTTP requests at a time? Or, is it expected to be only 1 HTTP request per "Call"? Connections to the target server are not re-used. There is 1 new connection per request forwarded. The connection with a client stays open as log as the client wants. There can be multiple requests per connection to the proxy. And your client is requesting keep-alives with the proxy, though most connections are sending only 1 request per connection. But Call 6 appears to share a TCP connection with an unknown Call number, and that 2nd call is difficult to determine what is actually happening on it. It might have hung, or not, I can't tell yet. I can't comment on that, since I don't see that in your code. Possibly, if the client uses HTTP keep-alives to reuse a connection for multiple HTTP requests. As it does appear really happened for at least 2 Calls. It is certainly possible that TIdHTTPProxyServer may not be handling that situation correctly, since it is not a fully functional HTTP server. I might have to force "Connection: close" on every response until that can be verified/addressed. Synchronize what, exactly? It should not be. Provided you are not blocking the exception then all Indy servers should be handling it to close the socket and stop the thread. No. I have not looked at the 2nd log yet, I will do so shortly. The 1st log was not easy to follow. But this does go back to my comment above that there possibly could be an issue with TIdHTTPProxyServer not correctly handling multiple requests on the same TCP connection through HTTP keep-alives. That only applies to the listening Binding sockets, to reuse ports that were previously closed and then reopened in a short amount of time.
-
Conditional compiling - Delphi has a bug
Remy Lebeau replied to KodeZwerg's topic in RTL and Delphi Object Pascal
This seems to be the opposite of documented behavior in Delphi: https://docwiki.embarcadero.com/RADStudio/en/IF_directive_(Delphi) So, {$IF FPC_VERSION >= 3} SHOULD just evaluate to False, and if it is not then this is likely a regression.- 5 replies
-
- delphi
- conditional
-
(and 1 more)
Tagged with:
-
Playing an MP4 animation / unsupported media file / cppbuilder
Remy Lebeau replied to alank2's topic in FMX
Did you make sure they were successful? In any case, I'm not sure this IS the root cause, only that it is ONE POSSIBLE cause. Like I mentioned, there are other reasons for that error to occur. I suggest you enable Debug DCUs and step into the FMX source code with the debugger to see what is REALLY happening inside the TMediaPlayer. Not that I'm aware of. MP4 is one of the supported file extensions on Windows. But that does not rule out the possibility that the MP4 file itself is bad in some way. -
Playing an MP4 animation / unsupported media file / cppbuilder
Remy Lebeau replied to alank2's topic in FMX
Make sure CoInitialize() or CoInitializeEx() is being called at program startup.