Jump to content

Remy Lebeau

Members
  • Content Count

    3001
  • Joined

  • Last visited

  • Days Won

    135

Everything posted by Remy Lebeau

  1. That is an error message for a compiler bug. All you can do with it is report it to Embarcadero at https://qp.embarcadero.com. Start taking code out of your project until the error goes away, and then slowly reintroduce the removed code until the error returns. Try to isolate the most minimal code change that causes the bug, then include that detail in your bug report.
  2. The style attribute on the <img> element is formatted correctly: <img src="data:image/png;base64,..." style="height:150px; width:125px" /> Multiple CSS styles in a single "style" attribute ARE supposed to be separated by semicolons.
  3. Then the problem is not with Indy itself. It is entirely a problem with the email reader. It likely just doesn't support CSS, or doesn't handle it correctly. My point is, a .msg file is a binary file that is useless to people who don't use Outlook and can't open it (like me). Whereas an .eml file is plain text instead. You are using a Unicode version of Delphi, and Indy handles Unicode strings for you. You don't need to encode the strings manually, nor is it beneficial to do so because you are assigning UTF8String's where UnicodeString's are expected, thus the Delphi compiler+RTL is simply going to implicitly decode your UTF-8 back to UTF-16 before Indy even sees the data. So just use plain Delphi strings, don't encode them manually. Indy defaults to UTF-8 in Unicode versions of Delphi when encoding the email data. Look for yourself. You have a <meta charset> that states "utf-8", followed by a <meta http-equiv> that states "windows-1255". Doubtful.
  4. You can't modify or replace the IDE's built-in editors. If you don't like the way the editors look/behave, file a report at https://qp.embarcadero.com
  5. What versions of Delphi and Indy are you using? Did you compare the HTML that Outlook has to the HTML that you sent? .msg files are binary files that Outlook creates for itself after converting the original data. It is not "received" from the sender. So, it is hard to diagnose them. Can you export an .eml file instead? Or at least view the source data for the .msg file? How does its HTML compare to the original HTML? Not related to your problem, but why are you UTF-8 encoding the strings you give to Indy? Don't do that. Use plain strings instead, and let Indy encode them for you. Also, you should not be messing with the TIdMessage properties after you have called TIdMessageBuilderHtml.FillMessage(). It has already set the properties accordingly for you. Your HTML has conflicting charsets in its <meta> elements. Also, it is unusual to use a "data:" url to display an image in an HTML email. Why are you not attaching the image file to the email and then using a "cid:" url to display it? Granted, TIdMessageBuilderHtml doesn't care what HTML you give it, but it is designed for "cid:" urls in mind.
  6. Remy Lebeau

    Buggy auto-completion

    Agreed. I'm constantly having to fight with the auto-complete, taking time to edit/undo what it generates. Embarcadero should really focus more effort on this area.
  7. Remy Lebeau

    Searching Edit component with autocomplete for Directory/Files

    This is the complete code. No crashes or leaks: program WideStringTest; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils, Winapi.ActiveX; type TEnumStringTestBase = class(TInterfacedObject, IEnumString) function Next(celt: Longint; out elt; pceltFetched: PLongint): HResult; virtual; stdcall; abstract; function Skip(celt: Longint): HResult; stdcall; function Reset: HResult; stdcall; function Clone(out enm: IEnumString): HResult; stdcall; end; TEnumStringTest1 = class(TEnumStringTestBase) function Next(celt: Longint; out elt; pceltFetched: PLongint): HResult; override; end; TEnumStringTest2 = class(TEnumStringTestBase) function Next(celt: Longint; out elt; pceltFetched: PLongint): HResult; override; end; function TEnumStringTestBase.Skip(celt: Longint): HResult; stdcall; begin Result := S_FALSE; end; function TEnumStringTestBase.Reset: HResult; stdcall; begin Result := S_OK; end; function TEnumStringTestBase.Clone(out enm: IEnumString): HResult; stdcall; begin Result := E_NOTIMPL; end; function TEnumStringTest1.Next(celt: Longint; out elt; pceltFetched: PLongint): HResult; type TPointerList = array[0..0] of Pointer; var i: Integer; begin i := 0; while (i < celt) do begin {$RANGECHECKS OFF} TPointerList(elt)[I] := nil; WideString(TPointerList(elt)[I]) := 'test'+IntToStr(I); Inc(i); end; if pceltFetched <> nil then pceltFetched^ := i; if i = celt then Result := S_OK else Result := S_FALSE; end; function TEnumStringTest2.Next(celt: Longint; out elt; pceltFetched: PLongint): HResult; type TWideStringList = array[0..0] of WideString; var i: Integer; begin i := 0; while (i < celt) do begin {$RANGECHECKS OFF} Pointer(TWideStringList(elt)[I]) := nil; TWideStringList(elt)[I] := 'test'+IntToStr(I); Inc(i); end; if pceltFetched <> nil then pceltFetched^ := i; if i = celt then Result := S_OK else Result := S_FALSE; end; var Enum: IEnumString; arr1: array[0..4] of TBstr; arr2: array[0..4] of WideString; I: Integer; begin Enum := TEnumStringTest1.Create; Enum.Next(Length(arr1), arr1, nil); for I := Low(arr1) to High(arr1) do WriteLn(arr1[i]); for I := Low(arr1) to High(arr1) do SysFreeString(arr1[i]); WriteLn; Enum := TEnumStringTest2.Create; Enum.Next(Length(arr2), arr2, nil); for I := Low(arr2) to High(arr2) do WriteLn(arr2[i]); ReadLn; end.
  8. Remy Lebeau

    THttpClient vs TIdHttp in Linux

    Capturing the traffic is easy with TIdHTTP. You can simply assign any of Indy's TIdLog... components to the TIdHTTP.Intercept property, such as TIdLogFile. I don't know if THTTPClient has any similar feature to capture its traffic without using an external sniffer. If Wireshark is too much for you, maybe Fiddler or similar logging proxy with work for you?
  9. Remy Lebeau

    Searching Edit component with autocomplete for Directory/Files

    I tested the code I showed with Delphi 12.1 before I posted it, and it worked fine. And it should also work fine in earlier versions, as handling of WideString has not changed over the years. Which makes me wonder if there is another problem with your larger project and .Next is just a victim of circumstance? This feels like a classic symptom of "undefined behavior" being invoked in earlier code.
  10. Quite possibly. Keep in mind that TCP is resilient to temporary network outages, so as long as the WiFi maintains the same IP when it comes back on, previous TCP connections may be recoverable by the OS if it hasn't invalidated them yet. On the other hand, if your code is not reading/writing data with the connection during the time that the WiFi is turned off, then it makes sense that you won't see a disconnect. It can take time for a lost connection to be detected. If the OS's internal timeout is not to your liking, you might consider enabling TCP keep-alives on the connection.
  11. Remy Lebeau

    Searching Edit component with autocomplete for Directory/Files

    WideString is not reference counted, and setting a WideString to nil via a Pointer typecast works just fine to release ownership of the BSTR without freeing its memory. Always has. And that has not changed. Looking at your changes, I think you are simply misusing WideString to begin with. TEnumStringHelper.Next() should look more like this instead: function TEnumStringHelper.Next(celt: Integer; out elt; pceltFetched: PLongint): HResult; type TPointerList = array[0..0] of Pointer; var i: Integer; s: string; begin i := 0; while (i < celt) and TryGetNext(s) do begin // this directive is needed because the array is declared as having only // 1 element but celt may be > 1! The alternative is to either declare // TPointerList with a rediculous high number of elements (like the RTL // does in some places), eg: // // type // TPointerList = array[0..MaxInt div SizeOf(Pointer) - 1] of Pointer; // // or else just use pointer arithmetic on elt directly... {$RANGECHECKS OFF} // Ensure the WideString is nil before assiging a String to it... TPointerList(elt)[i] := nil; WideString(TPointerList(elt)[i]) := s; Inc(i); end; if pceltFetched <> nil then pceltFetched^ := i; if i = celt then Result := S_OK else Result := S_FALSE; end; Alternatively: function TEnumStringHelper.Next(celt: Integer; out elt; pceltFetched: PLongint): HResult; type TWideStringList = array[0..0] of WideString; var i: Integer; s: string; begin i := 0; while (i < celt) and TryGetNext(s) do begin // see notes above... {$RANGECHECKS OFF} Pointer(TWideStringList(elt)[i]) := nil; TWideStringList(elt)[i] := s; Inc(i); end; if pceltFetched <> nil then pceltFetched^ := i; if i = celt then Result := S_OK else Result := S_FALSE; end; Also, your TEnumStringHelper.Skip() is just plain wrong. If celt is <= 1 then you get stuck in an endless loop retrieving strings until the enum is exhausted, and if celt is > 1 then you exit prematurely after 1 string is retrieved. It should look more like this instead: function TEnumStringHelper.Skip(celt: Integer): HResult; var i: Integer; s: string; begin i := 0; while (i < celt) and TryGetNext(s) do Inc(i); if i = celt then Result := S_OK else Result := S_FALSE; end;
  12. No. The connection has to be disconnected and reconnected, it can't be moved from one network to another. Android is doing the right thing. https://android.stackexchange.com/questions/83830/how-can-android-keep-a-tcp-connection-alive-when-changing-network-provider I would question why Windows is not doing the same thing, as it should be. Maybe your code is just not doing anything that triggers a disconnect during a network change? On both platforms, you might consider having your app register with the OS to receive network change notifications.
  13. Remy Lebeau

    Dropping component on form

    Yes, TSelectionEditor is the way to go. When a unit is saved or compiled, the IDE first invokes any registered TSelectionEditor classes relevant to that unit, calling their RequiredUnits() methods to update the 'uses' clause. Indy registers several TSelectionEditor classes for a few specific cases. But, I want to point out 1 editor in particular: TIdBaseComponentSelectionEditor. It is registered for TIdBaseComponent, which all Indy components are derived from. TIdBaseComponentSelectionEditor iterates all Indy components on a Form, using RTTI to look for any event handlers that are assigned to those components, and then to determine the types of any parameters and return values of those event handlers, and the unit names that those types belong to. It then reports those units to the IDE to add to the 'uses' clause. Now, one would think this is something that the IDE itself should handle automatically for all components, but it doesn't, or at least not completely. Which is why TIdBaseComponentSelectionEditor was created, instead of having to define a separate TSelectionEditor for each individual component that needs it. I mention this because Indy has a LOT of components, so it makes sense to handle something like this in a central place. So, if you don't want to write a bunch of smaller editors for multiple components in your library, you might consider a similar approach (although handling RTTI differences across multiple IDE versions can be a bit of a task).
  14. Remy Lebeau

    WideString.c_bstr() operation in 11.2

    The ticket is still open. There is no solution presented in this thread. It is a regression bug somewhere in the RTL.
  15. I have updated the repo now.
  16. Remy Lebeau

    THttpClient vs TIdHttp in Linux

    Did you try capturing and comparing the raw HTTP requests that THTTPClient and TIdHTTP are generating to see what is different between them?
  17. Remy Lebeau

    THttpClient vs TIdHttp in Linux

    Can you be more specific? Yes. What does that have to do with the issue?
  18. OK, I will make that change tomorrow. I added that file earlier today, along with the non-cool DCR, and the LRS for FreePascal. Which changes? That's good to hear, thanks.
  19. Doubtful, given it was just created 2 days ago.
  20. Remy Lebeau

    Upgrading to new RAD Studio vs. project Lib names

    Nope. The documentation mentions the unit name and header file, but not the lib file.
  21. Remy Lebeau

    TButton : change backgroundcolor

    Embarcadero posted a blog article about that for TEdit, but the same approach should work for a TButton as well: Edit Custom Style to change the background color of a FMX TEdit It is just a property like any other. What is stopping you from setting it?
  22. Remy Lebeau

    Calling a Delphi DLL function from a C++ Builder application

    The C++ code is not creating a valid AnsiString object for the Delphi code to manipulate. The struct would have needed to look more like this instead: struct DECLSPEC_DRECORD TMyRecord { AnsiString MyString; int MyInteger; }; But, that still would not have worked correctly in your case, because the internal structure of AnsiString changed in 2009, so passing an AnsiString object between C++Builder 6 and Delphi 12.1 would cause Undefined Behavior, It it never safer to pass non-POD types across a DLL boundary, unless very strict prerequisites are met (ie, sharing a common RTL, sharing the same memory manager, etc), which is not the case in this example. To do what you are attempting, you would have to allocate AND free the string memory inside of the DLL, and then pass that memory around as needed, eg: library MyDLL; uses System, SysUtils; type TMyRecord = record MyString: PAnsiChar; MyInteger: Integer; end; function FillRecord(var Rec: TMyRecord): Boolean; stdcall; begin Rec.MyString := StrNew(PAnsiChar('Hello from Delphi')); Rec.MyInteger := 42; Result := True; end; procedure DisposeRecord(var Rec: TMyRecord); stdcall; begin StrDispose(Rec.MyString); Rec.MyString := nil; end; exports FillRecord, DisposeRecord; begin end. struct DECLSPEC_DRECORD TMyRecord { char* MyString; int MyInteger; }; extern "C" __declspec(dllimport) bool __stdcall FillRecord(TMyRecord *Rec); extern "C" __declspec(dllimport) void __stdcall DisposeRecord(TMyRecord *Rec); TMyRecord iMyRec = {}; Memo1->Lines->Clear(); Memo1->Lines->Add(Format("Address: %p", ARRAYOFCONST((&iMyRec)))); if (FillRecord(&iMyRec)) { AnsiString iData = iMyRec.MyString; Memo1->Lines->Add("iMyRec.MyString: " + iData); int iNumber = iMyRec.MyInteger; Memo1->Lines->Add("iMyRec.MyInteger: " + IntToStr(iNumber)); DisposeRecord(&iMyRec); } else { Memo1->Lines->Add("Error calling FillRecord"); }
  23. Remy Lebeau

    TButton : change backgroundcolor

    That is true only if its ControlType property is set to Platform. The default is Styled instead, in which case it is not a native Windows control. (This is posted in an FMX group, not a VCL group)
  24. Tools menu, Manage Features, select "Help" in the "Reference" section of the right sidebar, click Apply.
  25. Mouse.CursorPos is expressed in screen coordinates, not client coordinates. You would need to use PaintBox1.ScreenToClient() in this case: procedure TForm23.PaintBox1Click(Sender: TObject); begin if PaintBox1.ScreenToClient(Mouse.CursorPos).Y > 100 then Exit; // do something... end;
×