-
Content Count
3060 -
Joined
-
Last visited
-
Days Won
139
Everything posted by Remy Lebeau
-
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.
-
How to increase the distance between TCheckBox glyph and caption text?
Remy Lebeau replied to PeterPanettone's topic in VCL
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. -
One more memory leak and FastMM4
Remy Lebeau replied to Alberto Paganini's topic in RTL and Delphi Object Pascal
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. -
FreeAndNil 10.4 vs 10.3.1 and Pointers
Remy Lebeau replied to Sherlock's topic in RTL and Delphi Object Pascal
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). -
FreeAndNil 10.4 vs 10.3.1 and Pointers
Remy Lebeau replied to Sherlock's topic in RTL and Delphi Object Pascal
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". -
FreeAndNil 10.4 vs 10.3.1 and Pointers
Remy Lebeau replied to Sherlock's topic in RTL and Delphi Object Pascal
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. -
When did Pos become PosEx?
Remy Lebeau replied to Angus Robertson's topic in RTL and Delphi Object Pascal
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; -
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.
-
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.
-
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.
-
When did Pos become PosEx?
Remy Lebeau replied to Angus Robertson's topic in RTL and Delphi Object Pascal
PosEx() was introduced in Delphi 7. Confirmed. The Offset parameter is not present in Pos() in XE2, but is present in XE3. -
And you can see the future ... how, exactly? If nobody reports it, and nobody votes on it, then that is a likely certainty.
-
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.
-
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.
-
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.
-
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.
-
Just FYI, you should call the form's RecreateWnd() method instead of issuing the CM_RECREATEWND message manually.
-
One more memory leak and FastMM4
Remy Lebeau replied to Alberto Paganini's topic in RTL and Delphi Object Pascal
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. -
And that Community Edition of 10.4 doesn't exist yet. I think Embarcadero needs to give him a free license, for all the work he does to fix their issues. And then get him into the betas and make sure he finds broken things so they can fix them BEFORE they release!
-
One more memory leak and FastMM4
Remy Lebeau replied to Alberto Paganini's topic in RTL and Delphi Object Pascal
DO NOT free the TJSONValue that is returned by TJSONPair.JsonValue or TJSONObject.Values[]. You do not own that TJSONValue, so you are not responsible for freeing it. The parent TJSONObject owns it, and will free it for you when itself is freed. The only JSON object you should be freeing manually in this code is the TJSONValue that is returned by TJSONObject.ParseJSONValue(). You can also remove manual freeing of the Indy SSL and Log objects by utilizing TComponent ownership semantics. Thus, the only objects you really need to free manually in this code are the TIdHTTP and TStringList objects, eg: function TBetFairApi.GetToken: Boolean; var LJSONObject: TJSONObject; LJSONValue, LJSONToken: TJSONValue; {$IFDEF VER230} LJSONPair: TJSONPair; {$ENDIF} IdHTTP: TIdHTTP; IdSSL: TIdSSLIOHandlerSocketOpenSSL; IdLogFile: TIdLogFile; sList: TStringList; begin Result := False; FToken := ''; IdHTTP := TIdHTTP.Create(nil); try IdHTTP.HTTPOptions := IdHTTP.HTTPOptions + [hoForceEncodeParams]; IdHTTP.HandleRedirects := True; IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdHTTP); IdSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]; IdHTTP.IOHandler := IdSSL; IdLogFile := TIdLogFile.Create(IdHTTP); IdLogFile.Filename := 'c:\' + ChangeFileExt(ExtractFileName(ParamStr(0)), '.log'); IdLogFile.Active := True; IdHTTP.Intercept := IdLogFile; IdHTTP.Request.Accept := 'application/json'; IdHTTP.Request.CustomHeaders.Values['X-Application'] := cBetfair_AppKey; IdHTTP.Request.ContentType := 'application/x-www-form-urlencoded'; sList := TStringList.Create; try sList.Add('username=' + FUserID); sList.Add('password=' + FPassword); LJSONValue := TJSONObject.ParseJSONValue(IdHTTP.Post(URL_LOGIN, sList)); try if LJSONValue is TJSONObject then begin LJSONObject := TJSONObject(LJSONValue); {$IFDEF VER230} LJSONPair := LJSONObject.Get('token'); if LJSONPair <> nil then LJSONToken := LJSONPair.JsonValue else LJSONToken := nil; {$ELSE} LJSONToken := LJSONObject.Values['token']; {$ENDIF} if LJSONToken <> nil then FToken := LJSONToken.Value; end; finally LJSONValue.Free; end; finally sList.Free; end; finally IdHTTP.Free; end; Result := (FToken <> ''); end; -
Managed records have Initialize() and Finalize() operators that users can override. But that also means they have to be called automatically when the record enters and leaves scope. So, just like other managed types, there is an hidden try..finally to ensure Finalize() is called, yes. That is not the case with unmanaged records. Inline variables begin and end their lifetime in the same scope that they are declared in. So that is where their initialization and cleanup is performed.
-
Not on all platforms. On iOS 64bit and Linux 64bit, Longint is 8 bytes where Integer is 4 bytes.
-
Unless the result of FPosition + Count is higher than High(Int64), in which case the result will wrap to a negative value. Granted, that is unlikely to happen, but it is still a possibility with signed integer arithmetic. Perhaps, but Seek() is a very low overhead operation in a memory stream, so this is not really an issue. Then you should take this up with Embarcadero directly, since they are the only ones who can actually change it.
-
Converting project from Delphi 2006 to Delphi 10.2
Remy Lebeau replied to RTollison's topic in General Help
Not equivalent as-is, no. However, StrPas() converts a null-terminated C-style string to a Delphi string. A StrLPas()-like function would convert a C-style string to a Delphi string up to a given length or the null terminator, whichever is reached first. You can easily write your own StrLPas() function using SetString(), eg: function StrLPas(const Str: PChar; const MaxLength: Integer): String; var L: Integer; P: PChar; begin P := Str; L := 0; while (P^ <> #0) and (L < MaxLength) do begin Inc(P); Inc(L); end; SetString(Result, Str, L); end; -
Converting project from Delphi 2006 to Delphi 10.2
Remy Lebeau replied to RTollison's topic in General Help
SetString copies EXACTLY the length you specify, no more, no less. It is roughly equivalent to: var S: String; //SetString(S, Source, Length); SetLength(S, Length); Move(Source^, Pointer(S)^, Length * SizeOf(Char));