-
Content Count
2333 -
Joined
-
Last visited
-
Days Won
94
Everything posted by Remy Lebeau
-
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().
-
You can't send window messages to a non-windowed class. Well, unless you are calling TObject.Dispatch() directly, that is. Are you? Why are you not posting (not sending) the message to an actual window? You did not show how you are actually sending the message to the class in the first place. Yes, it should, if the messages are going through the normal Dispatch() mechanism to begin with. But since you didn't show your actual code, maybe that is not actually the case? We can't see your code. 144 is in the range of system-reserved message IDs, however there is no known system message with an ID of 144. Message ID 2 is WM_DESTROY, though.
-
Yup, it is not working again for me, too.
-
It is working for me.
-
I think if you change StartQuote to '<div class="q-te"><span>', you should be OK. At least, until the next time the website decides to change formats. This is why scraping data from webpages is not a good idea. It is cumbersome and unreliable over time. It would be better if you can find a different feed that provides data in a machine-parsable format like XML or JSON instead, then you don't run into these kind of issues.
-
But, you haven't explained WHY it is not working. Are you getting errors? Are you not getting the data you are expecting? Be SPECIFIC. Have you tried to debug the code at all? Does TIdHTTP.Get() complete without exception? What are StartQuote and EndQuote defined as?
-
I never liked McAfree.
-
I see some language-related issues in the code, but nothing that I would consider to be a "problem" with Indy itself. You are creating the TIdHTTP object before the try..except block, instead of inside of it. The 'if E.ClassName = 'EIdConnClosedGracefully'' statement should be using the 'is' operator instead: if E is EIdConnClosedGracefully then Or better, using separate 'on' clauses in your 'except' block: except on E: EIdConnClosedGracefully do begin // ShowMessage('Unable to access Thought for the Day at this time.'); // EXIT; end; on E: Exception do begin // ShowMessage('Exception class name = ' + E.ClassName); ShowMessage('Exception message = ' + E.message); // end; // end; And you should use a proper HTML parser instead of what you have now. Why not? What is the ACTUAL PROBLEM you are having? Please be more specific. Just saying it "doesn't work" is meaningless.
-
How to save send SMTP mail to .eml file
Remy Lebeau replied to Helge Larsen's topic in ICS - Internet Component Suite
Indy (more specifically, its OpenSSL IOHandler) supports TLS 1.2 just fine, so you should not have needed to switch to another SMTP library just to continue communicating with Microsoft. -
In addition to everthing said about AddRef/Release(), just note that if your Form's window is ever recreated at runtime, you will loose your marking and have to re-apply it on the new window. Best way to avoid that is to have your Form class override the virtual CreateWnd() method to call MarkWindowAsUnpinnable(), don't call it after creating the Form object.
-
This is why you should configure your AntiVirus/AntiMalware to ignore compiler output folders as exceptions.
-
Yes, you can. You can see the RTL doing exactly that, for instance in the SysUtils.(Wide)FormatBuf() functions. Yes. Yes. Just know that if the AnsiString is empty, the pointer will be nil, so don't dereference it. I prefer to use this instead: Move(PAnsiChar(AnsiString(VAnsiString))^, If the AnsiString is empty (ie, a nil pointer), the PAnsiChar cast will return a non-nil pointer to a #0 AnsiChar in static memory. So the dereference is always valid. Also, this isn't affected by 0-based vs 1-based string indexing. Yes.
-
What is 'S' in this code? It appears to be a pointer into an array of AnsiChars. The code appears to be building up an inline array of length-prefixed ANSI strings. Maybe for transmission/storage somewhere? In any case... vtString indicates a ShortString. Since ShortString is a fixed-length string, the TVarRec.VString field is a pointer to an actual ShortString variable. The code is handling the VString field correctly, dereferencing that pointer to access the ShortString, and thus its length and characters. vtAnsiString indicates an AnsiString. An AnsiString is a dynamic length string, and an AnsiString variable is just a pointer to the AnsiString's payload. As such, the TVarRec.VAnsiString field is a pointer to an AnsiString's payload, NOT a pointer to an AnsiString variable. The code is not handling the VAnsiString field correctly, most importantly its use of sizeof() is wrong to determine the AnsiString's length. You need to typecast the VAnsiString pointer to an AnsiString and then query its Length() instead. Try this: var Len: Integer; ... for I := Low(Values) to High(Values) do with Values[I] do case VType of vtString: begin Len := Length(VString^); // will never exceed 255 S[P] := AnsiChar(Len); System.Move(VString^[1], S[P+1], Len); Inc(P, Len + 1); ls := I = High(Values); end; vtAnsiString: begin Len := Length(AnsiString(VAnsiString)); if Len > 255 raise ...; S[P] := AnsiChar(Len); System.Move(PAnsiChar(AnsiString(VAnsiString))^, S[P+1], Len); Inc(P, Len + 1); ls := I = High(Values); end; ... end; WideString and UnicodeString are also dynamic length string types implemented as pointers, and so vtWideString and vtUnicodeString are handled similarly to vtAnsiString, in that you simply type-cast the TVarRec.VWideString and TVarRec.VUnicodeString pointers as-is to WideString and UnicodeString, respectively. Just know that you will need an additional cast to AnsiString for those values to fit in with the rest of this code, eg: var Len: Integer; Tmp: AnsiString; ... for I := Low(Values) to High(Values) do with Values[I] do case VType of ... vtWideString: begin Tmp := AnsiString(WideString(VWideString)); Len := Length(Tmp); if Len > 255 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 Len > 255 raise ...; S[P] := AnsiChar(Len); System.Move(PAnsiChar(Tmp)^, S[P+1], Len); Inc(P, Len + 1); ls := I = High(Values); end; ... end;
-
Async await with blocking mode using Application.ProcessMessage(var Msg: TMsg)
Remy Lebeau replied to Nasreddine's topic in VCL
That is what the "Msg" in its name stands for - it can wait on the Message Queue AND on a list of signallable objects. SyncEvent is used to let the main thread know when TThread.Synchronize()/TThread.Queue() requests are pending. It is not used to let the main thread know when messages are pending. Simply call MsgWaitForMultipleObjects() with any of the flags that wait on the message queue (I usually just use QS_ALLINPUT), and then call Application.ProcessMessages() whenever MsgWaitForMultipleObjects() reports that messages are pending (when its return value is WAIT_OBJECT_0 + nCount).