-
Content Count
2684 -
Joined
-
Last visited
-
Days Won
113
Everything posted by Remy Lebeau
-
Waiting for something without blocking the UI
Remy Lebeau replied to softtouch's topic in General Help
It seems like you are receiving the server's response asynchronously. In which case, I would suggest not waiting for the response at all. Send the request, and then move on. Let the asynchronous handler notify your code whenever the response actually arrives. You can always disable the UI in the meantime, if needed. Otherwise, if you must make the function act synchronously, even though the response is asynchronous, then at least consider waiting on a TEvent instead of a boolean, eg: function GetTCPIPC(const msg: string): string; begin if not clientclass.Connected then begin Result := ''; Exit; end; clientclass.MsgEvent.Reset; idTCPClient.IOHandler.WriteLn(msg); while clientclass.MsgEvent.WaitFor(10) <> wrSignaled do Application.ProcessMessages; Result := clientclass.msgfromserver; end; Otherwise, consider changing the function to read the response directly in the function itself, not asynchronously from elsewhere. That way, you can place a TIdAntiFreeze component on your Form to keep the UI responsive while the TCP socket is blocking the UI thread, eg: function GetTCPIPC(const msg: string): string; begin if not clientclass.Connected then begin Result := ''; Exit; end; IdTCPClient.IOHandler.WriteLn(msg); //read response here Result := msgfromserver; end; Though, you really should not be doing socket I/O in the UI thread at all. Consider a threaded approach, similar to what ioan showed earlier. -
IdUDPClient send packet to Broadcast address. how to avoid getting the data back
Remy Lebeau replied to ChrisChuah's topic in Indy
On some platforms, you might be able to just set the TIdUDPClient.BoundIP property to the IP of the network adapter you are sending broadcasts from. On other platforms, you might have to resort to using TIdUDPClient.Binding.SetSockOpt(SO_BINDTODEVICE) to bind to a network interface by name instead of IP. But either way, this should prevent your outgoing network adapter from receiving duplicates of its own broadcasts. Not quite sure I understand what you are asking. If you are asking how to ignore the IP of the broadcaster, then whenever you receive a packet, you are also given the sender's IP (and port). You can just ignore packets that come from your own IP. If you are asking what a broadcast IP is in general, then it is a network IP that is masked by its subnet mask, and then OR'ed with the inverse of the subnet mask. So, for example, if you have an IP of 192.168.100.5 and a subnet mask of 255.255.255.0, then the broadcast IP is 192.168.100.255: 192.168.100.5 & 255.255.255.0 = 192.168.100.0 ~255.255.255.0 = 0.0.0.255 192.168.100.0 | 0.0.0.255 = 192.168.100.255 -
How to use a particular network interface to send out UDP data
Remy Lebeau replied to ChrisChuah's topic in Indy
You can optionally set the TIdUDPClient.BoundIP property to the IP of the desired network adapter. If you don't bind the client, the OS will route the packet using the network adapter it thinks is most appropriate, based on its configured routing tables. -
Unfortunately, those version numbers predate Indy's switch from SVN to GIT, when build number versioning was lost. So I can't diff those versions to see what changed between them (unless you have dates on them, at least). But the behavior should have improved from older to newer, not degraded. It is technically possible (you would have to peek - not read - the first handful of bytes to detect whether a SSL/TLS header is present, and if so then set PassThrough=False. I do not recommend this approach. You should use separate ports, one port with SSL/TLS enabled, and one port without. It is just easier, safer, and more reliable that way. I posted an example in Indy's AToZed forum a few years ago (it was written for TIdHTTPServer, but you can adapt it for TIdTCPServer): https://www.atozed.com/forums/thread-367-post-834.html#pid834
-
You should double-check that, since you clearly do have an SSL/TLS handshake being performed. TIdServerIOHandlerSSLOpenSSL.Accept() should be creating a new TIdSSLIOHandlerSocketOpenSSL whose PassThrough is True, so the server can then decide when it is best to set it to False (ie, when the client is connected to an implicit SSL port, or after receiving a STARTTLS-style command, etc). Note that there was a bug where PassThrough was initialized as False in TIdSSLIOHandlerSocketBase, that was fixed a few years ago, I think that might have been after 10.6.2.5366. You might consider upgrading to the latest version from Indy's GitHub repo and see if the problem continues, just to make sure you have all of the latest fixes.
-
That is simply not possible the way you describe. The ONLY way you can get that error is during an SSL/TLS handshake, which is NOT performed when PassThrough is set to True (ie, pass-through raw data as-is) thus disabling SSL/TLS. So, you MUST be setting PassThrough to False (ie, intercept data for SSL/TLS processing) in order to get that error. Which version of Indy are you using? IIRC, there was a bug in old versions where a server would set PassThrough to false for all clients, causing an SSL/TLS handshake for non-SSL/TLS clients. But that was fixed a LONG time ago.
-
Yes. Define 2 separate ports in the server's Bindings collection, and then in the server's OnConnect event you can cast the AContext.Connection.IOHandler property to TIdSSLIOHandlerSocketBase and set its PassThrough property to True (SSL/TLS disabled) or False (SSL/TLS enabled) based on which port the client connected to, which you can get from the AContext.Binding.Port property.
-
FYI, this morning I checked in a new 'sasl-oauth' branch in Indy's repo, which includes a new 'IdSASLOAuth.pas' unit for SASL classes for OAUTH10A, OAUTHBEARER, and XOAUTH2 for TIdDICT, TIdPOP3, TIdSMTP, and TIdIMAP4. They are still a work in progress (ie, no parsing of response JSON yet), and you are responsible for obtaining the OAuth tokens externally (ie, over HTTP), but once you have the tokens then you can use these SASLs to login to the DICT/POP3/SMTP/IMAP servers.
-
Sending Email via GMail Using OAuth 2.0 via Indy
Remy Lebeau replied to Ugochukwu Mmaduekwe's topic in Indy
FYI, this morning I checked in a new 'sasl-oauth' branch in Indy's repo, which includes a new 'IdSASLOAuth.pas' unit for SASL classes for OAUTH10A, OAUTHBEARER, and XOAUTH2 for TIdDICT, TIdPOP3, TIdSMTP, and TIdIMAP4. They are still a work in progress (ie, no parsing of response JSON yet), and you are responsible for obtaining the OAuth tokens externally (ie, over HTTP), but once you have the tokens then you can use these SASLs to login to the DICT/POP3/SMTP/IMAP servers. -
In what way, exactly?
-
Very simple: pre-D2009: Char is AnsiChar String is AnsiString, using ANSI encoded AnsiChars UTF8String is just an alias for AnsiString, it is not guaranteed to be UTF-8 unless it comes from UTF8Encode() post-D2009: Char is WideChar String is UnicodeString, using UTF-16 encoded WideChars UTF8String is a native string type, using UTF-8 encoded AnsiChars In you example, the length will be 4 Chars (after adding the 'A'). How many bytes those Chars will take up depends on the byte encoding you tell the UDPClient to use. In ANSI/UTF-8, ASCII characters are 1 byte each. In UTF-16, they are 2 bytes each. Things get more complex when you start dealing with non-ASCII characters. First off, TIdUDPClient does have a Write() method, it has Send() and SendBuffer() methods instead. In any case, internally TIdUDPClient will convert the String to bytes, and then transmit the bytes. The default encoding used to produce those bytes is US-ASCII, but you can specify a different encoding in the optional AByteEncoding parameter of Send/Buffer(), or by setting the global GIdDefaultTextEncoding variable in the IdGlobal unit. Well, you have to do that anyway, since you are including binary data in your request, not just text. Explicitly tell TIdUDPClient (or any other Indy component) to transmit strings in the desired ANSI encoding. Don't just rely on defaults. It is because your server did not send the response to the correct port on the client. The client sent out the request from port 59114, so that was the port it could receive the response on, but the server sent the response to port 55510 instead. So, the server ignored the source port of the request and sent the response to its own port instead. If the server requires the client to use the same port that the server is using, you will have to set the TIdUDPClient.BoundPort to that port before sending requests (which you seem to have already figured out). Otherwise, fix the server to send the response to the request's actual source port. Indy does not behave differently in a worker thread vs in the main UI thread. It is not dependent on message handling. Your client is sending the request to a subnet broadcast IP (192.168.84.255) instead of to the server's actual IP (192.168.84.128) (why?). And since you are not setting the TIdUDPClient.BoundIP to bind the client to a specific network adapter, that explains why your client is receiving a duplicate copy of the broadcasted request. So, you will have to either: - send to the server's IP directly. - make your client ignore messages that have its own local IP as the source IP. - set the TIdUDPClient.BoundIP to the client's own local IP, then it won't receive broadcasts from itself.
-
How to Create dll with callback or event functions?
Remy Lebeau replied to ChrisChuah's topic in RTL and Delphi Object Pascal
Since there is a lot of talk of COM in this discussion, have a look at the COM tutorials on this website: http://www.techvanguards.com/com/, especially the section on Events and Callbacks. -
How to Create dll with callback or event functions?
Remy Lebeau replied to ChrisChuah's topic in RTL and Delphi Object Pascal
Yes, it can: Marshalling a Delegate as a Callback Method -
C++Builder has always included the ability to compile Delphi .pas files that are added to C++ projects. But, if you need to compile Delphi projects for use in C++, then you will need the full Studio.
-
Why are you doing this? This is the root of your crash. You don't own the Binding object, so you have no business destroying it like this. Let TIdUDPClient manage the Binding for you. It will be destroyed automatically when the TIdUDPClient itself is destroyed. The stack trace you have provided clearly shows TIdUDPClient's destructor is trying to access the Binding after you have manually destroyed it, hence the crash. If you really want to destroy the Binding manually, you need to instead set the TIdUDPClient.Active property to False, which will internally close and destroy the Binding if it is currently active: //FUDP.Binding.Destroy; FUDP.Active := False; No. That would just complicate your code further, since TIdUDPServer is asynchronous, whereas TIdUDPClient is synchronous. That being said, I see some other issues with your code... You should wrap the TIdUDPClient in a try/finally block to ensure that it gets Free()'d in case an exception is raised: FUDP := TIdUDPClient.Create(nil); try ... finally FUDP.Free; end; Same with the TMemoryStream. This is a buffer overflow, as l_buf has 5 bytes in it but you are asking Write() to read 6 bytes from it. l_buf is not null-terminated, so if you need it to be then you have to either add an explicit terminator to the string: l_buf := ToBytes('SOREQ'#0, IndyTextEncoding_ASCII); l_memStream.Write(l_buf[0], Length(l_buf)); Or, write the null-terminator separately: const NullTerm: AnsiChar = #0; ... l_buf := ToBytes('SOREQ', IndyTextEncoding_ASCII); l_memStream.Write(l_buf[0], Length(l_buf)); l_memStream.Write(NullTerm, 1); Alternatively, use Indy's own WriteStringToStream() function in the IdGlobal unit: WriteStringToStream(l_memStream, 'SOREQ'#0, IndyTextEncoding_ASCII); WriteStringToStream(l_memStream, 'SOREQ', IndyTextEncoding_ASCII); WriteStringToStream(l_memStream, #0, IndyTextEncoding_ASCII); In any case, you are converting the TMemoryStream data to a TIdBytes for transmission, so you might consider simply getting rid of the TMemoryStream altogether and use Indy's own TIdBytes-oriented functions instead (CopyTIdString(), CopyTIdUInt32(), CopyTIdByteArray(), etc), which are also in the IdGlobal unit: SetLength(l_buf, 6 + SizeOf(l_port) + SizeOf(l_ipBytes)); CopyTIdString('SOREQ'#0, l_buf, 0, -1, IndyTextEncoding_ASCII); l_port := GStack.HostToNetwork(l_port); CopyTIdUInt32(l_port, l_buf, 6); getArrayBytes(l_ip, l_ipBytes); CopyTIdByteArray(l_ipBytes, 0, l_buf, 10, SizeOf(l_ipBytes)); You are not checking to make sure that l_len specifies that enough bytes are actually available to read from l_buf into l_ilUDPData. There is no guarantee that the received datagram is actually as large as your buffer. You are not Connect()'ing the TIdUDPClient to a specific peer, so this line is useless and can be removed. Especially since the next statement is Free()'ing the TIdUDPClient anyway.
-
Cannot create instance of class CLSID_WICImagingFactory
Remy Lebeau replied to Paola's topic in General Help
Is there more to the error explaining WHY it can't create the instance? For instance, has CoInitialize/Ex() been called beforehand? -
You do know that you posted this in a VCL forum, right? ICS has its own forum on this site: https://en.delphipraxis.net/forum/37-ics-internet-component-suite/
-
The main reason it hasn't been merged yet is because I just haven't had any time to review it - I don't question that it "works" in general (I've seen enough people say it does), but I still need to see how consistent it fits with the rest of the library, if all of the necessary package/IDE support is in place, how it handles the multiple platforms Indy runs on, etc. The occasional issues being reported in the PR with regards to compiler/runtime errors, etc. As well as this is kind of a big feature to maybe warrant pushing Indy into a new versioning scheme that is long overdue. So, a lot of behind-the-scenes stuff that has made me hesitant to just merge it blindly.
-
How to get path to the parent folder of a certain directory?
-
That is because the TProgressBarInStatusBar.CreateIn() code provided earlier is not taking the Left position of the specified TStatusPanel into account, only its Width and Height. So it is positioning the TProgressBar in the wrong place for any TStatusPanel other than the 1st one. Unfortunately, neither TStatusBar nor TStatusPanel provide a property or method to get the Left position of a TStatusPanel, so you will have to retrieve it manually, either by: - looping through the TStatusBar.Panels collection: function GetLeftOfStatusPanel(APanel: TStatusPanel); Integer; var I: Integer; Coll: TStatusPanels; begin Result := 0; Coll := APanel.Collection as TStatusPanels; for I := 0 to APanel.Index-1 do Inc(Result, Coll[I].Width); end; - or by asking the OS directly: uses ..., Winapi.CommCtrl; function GetLeftOfStatusPanel(APanel: TStatusPanel); Integer; var R: TRect; begin SendMessage((APanel.Collection.Owner as TStatusBar).Handle, SB_GETRECT, APanel.Index, LPARAM(@R)); Result := R.Left; end; In which case, since CreateIn() wants the TProgressBar to fill the full rectangle of the TStatusPanel anyway, I would just ask the OS for the rectangle and use it as-is, eg: uses ..., Winapi.CommCtrl; class procedure TProgressBarInStatusBar.CreateIn(const inStatusBarPanel: TStatusPanel; var outProgressBar: TProgressBar; var outLabel: TLabel); var statusbar: TStatusBar; R: TRect; Begin statusbar := inStatusBarPanel.Collection.Owner As TStatusBar; SendMessage(statusbar.Handle, SB_GETRECT, inStatusBarPanel.Index, LPARAM(@R)); outProgressBar := TProgressBar.Create(statusbar); outProgressBar.Parent := statusbar; outProgressBar.Top := R.top; outProgressBar.Left := R.left; outProgressBar.Width := R.Width; outProgressBar.Height := R.Height; outLabel := TLabel.Create(outProgressBar); outLabel.Parent := outProgressBar; outLabel.Align := alClient; outLabel.AutoSize := False; outLabel.Alignment := taCenter; end;
-
How does Chrome know that a input with a random name is the password field?
Remy Lebeau replied to ioan's topic in General Help
A password field is the only type of <input> element that has masked characters. Unless you are manually masking the characters of a non-password text input field via script? No browser would treat that as a password field. No. Only an <input> field that is explicitly marked as being a password field is treated as a password field. This doesn't just affect Chrome. Per Mozilla's documentation: And, per autocomplete attribute and login fields: Per Preventing autofilling with autocomplete="new-password": -
How does Chrome know that a input with a random name is the password field?
Remy Lebeau replied to ioan's topic in General Help
Um, because it states that it is a password field? <input ... TYPE="password" ... /> -
# in URLs results in HTTP 400 or 404
Remy Lebeau replied to Maxxed's topic in ICS - Internet Component Suite
Yes, it does.