-
Content Count
3056 -
Joined
-
Last visited
-
Days Won
139
Everything posted by Remy Lebeau
-
Trying to avoid using SetString when doing a token lookup in a TDictionary
Remy Lebeau replied to MarkShark's topic in RTL and Delphi Object Pascal
Your example is checking for 4 bytes, not 8 bytes. And also, presumably @TokenStart should be FTokenStart, which is already a pointer. But, in any case, if the strings are greater than SizeOf(UInt64) in length, you are only using those first 8 bytes for the "key", so you might not end up with unique keys for different string values. -
Upload files from Delphi to PHP
Remy Lebeau replied to Luciano Veneziano's topic in Network, Cloud and Web
It should look like this: uses ..., IdMultipartFormDataStream; var PostData: TIdMultipartFormDataStream; begin PostData := TIdMultipartFormDataStream.Create; try PostData.AddFile('upfile', 'C:\path\filename'); IdHTTP.Post('https://server/UPLOAD.PHP', PostData); finally PostData.Free; end; end; -
GStack.LocalAddress is deprecated, you should be using GStack.GetLocalAddressList() instead. Indy does not currently use WifiManager, that operates at the Android Java layer, but since Indy operates at the native layer then it uses Linux APIs instead to enumerate local IPs, namely gethostname()+getaddrinfo() (there is a TODO in IdStackVCLPosix.pas to use getifaddrs() instead, or Android's NetworkInterface or WifiManager). MakeDWordIntoIPv4Address() is deprecated, use MakeUInt32IntoIPv4Address() instead. Alternatively, have a look at TIdIPAddress.IPv4(AsString) (which uses MakeUInt32IntoIPv4Address() internally). But in any case, the input value is expected to be in network byte order, aka big endian, but WifiInfo.getIpAddress() returns an IPv4 address in host byte order, which may be either little endian or big endian, depending on the device. You can use Integer.reverseBytes() to swap the IP's endian if ByteOrder.nativeOrder() is LITTLE_ENDIAN. Seems Indy's own use of MakeUInt32IntoIPv4Address() may be inconsistent, some places use host byte order, some don't. Also, WifiInfo.getIpAddress() is deprecated since Android 12. You should use LinkProperties now.
-
Possibly. Embarcadero has been playing around with High-DPI support for a few versions now, which can cause weird side effects.
-
TWSocket problem on Delphi Intraweb
Remy Lebeau replied to Baxing's topic in ICS - Internet Component Suite
I don't agree, since the failure is coming straight from the Win32 API itself. But there is definitely something fishy going on here. The only thing that makes sense to me is if maybe the window created by AllocateHWnd() was silently destroyed, but the FWinHandle variable wasn't updated to reflect that. What does IsWindow(FWinHandle) report after AllocateHWnd() returns, and when PostMessage() fails? Even if there were no message *pump* to process messages, there would still be a message *queue* (a thread's message *queue* is created the first time the thread calls any function in user32.dll) when the window is created. PostMessage() fails with error 1400 only if the provided HWND is not a valid window, not if the window is valid but no message pump is running. PostMessage() has no concept of whether a pump is running or how often, it only cares if the queue exists and is not full. And the failing code has nothing to do with IntraWeb itself, but rather with the Win32 API directly. -
On the client side, I would strongly advise you NOT to bind the TIdTCPClient and TIdHTTPProxyServer to the same local IP/Port at all. Put the destination proxy server's IP/Port in the DEST command itself, and then have the receiving server extract the values from the command, not from the connected socket. Then you can drop the TIdTCPClient connection after sending the DEST command, and even run the TIdTCPClient on a different PC if you wanted to. constructor TRemoteServer.Create(IP: string; Port: Integer); begin IdHTTPProxyServer1 := TIdHTTPProxyServer.Create(nil); IdHTTPProxyServer1.ReuseSocket := rsTrue; IdHTTPProxyServer1.DefaultPort := Port; IdHTTPProxyServer1.OnHTTPBeforeCommand := HTTPBeforeCommandHandler; var handler := IdHTTPProxyServer1.CommandHandlers.Add; handler.Command := 'DEST'; handler.HelpSuperScript := 'Sets caller as a DESTINATION for remote support (usage DEST PIN IP PORT URL)'; handler.OnCommand := DEST_Handler; if (IP <> '') and (Port <> 0) then begin Writeln('Starting server, binding to: ' + IP + '-' + Port.ToString); var bind := IdHTTPProxyServer1.Bindings.Add; bind.IP := IP; bind.Port := Port; bind.ReuseSocket := rsTrue; end else Writeln('Starting server, default binding'); IdHTTPProxyServer1.Active := True; Writeln('Server running, listening on: ' + IdHTTPProxyServer1.Bindings[0].IP + ' - ' + IdHTTPProxyServer1.Bindings[0].Port.ToString); end; procedure TRemoteServer.DEST_Handler(ASender: TIdCommand); begin Writeln(ASender.RawLine); dest_pin := ASender.Params[0]; dest_ip := ASender.Params[1]; dest_port := ASender.Params[2]; dest_url := ASender.Params[3]; Writeln('Address: ' + dest_ip + ' - ' + dest_port.ToString); end; procedure TRemoteServer.HTTPBeforeCommandHandler(AContext: TIdHTTPProxyServerContext); begin if dest_ip <> '' then begin Writeln('Command redirected to DESTINATION: ' + dest_ip + ' - ' + dest_port.ToString); var tempIO := TIdIOHandlerStack.Create(AContext.OutboundClient); tempIO.ReuseSocket := rsTrue; // tempIO.BoundIP := '10.0.2.4'; //IdHTTPProxyServer1.Bindings[0].IP; tempIO.BoundPort := 443; // IdHTTPProxyServer1.Bindings[0].Port; var tempProxy := TIdConnectThroughHttpProxy.Create(AContext.OutboundClient); tempProxy.Enabled := True; tempProxy.Host := dest_ip; tempProxy.Port := dest_port; tempIO.TransparentProxy := tempProxy; AContext.OutboundClient.IOHandler := tempIO; end; end; procedure TForm1.ConnectToRemote(Connection: string); begin HTTPProxyServer.Active := False; HTTPProxyServer.Bindings.Clear; var bind := HTTPProxyServer.Bindings.Add; bind.IP := ...; bind.Port := ...; bind.ReuseSocket := rsTrue; HTTPProxyServer.Active := True; // Tell server we are there IdTCPClient1.Host := <SERVER IP/URL>; //Connection.Split([':'])[0]; IdTCPClient1.Port := <SERVER PORT>; // Connection.Split([':'])[1].ToInteger; IdTCPClient1.Connect; try IdTCPClient1.SendCmd('DEST 4444 ' + bind.IP + ' ' + bind.Port.ToString + ' https://abc.com'); finally IdTCPClient1.Disconnect; end; end;
-
How to use tDateTimePicker for BOTH date and time
Remy Lebeau replied to A.M. Hoornweg's topic in VCL
Has that been reported as a bug yet? If not, it should be. -
Try something like this: procedure update_toc(const docx_file: string); var word, doc: Variant; begin word := CreateOleObject('Word.Application'); doc := word.Documents.Open(WideString(docx_file)); doc.TablesOfContents.Item(1).Update; doc.Close(-1); word.Quit; end;
-
My usual QP credentials are not working right now.
-
How to use tDateTimePicker for BOTH date and time
Remy Lebeau replied to A.M. Hoornweg's topic in VCL
That must be a new feature added in 11.0 Alexandria, because in 10.4 Sydney the only options available were still dtkDate and dtkTime. In which case, I would expect there to be a new and easier way to get the full TDateTime value without resorting to parsing the display text manually. -
It is not just the load balancer that is having issues. For example, trying to access several pages today, I'm running into a new kind of error:
-
Transfer a file from client to server (Delphi XE)
Remy Lebeau replied to MrCamarium's topic in Network, Cloud and Web
You can use the connection's OnWork events for that purpose. Just be aware that they are fired in the context of the thread that is performing the transfer, so if that is not the main UI thread then you will have to synchronize your UI updates with the main thread. https://www.indyproject.org/2021/02/10/links-to-old-indy-website-pages-are-currently-broken/ https://web.archive.org/web/20200925081341/http://ww2.indyproject.org/Sockets/Docs/Indy10Installation.EN.aspx That is a build script for C++Builder (hence the C in the filename), not for Delphi. Indy stopped using build scripts for Delphi after D2009. For Delphi, you can just open the project files directly in the IDE and use the Compile and Install options in the Project Manager. Of course, because 1) you didn't remove the old version from the IDE, and 2) the build script only compiles the library binaries, but it does not install them into the IDE. You have to do that step manually. Never use relative paths, always use absolute paths. -
Transfer a file from client to server (Delphi XE)
Remy Lebeau replied to MrCamarium's topic in Network, Cloud and Web
That is an extremely old version of Indy, you should upgrade to the latest version, which is 10.6.2. Even if you can't upgrade your IDE. -
Transfer a file from client to server (Delphi XE)
Remy Lebeau replied to MrCamarium's topic in Network, Cloud and Web
OK, thanks. The TIdTCPServer.OnExecute event is fired in an endless loop for the lifetime of the TCP connection. The event doesn't care what data is transmitted. That is the responsibility of the code inside of the event to deal with as needed. That doesn't really matter in this case. ReadStream() will handle those chunks internally. The code's use of Write() is sending the file size followed by the file data. Then the code's use of ReadStream() is reading the file size and then reading the file data up to that size. -
Transfer a file from client to server (Delphi XE)
Remy Lebeau replied to MrCamarium's topic in Network, Cloud and Web
What errors, exactly? Please be more specific. And, are you really using Delphi XE? That is 12 years old. Are you, at least, using an up-to-date version of Indy? Or, are you are using a 12-year-old version? The code to transfer the file looks fine to me, so you are just going to have to debug deeper. That zip file contains the same client code you originally posted. It does not include the changes I suggested for the client. -
Transfer a file from client to server (Delphi XE)
Remy Lebeau replied to MrCamarium's topic in Network, Cloud and Web
You are leaking the TFileStream object. You need to Free() it when you are done using it. You should wrap the call to Write() in a try..finally block for that, in case Write() raises an exception, eg: procedure TForm1.Button3Click(Sender: TObject); var TFLFileOut: TFileStream; begin TFLFileOut := TFileStream.Create(ToDownload.Text, fmOpenRead or fmShareDenyWrite); try Client.IOHandler.Write(TFLFileOut, 0, true); finally TFLFileOut.Free; end; end; Likewise, you should use try..finally here too, to ensure the TFileStream is Free()'d even if an exception is raised. procedure TForm1.ServerExecute(AContext: TIdContext); var TFSFileIn: TFileStream; begin Label1.Caption := 'Arriva qualcosa...'; TFSFileIn := TFileStream.Create(Edit1.Text, fmCreate); try AContext.Connection.IOHandler.ReadStream(TFSFileIn); finally TFSFileIn.Free; end; end; But, more importantly, TIdTCPServer is a multi-threaded component. Its OnExecute event is fired in the context of a worker thread, not the main UI thread. As such, accessing UI controls, such as your TLabel an TEdit controls, is NOT thread-safe. You MUST synchronize with the main UI thread when accessing them, eg: procedure TForm1.ServerExecute(AContext: TIdContext); var FileName: string; TFSFileIn: TFileStream; begin TThread.Synchronize(nil, procedure begin Label1.Caption := 'Arriva qualcosa...'; Filename := Edit1.Text; end end; TFSFileIn := TFileStream.Create(FileName, fmCreate); try AContext.Connection.IOHandler.ReadStream(TFSFileIn); finally TFSFileIn.Free; end; end; Your calls to Write() and ReadStream() are matched up properly, so I cannot see a reason why the received file would be empty, provided that Write() and ReadStream() are actually being called. Did you verify that with your debugger? -
That code pre-dates Delphi's shift to Unicode in D2009. But vtWideString and vtPWChar did exist prior to that shift. That code simply does not support Unicode strings at all.
-
TBitmap to TBytes for ESC/POS Thermal Printer
Remy Lebeau replied to at3s's topic in RTL and Delphi Object Pascal
BytesOf() for a (Wide|Unicode)String is just a wrapper for TEncoding.Default.GetBytes(), eg: SND := TEncoding.Default.GetBytes(AValue); Note that TEncoding.Default is TEncoding.ANSI on Windows, but is TEncoding.UTF8 on other platforms. -
In this code, FISize is not a local variable! It is a class member instead. That makes a big difference. That is completely wrong, and shouldn't have worked even before D2009. vtPWideChar and vtAnsiString are completely different and incompatible types, they need to be handled separately (like I showed you earlier). vtPChar (TVarRec.VPChar) is a null-terminated PAnsiChar, whereas vtPWideChar (TVarRec.VPWideChar) is a null-terminated PWideChar, and TVarRec.VAnsiString is a pointer to an AnsiString's internal payload. You can't treat those 3 pointers the same way. For the purpose of this code, TVarRec.VPChar can be used as-is, but TVarRec.VPWideChar would need to be assigned to a temp AnsiString, and TVarRec.VAnsiString would need to be type-casted into an AnsiString, before their characters can be copied into the ShortString (like I showed you earlier).
-
Why not? Can you be more specific about the problems you are having with it? Yes, it is. Where did you get that idea from? Did that code ever work to begin with? The very first thing it is doing is calling SetLength() (twice!) on a ShortString using an uninitialized FISize variable. Did you remove code not shown here? Where is FISize calculated? In any case, vtChar represents an AnsiChar. System.Char was changed from AnsiChar to WideChar in D2009, so you need to handle vtWideChar now, converting/truncating it to an AnsiChar before copying it into your ShortString. I already showed you earlier how to properly handle vtString, vtAnsiString, vtWideString, and vtUnicodeString and copy them into the ShortString. That said, it is really hard to help you when you keep showing PIECES of code, and not the WHOLE code. It is really hard to understand what your target ShortString is supposed to actually look like given various inputs (or why are you even using ShortString to begin with!!!). And you haven't shown ANY examples of what the input array even looks like to begin with.
-
E:ErrorCode is not valid syntax, did you mean E.ErrorCode? The original code displays a TaskMessageDlg on an unhandled exception, but regardless of the type of exception raised, any code following the try..except block continues to run normally, With your proposed change, on a re-raise of an unhandled exception, any code following the try..except block won't run anymore. That is a change in program flow, not just a change in error reporting. Is that what you really want?
-
It does unchunk a chunked response, yes. It does not send chunked requests, though.
-
TIdHTTP only supports RECEIVING chunked data, it does not SEND chunked data (you would have to chunk the data manually). And, even if it did support SENDING chunked data, it certainly would not do that in an HTTP 1.0 request, since chunking didn't exist until HTTP 1.1. And per the HTTP 1.1 spec, clients are discouraged from SENDING chunked requests unless they know beforehand via outside means that the server can actually receive chunked requests, That is not possible with TIdHTTP alone. Something else must be going on, for instance if your request is passing through a proxy that reformats your request into a chunked request. You need to explain your situation better, and provide actual logs of the HTTP traffic you are seeing, if you can.
-
There has to be more involved than just that. Since the code is indexing into S using P, and it is incrementing P after copying AnsiChars into S, and the code is looped, that implies that the code is storing more than 1 string value into that ShortString. If it were just a matter of converting 1 (Ansi|Unicode|Wide)String to 1 ShortString, the code could be much simpler, using a normal assignment and letting the compiler do all of the hard work. Can you be more specific? I know for a fact that the code I showed earlier is handling UnicodeString correctly. Though, now that I can see your variable declarations and see that P is a Byte not an Integer, there is an extra validation that the code should be doing when copying AnsiChars into S to make sure S doesn't overflow, eg: var I: Integer; P: Byte; S: ShortString; ls: Boolean; ... Len: Integer; Tmp: AnsiString; ... for I := Low(Values) to High(Values) do with Values[I] do case VType of vtString: begin Len := Length(VString^); if (Integer(P) + 1 + Len) > 256 then raise ...; S[P] := AnsiChar(Len); System.Move(VString^[1], S[P+1], Len); Inc(P, Len + 1); ls := I = High(Values); end; vtAnsiString: begin Tmp := AnsiString(VAnsiString); Len := Length(Tmp); if (Integer(P) + 1 + Len) > 256 then raise ...; S[P] := AnsiChar(Len); System.Move(PAnsiChar(Tmp)^, S[P+1], Len); Inc(P, Len + 1); ls := I = High(Values); end; vtWideString: begin Tmp := AnsiString(WideString(VWideString)); Len := Length(Tmp); if (Integer(P) + 1 + Len) > 256 then raise ...; S[P] := AnsiChar(Len); System.Move(PAnsiChar(Tmp)^, S[P+1], Len); Inc(P, Len + 1); ls := I = High(Values); end; vtUnicodeString: begin Tmp := AnsiString(UnicodeString(VUnicodeString)); Len := Length(Tmp); if (Integer(P) + 1 + Len) > 256 then raise ...; S[P] := AnsiChar(Len); System.Move(PAnsiChar(Tmp)^, S[P+1], Len); Inc(P, Len + 1); ls := I = High(Values); end; ... end;
-
You can't mix AllocateHwnd() with message dispatch methods, that is not how the system works. You need to handle the messages directly in the WndProc that you give to AllocateHwnd().