Jump to content

Remy Lebeau

Members
  • Content Count

    2985
  • Joined

  • Last visited

  • Days Won

    134

Posts posted by Remy Lebeau


  1. DNS servers are just servers like any other. And the OS acts as a client like any other. Like any client/server system, servers can go down at times, network routes to servers can go down at times, etc. The Internet doesn't have 100% connectivity 100% of the time. Hiccups happen. All you can do is make sure your software is robust enough to handle network errors when (not if) they happen, retry operations as needed, and move on. 


  2. 42 minutes ago, dmitrybv said:

    "Just curious — how did you specify the calling convention cdecl? After all, DoGetValue doesn't contain any parameters for specifying the calling convention."

    I simply used the debugger at runtime to modify the CallingConvention parameter of the internal System.Rtti.Invoke() function that DoGetValue() calls for a getter method.  DoGetValue() is hard-coded to pass in a value of ccReg, I just changed that to ccCdecl instead, and then the AV went away.  That is at least a POC that the issue could be fixed if DoGetValue() were made to be more sensitive to the getter's actual RTTI. And likewise for DoSetValue(), too.

    • Thanks 1

  3. 10 hours ago, Stefan Glienke said:

    the code for getting properties does not know about the calling conventions  of their setter and getter (because that is not part of their RTTI but only the code pointer to them) and thus always assumes standard calling convention.

    Actually, calling convention is part of the RTTI for invokable types, including class methods. But TRttiInstanceProperty.DoGetValue() (and DoSetValue()) are ignoring that fact and just assume Delphi's default Register convention when invoking the property getter/setter.  If I change the invocation to specify Cdecl instead of Register, then no crash occurs.

     

    I have opened a bug ticket for this:

    RSS-3574: AV in TRttiProperty if getter/setter does not use Register calling convention


  4. 4 hours ago, Typer2 said:

     One safe workaround is to defer the font update. You can use a short-delay TTimer for this purpose

    Or, use TThread.ForceQueue() instead, eg:

    procedure TFprincipal.FontNameRClick(Sender: TObject);
    begin
      TThread.ForceQueue(nil,
       procedure
       begin
         ga_TipoLetra := FontNameR.Text;
         Font.Name := ga_TipoLetra;
         gn_FontSize := StrToInt(ComboSize.Text);
         Font.Size := gn_FontSize;
         Screen.MessageFont := Font;
         ToolBar.Font.Size := Min(gn_FontSize, 14);
      end);
    end;

     


  5. You are not checking if either TRttiContext.GetType() or TRttiType.GetProperty() are returning nil before accessing the objects they return.

    procedure TFormSimpleDraw2.Button2Click(Sender: TObject);
    var
      Value: TValue;
      LType: TRttiType;
      RttiContext: TRttiContext;
      CaretPositionProp: TRttiProperty;
      CaretPosition: TCaretPosition;
    begin
      RttiContext := TRttiContext.Create;
    
      LType := RttiContext.GetType(Memo1.ClassInfo);
      if LType = nil then Exit; // <-- add this
    
      CaretPosition := Memo1.CaretPosition;
    
      CaretPositionProp := LType.GetProperty('CaretPosition');
      if CaretPositionProp = nil then Exit; // <-- add this
      
      Value := CaretPositionProp.GetValue(Memo1);
      ...
    end;

  6. 1 hour ago, limelect said:

    @Remy Lebeau Obviously, it works, but using this, free gives an error

    D10,2,3

    Calling Free() on a nil pointer is perfectly safe. So that means you must be calling Free() on a non-nil pointer that is NOT pointing at a valid TBitmap object. Did you make the adjustments I showed you to give each TListItemData its own TBitmap object?

     

    Also, DO NOT use both OnClose and OnDeletion events to free the same memory. Use one or the other. The OnDeletion event is the preferred place to free custom data stored in a TListView, as it is called every time a TListItem is removed from the TListView, even during shutdown.

     

    I've used this technique many times in the past, so I know it works.

    1 hour ago, limelect said:
    
    
     

     


  7. All of your list items are pointing at a single TBitmap object in memory, so all of them will show the last image that was loaded from the DB.  If you want to show a separate image for each list item, they each need their own TBitmap object, eg:

    type
      PListItemData = ^TListItemData;
      TListItemData = record
        theString: string;
        ThePicture: TBitmap;
      end;
    
    ...
    
    procedure TMyForm.RunQueryAndFillListView;
    var
      ClipItem: TListItem;
      ListItemData: PListItemData;
    begin
      ...
      while not FDQuery2.Eof do
      begin
        ...
        ClipItem := lvClip.Items.Insert(0);
        New(ListItemData);
        try
          ListItemData.theString := s.Text;
          ListItemData.ThePicture := nil;
          if ContainsText(s.Text, 'Picture') then
          begin
            BlobField := FDQuery2.FieldByName('Image') as TBlobField;
            Stream := FDQuery2.CreateBlobStream(BlobField, bmRead);
            try
              ListItemData.ThePicture := TBitmap.Create;
              ListItemData.ThePicture.LoadFromStream(Stream);
            finally
              Stream.Free;
            end;
          end;
          ClipItem.Data := ListItemData;
        except
          ListItemData.ThePicture.Free;
          Dispose(ListItemData);
        end;
        FDQuery2.Next;
      end;
      ...
    end;
    
    ...
    
    // TListView.OnDeletion event handler
    procedure TMyForm.lvClipDeletion(Sender: TObject; Item: TListItem);
    var
      ListItemData: PListItemData;
    begin
      ListItemData := PListItemData(Item.Data);
      if ListItemData <> nill then
      begin
        ListItemData.ThePicture.Free;
        Dispose(ListItemData);
      end;
    end;

     


  8. First off, you posted a screenshot of code that appears to contain an actual live app password in it. I suggest you delete that screenshot, invalidate that app password, and generate a new one.  Don't EVER post live credentials to an online forum!

     

    Now then...

    44 minutes ago, Badnaf said:

    When I click the button to send the email, I get this error:

    Could not load SSL library

    What does Indy's IdSSLOpenSSLHeaders.WhichFailedToLoad() function report after the error has occurred?

    44 minutes ago, Badnaf said:

    I downloaded what I thought was the correct OpenSSL 1.0.2u (32-bit) from GitHub, but the files seem incorrect or too small (e.g., 1.3MB instead of 2.5MB).

    All of the DLLs on the GitHub page have been tested with Indy and known to be working.

    44 minutes ago, Badnaf said:

    Also, I couldn’t find any actual releases section on the OpenSSL GitHub page for the Indy-specific binaries.

    There isn't a releases section. That repo is just a collection of downloadable files.

    44 minutes ago, Badnaf said:

    Where can I get the correct OpenSSL 1.0.2u 32-bit (light) DLLs that work with Delphi and Indy?

    From https://github.com/IndySockets/OpenSSL-Binaries

    44 minutes ago, Badnaf said:

    Should the SSL DLLs be placed somewhere other than next to the .exe?

    No, that is the best place.  However, if you wanted to put them somewhere else, you can do that, too. You would just have to call Indy's IdSSLOpenSSLHeaders.IdOpenSSLSetLibPath() function at runtime to tell Indy where the DLLs are located.

     


  9. 10 hours ago, apachx said:

    Yes, at first I tried calling InAppPurchase.RestorePurchasedProducts instead of InAppPurchase.QueryProducts, but the event InAppPurchasePurchaseRestored(Sender: TObject; const ProductID: string; const Receipt: string) on Android never fires.

    There is no OnPurchaseRestored event in TInAppPurchase.  Did you mean OnPurchaseCompleted instead?

    10 hours ago, apachx said:

    As for InAppPurchase.QueryPurchases — this method is not available for direct use in TInAppPurchase.

    Oh. QueryPurchases() is an internal method that is called by another internal method QueryInventory() which is called only at the end of setting up a new connection to the BillingClient.


  10. 57 minutes ago, apachx said:

    Hi. I just tried your solution, but it seems it's not working. After successfully completing a subscription, every attempt to call InAppPurchase.QueryProducts fails to return my subscription in the Products list within the event InAppPurchaseProductsRequestResponse(Sender: TObject; const Products: TIAPProductList; const InvalidProductIDs: TStrings).

    This condition never evaluates to true:

    
    for Product in Products do
        begin
            if (Product.ProductID = PlayMarketProductID) and (InAppSubscription.IsProductPurchased(PlayMarketProductID)) then
                    SUBSCRIPTION := true;  
        end;

      

    That is because you are clearing the FPurchaseMap before then querying the Products instead of querying the Purchases.  IsProductPurchased() looks at FPurchaseMap, but QueryProducts() populates FProductDetailsMap and not FPurchaseMap.  QueryPurchases() populates FPurchaseMap.

     

    Also, there is no OnPurchasesRequestResponse event for when QueryPurchases() completes.  It triggers the OnSetupComplete event instead.  Which implies QueryPurchases() was not intended to be used outside of initial component setup, since TInAppPurchase tracks purchase updates in real-time.

    • Like 2

  11. 14 hours ago, Peter C said:

    Even if I take the Thread Instance out and replace MyThread.Start with  TDirectory.Copy( MASTER_PATH, BACKUP_PATH ) the same issue occurs.

    Have you tried calling the overloaded version of TDirectory.Copy() that takes an IgnoreErrors parameter and set it to False? The overload of TDirectory.Copy() that you are calling implicitly uses IgnoreErrors=True.  When IgnoreErrors=False then a failure to copy files will raise an EInOutError exception, containing a list of all the files that failed to copy and the reason(s) why they couldn't be copied.  Are you getting that exception?


  12. On 5/18/2025 at 11:47 PM, apachx said:

    Just in case, this limitation of the TInAppPurchase component has already been reported in a ticket on the Embarcadero Quality Portal - https://embt.atlassian.net/servicedesk/customer/portal/1/RSS-3516.

    Yes, as a result of this post on StackOverflow a few day ago:

    How to get updated subscription status after cancellation without restarting the app (Google Play Billing in Delphi)?

    On 5/18/2025 at 11:47 PM, apachx said:

    Who knows - maybe the developers will eventually do something about it.

    Hopefully, considering they did confirm on the ticket that it is a problem.

    9 hours ago, Chris Pim said:

    I believe the following should do what you need, but I've been unable to test this at the moment so can you please try and let me know if it works.

    Rather than clearing the whole Inventory, I was thinking it could instead just remove individual items that are no longer in the queried purchase list.

    9 hours ago, Chris Pim said:

    The component won't automatically re-query the products when a purchase changes (the billing API doesn't have this facility as far as I can tell)

    True, it won't query the whole list.  But it does listen for new purchases in real-time and add them individually to the Inventory.  But I don't think it listens for subscription changes, that is a whole different workflow that the component is not setup to handle.


  13. 8 hours ago, pyscripter said:

    I would like to share the following in case you encounter the same issue.

    ...

    Actually, this is not entirely correct. 

    ...

    So it appears that if a class helper is in scope where a class is defined, it is used unconditionally in all units of a project.  If not, then what it is stated in the documentation applies.

    Did you report the problem to Embarcadero? It is either an error in the documentation, or a bug in the compiler.


  14. 3 hours ago, dwrbudr said:
    
    procedure TFormPopup.CreateParams(var Params: TCreateParams);
    var ClassRegistered: boolean;
        TempClass: TWndClass;
    begin
      inherited CreateParams(Params);
    
      ClassRegistered := GetClassInfo(Params.WindowClass.hInstance, Params.WinClassName, TempClass);
      if ClassRegistered then
      begin
        Winapi.Windows.UnregisterClass(Params.WinClassName, Params.WindowClass.hInstance);
      end;
    
      If FIsBorderlessPopup then
        begin
          Params.Style := WS_POPUP;
          Params.WindowClass.style := Params.WindowClass.style or CS_DROPSHADOW;
          Params.ExStyle := WS_EX_TOPMOST;
      end;
    end;

     

    You should not need to resort to this.  The VCL already calls UnregisterClass() and RegisterClass() after CreateParams() exits.  The purpose of CreateParams() is just to report the class details that the control wants, not to manipulate the registration directly.


  15. That is a very dangerous approach. Don't do it that way. First, there is no guarantee that accessing the ClassName on an invalid object will raise an exception. And second, reading from invalid memory may cause other side effects (ie, page faults, etc).

     

    Since your integers are very small, then you could simply look for integers first, and treat higher values as objects since they should never reside at such low memory addresses, eg:

    const
      MaxObjIntValue = 8;
    ...  
    var value := NativeInt(Objects[c,r]);
    if (value >= 0) and (value <= MaxObjIntValue) then
    begin
      // is an integer, use value as needed...
    end
    else
    begin
      // is an object, use TObject(value) as needed...
    end
    ...
    if NativeInt(Objects[c,r]) > MaxObjIntValue then
      Objects[c,r].Free;

     

    • Like 1

  16. 2 minutes ago, steelha said:

    I hope this code help you

    ...

    just pass the file path.  Can change Tbimap.create for TJPEGImage.Create; or  TPngImage.Create;,

    imgFLogin its the image timage visual component you want load the image 

    How does that code have anything to do with the discussion at hand?

     

    Also, why would you not simply use TImage.Picture.LoadFromFile() and let it handle everything for you?

    procedure LoginImage(const RutaBase: string);
    var
      filename : string;
    begin
      // Intentar cargar como BMP
      filename := IncludeTrailingPathDelimiter(RutaBase) + 'flogin.bmp';
      if FileExists(filename) then
      begin
        imgFLogin.Picture.LoadFromFile(filename);
      end
      else
      begin
        ShowMessage('File not found')
      end;
    end;

     


  17. Yes, and this is documented behavior:

    https://docwiki.embarcadero.com/Libraries/en/Vcl.Graphics.TPicture.Bitmap

    Quote

    Use Bitmap to reference the picture object when it contains a bitmap. If Bitmap is referenced when the picture contains a Metafile or Icon graphic, the graphic won't be converted (Types of Graphic Objects). Instead, the original contents of the picture are discarded and Bitmap returns a new, blank bitmap.

    Where it says "a Metafile or Icon graphic", it really means "any non-Bitmap graphic" instead.

     

    Use the TPicture.Graphic property when you need to access as-is whatever TGraphic descendant is currently loaded in the TPicture.  Use the TPicture.Bitmap property instead when you specifically need a TBitmap.


  18. 2 hours ago, Squall_FF8 said:

    1. How to check is the TImage empty (aka no image loaded?

    Check the TImage.Picture.Graphic and TGraphic.Empty properties:

    if (Image1.Picture.Graphic = nil) or Image.Picture.Graphic.Empty then
    2 hours ago, Squall_FF8 said:

    2. How to erase the loaded image? (when an image is loaded)

    You can assign nil to the TImage.Picture property:

    Image1.Picture := nil;
    // same as:
    // Image1.Picture.Assign(nil);

    Or to its Graphic property:

    Image1.Picture.Graphic := nil;

     

    • Like 2
    • Thanks 1

  19. On 5/12/2025 at 4:18 PM, Dave Novo said:

    If you are using low valued integers, you could inspect the value of the objects[x,y] and if the value is below some threshold, then do not free it.

    Another option might be to use tagged pointers.

     

    Normally, objects are aligned in memory in such a way that certain bits in object pointers are always zero, so those bits can be repurposed if you are careful.

     

    For instance, if you limit your integer values to 31 bits (x86) or 63 bits (x64), you can use an unused bit in an object pointer to flag whether the pointer holds an integer value vs an object address, and just mask off the bit when extracting the value.

     

    For example:

    // this assumes objects are never stored at an odd-numbered memory address..
    
    var intValue: Integer := ...;
    Objects[c,r] := TObject((NativeUInt(intValue) shl 1) or $1);
    ...
    var objValue: TObject := ...;
    Objects[c,r] := objValue;
    ...
    if (NativeUInt(Objects[c,r]) and $1) <> 0 then
    begin
      // is an integer...
      var intValue := Integer(NativeUInt(Objects[c,r]) shr 1);
      ...
    end
    else
    begin
      // is an object...
      var objValue := Objects[c,r];
      ...
    end;
    ...
    if (NativeUInt(Objects[c,r]) and $1) = 0 then
      Objects[c,r].Free;

     

    • Like 1

  20. 5 hours ago, alogrep said:

    Perhaps the integer type object does not need to be freed.

    Correct.

    5 hours ago, alogrep said:

    But then how do I inpect if the object is of integer type or not?

    You can't. So, either store all objects only, or all integers only. Don't mix types. But, if you must, then you'll need to either wrap the integers inside of objects, or add an extra header in front of each value to identify its type, etc.

    • Like 2

  21. 2 hours ago, PeterPanettone said:

    when I use my previous code version:

    
    const
      {$IFNDEF PROCESS_QUERY_LIMITED_INFORMATION} 
      PROCESS_QUERY_LIMITED_INFORMATION = $1000;
      {$ENDIF}

    ...then I can compile/build my app

    It will compile, but it won't behave as you are expecting.

     

    PROCESS_QUERY_LIMITED_INFORMATION is not a compiler conditional via a {$DEFINE} statement, "-D" compiler switch, or "Conditional Defines" list in the project options, thus:

    {$IFDEF PROCESS_QUERY_LIMITED_INFORMATION}  // will always evaluate as false
    {$IFNDEF PROCESS_QUERY_LIMITED_INFORMATION} // will always evaluate as true

    Regardless of whether PROCESS_QUERY_LIMITED_INFORMATION has been declared as a constant in a unit that your code uses.

     

    That is where {$IF (NOT) DECLARED} comes into play, as it supports declarations of types, constants, variables, etc.  Not to be confused with {$IF (NOT) DEFINED}, which is the {$IF} version of {$IF(N)DEF} and thus supports only compiler conditionals.

    const
      PROCESS_QUERY_LIMITED_INFORMATION = $1000;
    
    {$IF DECLARED(PROCESS_QUERY_LIMITED_INFORMATION)}     // will evaluate as true
    {$IF NOT DECLARED(PROCESS_QUERY_LIMITED_INFORMATION)} // will evaluate as false
    
    {$IF DECLARED(PROCESS_QUERY_DOESNT_EXIST)}     // will evaluate as false
    {$IF NOT DECLARED(PROCESS_QUERY_DOESNT_EXIST)} // will evaluate as true
    2 hours ago, PeterPanettone said:

    but when I run it from the IDE, I get F2084 Internal Error: AV50A9A35E(509E0000)-R00000000-0 shown in the Structure view (without affecting my app at runtime).

    That is a compiler/IDE bug that would need to be reported, with example to reproduce it.  I don't get that error in my tests.

    2 hours ago, PeterPanettone said:

    So I now simply declare the variable without any compiler conditionals, which resolves all compiler problems.

    Sure, though you would be using your own constant declared in your own unit, and not using a constant declared in the Winapi.Windows unit if Embarcadero ever decides to add it in at a later time.

    • Like 1
×