Jump to content

Remy Lebeau

Members
  • Content Count

    2914
  • Joined

  • Last visited

  • Days Won

    130

Everything posted by Remy Lebeau

  1. Remy Lebeau

    Present status of Indy

    Still actively developed (by me). RAD Studio 10.4 includes an updated trunk version from Indy's GitHub repo from about 2 months ago. I'm not in charge of, or involved with, how Embarcadero incorporates help files for 3rd party components. But I do recall that Indy's help files are included, somewhere. Although, Indy's documentation is quite dated, it hasn't been updated in many years. There have been property/interface changes made over the years that still need to be documented.
  2. Remy Lebeau

    Whats the idea behind Dev-Cpp support ?

    Are you thinking of Borland C++Builder X? The Java-based IDE (using the JBuilder architecture) that was released after Borland EOL'ed C++Builder 6, and before CodeGear resurrected C++Builder in BDS 2005. Using wxWidgets with the Borland C++ BuilderX There is also TwinForms, which is a wxWidgets Form Designer for C++Builder.
  3. Remy Lebeau

    Whats the idea behind Dev-Cpp support ?

    C++Builder includes a Delphi compiler.
  4. Remy Lebeau

    How to increase the distance between TCheckBox glyph and caption text?

    The WordWrap property was added to TButtonControl (which TCheckBox derives from) in Delphi 7. All it does is enables the BS_MULTILINE window style on the control, so it is possible to enable this style manually in earlier Delphi versions, too.
  5. Remy Lebeau

    Varialbe set to nil

    That is a VERY OLD problem that interface users must always be careful of. You are creating a strong circular reference between the two classes. TMyClass1 has a strong reference to TMyClass2, so the refcount of TMyClass2 is incremented. And TMyClass2 has a strong reference to TMyClass1, so the recount of TMyClass1 is incremented. In that situation, neither object's refcount can fall to 0 to destroy either object without explicit intervention to release one of the references, otherwise both objects are leaked. So yes, one solution is to explicitly release one of the references to decrement its refcount manually. Another solution is to have one of the objects use a weak reference instead of a strong reference, so that the refcount of the weak-referenced object is not incremented to begin with. Delphi 10.1 Berlin added support for [weak] and [unsafe] attributes on interface variables. But prior to 10.1 Berlin, raw Pointers have to be used to accomplish the same thing. For example: program Project3; {$APPTYPE CONSOLE} {$R *.res} uses FastMM4, system.Classes, System.SysUtils; type MyInterface2 = interface ['{8E7BC71F-55EE-4345-8A5B-42C433BE9EBA}'] procedure print; end; MyInterface1 = interface ['{607A66A6-D68C-4980-9FB1-83B325EE9A91}'] procedure SetInterface2(aInterface: MyInterface2); procedure DoSomethingWithInterface2; end; TMyClass1 = class(TInterfacedObject, MyInterface1) private RefToInterFace2: MyInterface2; public procedure SetInterface2(aInterface: MyInterface2); procedure DoSomethingWithInterface2; end; TMyClass2 = class(TInterfacedObject, MyInterface2) private // in Delphi 10.1 Berlin and later, use this... [weak]{or [unsafe]} Ref: MyInterface1; // prior to Delphi 10.1 Berlin, use this... // (you will have to typecast Ref every time you want to access MyInterface1) // Ref: Pointer{MyInterface1}; public constructor Create(int: MyInterface1); reintroduce; procedure print; end; { TMyClass1 } procedure TMyClass2.print; begin Writeln('TMyClass2.print'); Sleep(1500); end; { TMyClass2 } procedure TMyClass1.DoSomethingWithInterface2; begin if RefToInterFace2 <> nil then RefToInterFace2.print; end; procedure TMyClass1.SetInterface2(aInterface: MyInterface2); begin RefToInterFace2 := aInterface; end; constructor TMyClass2.Create(int: MyInterface1); begin inherited Create; Ref := int; if int <> nil then int.SetInterface2(Self); end; var aMyClass1: MyInterface1; aMyClass2: MyInterface2; begin aMyClass1 := TMyClass1.Create; aMyClass2 := TMyClass2.Create(aMyClass1); aMyClass1.DoSomethingWithInterface2; end. Or: program Project3; {$APPTYPE CONSOLE} {$R *.res} uses FastMM4, system.Classes, System.SysUtils; type MyInterface2 = interface ['{8E7BC71F-55EE-4345-8A5B-42C433BE9EBA}'] procedure print; end; MyInterface1 = interface ['{607A66A6-D68C-4980-9FB1-83B325EE9A91}'] procedure SetInterface2(aInterface: MyInterface2); procedure DoSomethingWithInterface2; end; TMyClass1 = class(TInterfacedObject, MyInterface1) private // in Delphi 10.1 Berlin and later, use this... [weak]{or [unsafe]} RefToInterFace2: MyInterface2; // prior to Delphi 10.1 Berlin, use this... // (you will have to typecast RefToInterFace2 every time you want to access MyInterface2) // RefToInterFace2: Pointer{MyInterface2}; public procedure SetInterface2(const aInterface: MyInterface2); procedure DoSomethingWithInterface2; end; TMyClass2 = class(TInterfacedObject, MyInterface2) private Ref: MyInterface1; public constructor Create(int: MyInterface1); reintroduce; // prior to Delphi 10.1 Berlin, use this... // destructor Destroy; override; procedure print; end; { TMyClass1 } procedure TMyClass2.print; begin Writeln('TMyClass2.print'); Sleep(1500); end; { TMyClass2 } procedure TMyClass1.DoSomethingWithInterface2; begin if RefToInterFace2 <> nil then begin // in Delphi 10.1 Berlin and later, use this... RefToInterFace2.print; // prior to Delphi 10.1 Berlin, use this... // MyInterface2(RefToInterFace2).print; end; end; procedure TMyClass1.SetInterface2(const aInterface: MyInterface2); begin RefToInterFace2 := aInterface; end; constructor TMyClass2.Create(int: MyInterface1); begin inherited Create; Ref := int; if int <> nil then int.SetInterface2(Self); end; // prior to Delphi 10.1 Berlin, use this... {destructor TMyClass2.Destroy; begin if Ref <> nil then MyInterface1(Ref).SetInterface2(nil); inherited Destroy; end;} var aMyClass1: MyInterface1; aMyClass2: MyInterface2; begin aMyClass1 := TMyClass1.Create; aMyClass2 := TMyClass2.Create(aMyClass1); aMyClass1.DoSomethingWithInterface2; end.
  6. Remy Lebeau

    Twsocket Tcp Client

    I don't use ICS, so I don't know the answer to that. However, an easy way to find out is to simply have the event handler compare the return value of the Win32 GetCurrentThreadId() function against the value of the RTL's MainThreadID variable. If the values match, the event is run in the UI thread and accessing UI controls is safe, otherwise access needs to be synchronized. If the TWSocket is running in the context of the main UI thread, then whether you do the logic in the OnDataAvailable event or in another UI event, you are still going to block subsequent data receiving while the TMemo is busy adding data. I would say don't worry about it unless it proves to be a real bottleneck, in which case you can then either move the TWSocket to another thread that posts new data to the main UI thread asynchronously so as not to block the socket, or else perhaps put new data into a queue somewhere and then use a timer to periodically dump the queue into the TMemo.
  7. Remy Lebeau

    Centered message?

    Note that only applies to MessageDlg() and other custom dialogs. For the Win32 MessageBox() function, and the VCL's Application.MessageBox() (which just calls the Win32 MessageBox()), you would have to hook into the Win32 API via SetWindowsHookEx() or SetWinEventHook() in order to capture the dialog window, and then you can reposition it as needed.
  8. Remy Lebeau

    Varialbe set to nil

    When a class object instance is destroyed, its managed data members are finalized automatically for you (inside of TObject.CleanupInstance(), which is called automatically by the RTL). Strings, dynamic arrays, and interfaces are all managed types that use reference counting. The RTL automatically decrements their reference counts for you when they go out of scope, such as when a TMyClass1 object is destroyed (it will not necessarily set the variables themselves to nil, though, but that doesn't matter during object destruction). RefToInterFace2.Release() is invoked by the RTL when the TMyClass1 object is destroyed. Actually, it does. When the TMyClass1 object is destroyed, the RTL calls TObject.CleanupInstance(), which then loops through TMyClass1's RTTI finalizing all managed class members. That destruction happens when your aMyClass1 variable goes out of scope on the 'end.' statement, where the RTL finalizes the aMyClass1 variable, decrementing the TMyClass1 object's refcount. And since its refcount falls to 0, the TMyClass1 destructor is called. Your aMyClass2 variable will have already gone out of scope and been finalized by that time, so the refcount of the TMyClass2 object will be 1 when the TMyClass1 destructor is entered. When RefToInterFace2 is finalized by the RTL, the TMyClass2 object's refcount falls to 0, calling the TMyClass2 destructor.
  9. Remy Lebeau

    How to increase the distance between TCheckBox glyph and caption text?

    For VCL, you could set the button to owner-draw and then manually draw your own check box however you want, then you can include more spacing in the drawing. However, you would have to do this at the Win32 API layer, not at the VCL layer.
  10. Remy Lebeau

    One more memory leak and FastMM4

    As you noted, that code is not allocating memory correctly. GetMem() operates on bytes, but s.Length is expressed in characters, not bytes, and SizeOf(Char) is 2 bytes in D2009+, so you need to double the allocation, otherwise you will have a buffer overflow on StrPCopy(), corrupting surrounding memory, which can lead to all kinds of problems, including leaks of other things that were allocated dynamically, if you corrupt the pointers to them: var pc: PChar; s: String; begin s := 'Hello, world!'; GetMem(pc, (s.Length + 1) * SizeOf(Char)); try StrPCopy(pc, s); finally FreeMem(pc); end; end; Alternatively, use StrAlloc() instead: var pc: PChar; s: String; begin s := 'Hello, world!'; pc := StrAlloc(s.Length + 1); try StrPCopy(pc, s); finally StrDispose(pc); end; end; In either case, I would not use GetMem() or StrAlloc() at all, I would use a dynamic array instead: Var pc: array of Char; // or TArray<Char> s: String; begin s := 'Hello, world!'; SetLength(pc, s.Length + 1); StrPCopy(PChar(pc), s); end; Now, with that said, you claim you are seeing "leaks" when you use StrPCopy(pc, s + s) and StrPCopy(pc, s + s + s + s +s). Well, those are both buffer overflows, since you are allocating only enough memory for 1 string. It is likely that one of the pointers you are corrupting may be the one used by 'pc' or 's' itself! You will have to use the debugger to verify that. Check the values of the pointers before and after calling StrPCopy(), and see if they changed. Or even put Data Breakpoints on the pointers and see if the breakpoints are getting triggered.
  11. Remy Lebeau

    FreeAndNil 10.4 vs 10.3.1 and Pointers

    It can't, that is a bug in the original code. But the pre-10.4 version of FreeAndNil() would allow it to compile (but it would fail to operate properly at runtime). The new 10.4 version of FreeAndNil() will not compile it. Absolutely, and Delphi even has a TBytes type for this exact purpose (unless the PArrayOfByte pointer is coming from another library, in which case it needs to be passed back to that library for proper freeing).
  12. Remy Lebeau

    FreeAndNil 10.4 vs 10.3.1 and Pointers

    Yes, actually it does. The thing on its left is the * token. It might help to visualize this if you add whitespace between the TObject and * tokens - the compiler will simply ignore that whitespace, but the tokens are still treated separate: void FreeAndNil(const TObject * const &Obj); So, the 1st const binds to the TObject type, and the 2nd const binds to the * pointer, so you end up with the Obj parameter being a reference to type "const pointer to const TObject".
  13. Remy Lebeau

    FreeAndNil 10.4 vs 10.3.1 and Pointers

    in C++, const is much more flexible than in Delphi, more so than that documentation explains. The semantics of const depend on WHERE the const is placed. In general, const applies to the thing on its left, unless there is nothing there then it applies to the thing on its right instead. So, for instance, if we have these declarations in C++: void FreeAndNil(const TObject* &Obj); or void FreeAndNil(TObject const * &Obj); Then the const applies only to the TObject instance that is being pointed at, so its data members cannot be changed by the function. The const does not apply to the TObject* pointer itself, which is being passed by reference, so that pointer can be freely assigned a new value by the function to point at a different TObject instance or null, and that change is reflected to the caller's variable. Whereas if we have this declaration instead in C++: void FreeAndNil(TObject* const &Obj); Then the const applies only to the TObject* pointer itself, thus it can't be assigned a new value by the function. The const does not apply to the TObject instance, so its data members can be freely modified by the function. The two examples above can be combined to have a const pointer to a const TObject instance: void FreeAndNil(const TObject* const &Obj); or void FreeAndNil(TObject const * const &Obj); It does not make sense to put const after Obj, since by definition a reference cannot be reseated once it has been initialized: void FreeAndNil(... &Obj const); or void FreeAndNil(... &Obj const); Now, given this declaration in Delphi: procedure FreeAndNil(const [ref] Obj: TObject); The semantics of this are roughly equivalent to the above C++ code - a reference to a const pointer to a const TObject instance (I don't have 10.4 installed to look at how FreeAndNil() is actually declared in C++Builder). That is fine and good to get a pointer to a TObject-based instance passed into the function by reference, but normally Delphi would then not allow that pointer to be assigned a new value. So FreeAndNil() employs a workaround for that: TObject(Pointer(@Obj)^) := nil; Applying the @ operator to a reference parameter returns the address of the passed variable. Then all const-ness on that variable is being casted away, thus allowing the function to assign a new value to that variable. That would be something along the lines of the following in C++: ((TObject*&)(*(void**)&ref)) = nullptr; or reinterpret_cast<TObject*&>(const_cast<void*&>(*reinterpret_cast<const void * const *>(&ref))) = nullptr; or const_cast<TObject*&>(const_cast<const TObject * const &>(ref)) = nullptr; Except that if a C++ compiler sees a 'const TObject* const &' function parameter, and is passed anything other than a 'const TObject*' variable, an implicit conversion is performed and an rvalue gets passed to the reference parameter, so the function modifies the rvalue, not the caller's original variable. For example: https://ideone.com/CO4Dac So, there is likely some additional compiler magic in C++Builder to avoid this problem with FreeAndNil() specifically. Unless I'm missing something.
  14. Remy Lebeau

    When did Pos become PosEx?

    In D2007, there is no Offset parameter in Pos(), only in PosEx(). Here are the declarations from D2007: System.pas function Pos(const substr, str: AnsiString): Integer; overload; function Pos(const substr, str: WideString): Integer; overload; StrUtils.pas function PosEx(const SubStr, S: string; Offset: Integer = 1): Integer; For comparison, here are the declarations from XE2, before the Offset parameter was added to Pos(): System.pas function Pos(const SubStr, Str: ShortString): Integer; overload; function Pos(const SubStr, Str: UnicodeString): Integer; overload; function Pos(const SubStr, Str: WideString): Integer; overload; function Pos(const SubStr, Str: RawByteString): Integer; overload; System.StrUtils.pas function PosEx(const SubStr, S: string; Offset: Integer = 1): Integer; overload; System.AnsiStrings.pas function PosEx(const SubStr, S: AnsiString; Offset: Integer = 1): Integer; overload; And here are the declarations from XE3, when the Offset parameter was added to Pos(): System.pas function Pos(const SubStr, Str: _ShortStr; Offset: Integer = 1): Integer; overload; function Pos(const SubStr, Str: UnicodeString; Offset: Integer = 1): Integer; overload; function Pos(const SubStr, Str: _WideStr; Offset: Integer = 1): Integer; overload; function Pos(const SubStr, Str: _RawByteStr; Offset: Integer = 1): Integer; overload; System.StrUtils.pas function PosEx(const SubStr, S: string; Offset: Integer = 1): Integer; inline; overload; System.AnsiStrings.pas function PosEx(const SubStr, S: AnsiString; Offset: Integer = 1): Integer; inline; overload;
  15. Remy Lebeau

    TStrings properties

    Delphi XE Delphi 10.1 Berlin Delphi 8 or 2005, I'm not sure which (I don't have either to look at). It did not exist in Delphi 7, but did exist in 2005.
  16. Remy Lebeau

    How to make an "About" for a simple component?

    Well, then you are doing it differently than how the majority of other component developers do it. Components are meant to be implemented in run-time packages. If an application is built with run-time packages disabled, then the component's run-time package would be built statically into the application executable. Otherwise the run-time package would be linked into the application dynamically via BPL file. Either way, the application should not be linking in the actual component unit directly, it should be importing it from the run-time package, whether statically or dynamcially. The application itself would have a linkage to the same runtime package, and the compiler would choose the static or dynamic version of the run-time package as needed by the application's build settings. That means you run the risk of separate applications compiling against different copies of the component's PAS/DCU over time. Rather than installing a single copy in the IDE for it to link into each application as needed. Using a runtime package would also ensure the same version of the component that the IDE uses is the same version that is compiled/linked into each application. And, in the case where you DO want to enable run-time packages, the component's DCU won't be linked statically. Just because a component's source is compiled into a run-time package doesn't require the package to be DEPLOYED with the application, if the application has run-time packages disabled. The package may be compiled statically into the application instead. But, it does ensure that there is only 1 copy of the compiled component unit, in a place where it can be easily shared and reused as needed. Sure, you can do the same thing by simply configuring separate application projects to link in the component's DCU directly, but that is not the appropriate or typical setup. Maybe that is how YOU do it. But that is not how MOST PEOPLE do it. As it should be. But the component itself SHOULD be implemented in a run-time package that is shared by the design-time package and application projects, whether statically or dynamically, that doesn't change the fact that the component should be in a project for a run-time package.
  17. Remy Lebeau

    How to make an "About" for a simple component?

    From Borland/Embarcadero. A component used in an application can't be implemented in a design-time package, it must be implemented either in the application itself, or in a run-time package that is linked into the application. Why are you contesting that? I'm sure YOU know this, but not EVERYONE ELSE does. And since the OP is delving into component development, this is important for the OP to understand this, since this is how components are intended to be designed. Components go in run-time packages. Editors go in design-time packages. True, it is not, because it is in a design-time package instead, BY YOUR OWN EXAMPLE. A design-time package is NOT linked into an application, only the IDE itself uses a design-time package. So, how do you expect the application to link to the component unit when it is inside a design-time package? The application would have to static link to the unit directly instead, which would happen only if the unit's DCU or PAS file is on the application's search path, bypassing the design-time package entirely.
  18. Remy Lebeau

    When did Pos become PosEx?

    PosEx() was introduced in Delphi 7. Confirmed. The Offset parameter is not present in Pos() in XE2, but is present in XE3.
  19. Remy Lebeau

    Load form icon while using styles

    And you can see the future ... how, exactly? If nobody reports it, and nobody votes on it, then that is a likely certainty.
  20. Remy Lebeau

    How to make an "About" for a simple component?

    The OP asked to implement a component with additional design-time functionality. The actual component itself needs to be in a run-time package, it can't be in a design-time package. In the simple case where no design-time editors are being used, the run-time package and the design-time package can be the same package. But as soon as editors are involved, that is no longer the case. And you proposed using a component editor. So that means implementing the component in a run-time package, and the editor in a separate design-time package. TMyAwesomeComponent MUST be in a separate run-time package that the design-time package requires. A design-time package is not linked into a compiled executable, only a run-time package is, so if you were to place TMyAwesomeComponent onto a Form Designer at design-time, it will certainly be visible at design-time, and its editor would work as expected, but at runtime when the compiled executable is actually run, TMyAwesomeComponent won't be available so the Form will fail to load properly.
  21. Remy Lebeau

    Load form icon while using styles

    RecreateWnd() is used when ANY control, even a MainForm, needs a new HWND for ANY reason. This is most commonly used when applying new property values that are applied only when a new HWND is created, rather than using SetWindowLong() or similar API to update an existing HWND. But ReceiveWnd() is not limited to just those use cases, and is not specific to dynamic controls alone. And yes, you do know exactly when the HWND will be available - when the control's Handle property is accessed. RecreateWnd() merely signals a control to destroy its current HWND. A new HWND is then created the next time the Handle property is accessed afterwards.
  22. Remy Lebeau

    How to make an "About" for a simple component?

    You can't use design-time code in a runtime executable. A popup dialog that is meant to be viewed only at design-time needs to be in a design-time package only. Yes, a single package CAN be marked for both run-time and design-time usage, but when property/component editors are involved, you need to use separate run-time and design-time packages.
  23. Remy Lebeau

    How to make an "About" for a simple component?

    Why not just make that property be a simple String value? Easy to read, no popup dialog needed, and you can disable DFM streaming for that property if needed. But, if you really want a popup dialog, then you need to separate your code into run-time and design-time packages. The run-time package implements only the main component by itself, and it should have no concept of the popup dialog at all. The design-time package uses the run-time package, and implements the actual popup dialog, as well as a component editor to display that popup dialog when the user double-clicks or right-clicks on your main component. Indy implements both approaches, and Indy is pre-installed in the IDE w/ source code, so you can look at how Indy handles these steps: - In the IdBaseComponent.pas unit of the IndySystem run-time package, the TIdBaseComponent class, which is a base class that all Indy components derive from, has a public Version property that returns a static String from IdVers.inc specifying the compiled version number (the Version property could have been made published in the Object Inspector with a little extra work). - In the IdCoreDsnRegister.pas unit of the IndyCore design-time package, there is a TIdBaseComponentEditor class that is registered with the IDE at design-time using the RTL's RegisterComponentEditor() function, to display a popup dialog when double/right-clicking on any Indy component in the Form Designer. - Also in IdCoreDsnRegister.pas is a 3rd way that Indy displays its version number - in the IDE's own About dialog! When the IndyCore design-time package is loaded in the IDE, it registers some custom Strings with the IDE's About box using the IDE's OpenTools API, specifically the IOTAAboutBoxServices.AddPluginInfo() interface method.
  24. Remy Lebeau

    Load form icon while using styles

    Just FYI, you should call the form's RecreateWnd() method instead of issuing the CM_RECREATEWND message manually.
  25. Remy Lebeau

    One more memory leak and FastMM4

    The stack trace you showed is complaining that the character data for the UnicodeString returned by TJSONString.Value is what is being leaked. That UnicodeString is assigned to your private FToken variable. Which means the TBetFairApi object that is holding that UnicodeString is not freeing that UnicodeString properly. Either because itself is being leaked as well, or you have a bug elsewhere in your code that is overwriting the UnicodeString's internal pointer to the allocated character data. Either way, this is happening after TBetFairApi.GetToken() has exited, so the problem is not in TBetFairApi.GetToken() itself. The leak report is merely showing you the call stack leading up to the memory allocation that leaked, not the call stack leading up to the leak itself. That was a typo in my example. I meant to free only the TJSONValue that ParseJSONValue() returns, not the TJSONValue of the 'token' field. I have corrected that mistake in my earlier example.
×