Jump to content

Remy Lebeau

Members
  • Content Count

    2333
  • Joined

  • Last visited

  • Days Won

    94

Everything posted by Remy Lebeau

  1. 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.
  2. 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.
  3. 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?
  4. Remy Lebeau

    Delphi 2006 to Delphi XE 10.1 VType help

    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.
  5. 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.
  6. Remy Lebeau

    Delphi 2006 to Delphi XE 10.1 VType help

    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).
  7. Remy Lebeau

    Delphi 2006 to Delphi XE 10.1 VType help

    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.
  8. Remy Lebeau

    Cleaning up some Exception handling...

    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?
  9. Remy Lebeau

    TidHTTP TransferEncoding - Chunked

    It does unchunk a chunked response, yes. It does not send chunked requests, though.
  10. Remy Lebeau

    TidHTTP TransferEncoding - Chunked

    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.
  11. Remy Lebeau

    vtString and vtWideString & vtAnsiString

    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;
  12. Remy Lebeau

    Window message sent to a component

    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().
  13. Remy Lebeau

    Window message sent to a component

    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.
  14. Remy Lebeau

    docwiki.embarcadero.com is not working

    Yup, it is not working again for me, too.
  15. Remy Lebeau

    docwiki.embarcadero.com is not working

    It is working for me.
  16. Remy Lebeau

    Thought for the Day stopped working..

    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.
  17. Remy Lebeau

    Thought for the Day stopped working..

    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?
  18. Remy Lebeau

    Virus? How odd.

    I never liked McAfree.
  19. Remy Lebeau

    Thought for the Day stopped working..

    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.
  20. Remy Lebeau

    How to save send SMTP mail to .eml file

    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.
  21. Remy Lebeau

    UnPinnable App

    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.
  22. Remy Lebeau

    Virus? How odd.

    This is why you should configure your AntiVirus/AntiMalware to ignore compiler output folders as exceptions.
  23. Remy Lebeau

    vtString and vtWideString & vtAnsiString

    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.
  24. Remy Lebeau

    vtString and vtWideString & vtAnsiString

    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;
  25. 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).
×