Jump to content

PeterBelow

Members
  • Content Count

    468
  • Joined

  • Last visited

  • Days Won

    13

Posts posted by PeterBelow


  1. 11 hours ago, Anders Melander said:

    That typically doesn't stick. For some reason Delphi enables the packages again sooner or later. It's been like that for as long as I can remember.

    The package list is specific for the active project. To change the default you have to edit the package list with no project or project group open, and that will still only apply to new projects you create after that.


  2. You can do this for any class hierarchy you wrote yourself, but doing it for any class starting with TObject would be difficult without patching routines of the run-time library.  The full version of FastMM4 has more options for tracing memory leaks that the one used in the Delphi RTL, I think, but I have never had to investigate this myself. Even if you manage to do it somehow you will drown in data; any even moderately complex program will create and delete tens of thousands of objects during a program run, and most of the collected data will be totally useless for figuring out where in your code the problem point is located.

     

    Are you trying to track a particular problem? If so, give us more detail, perhaps there is a more focused way to handle that.


  3. 1 minute ago, Mike Torrettinni said:

    Thank you, I know sometimes it's hard to give advice on my questions - sometimes they are more a discussion than problems looking for solution.

     

    Common form with frames, and it was useful. OK. Now, as you look back, would you do it same or similar way, or you would choose different strategy for same feature?

    If I build an application using the same framework I would definitely use the same approach again. In fact I could reuse much of the old code 1:1 in this case since the dialog and its frames are completely independent of the actual data classes to search.


  4. On 5/17/2019 at 1:04 PM, Mike Torrettinni said:

    Interestingly enough, nobody is suggesting to reduce the number of forms/frames. Odd. It's almost like when you have 1 class per unit, no matter how small the class is. I'm not there, yet 🙂

    I re-checked and I have 19 Search forms... they are all modal forms that hover over data view and the behavior is the same: - search form is opened on top center of data view (Virtual treeview) and locates searched line; - Next/Prev works - search logic is very similar in all of the search forms, but on different data.

    I need even more search forms, so I had this idea of single search form. I guess you guys are advising against this approach.

     

     

    It all depends on how your application is constructed. In the largest one I ever wrote I used data classes with an ORM on the back to interact with the database. The application had a number of views to shows list of different kinds of those data objects (which represented the entities in the data model), and each of these views had a search function. They all used a common search dialog, which was configured at run-time using a configuration interface, which had implementations for each of the searchable data classes.  The dialog contained frames for the user to specify the search critertia, each of which had a dropdown list with the available searchable properties, applicable operators, and input controls (dynamically adjusted according to property type) to specify the criteria. The dialog build the WHERE clause to use from the input, which the user could modify to change the default AND combination used if necessary. Worked quite well, the work required to build the configuration interface was not that large since the dialog could use the data classes' metadata to construct the property list, figure out the applicable operators and input controls needed. The interface just told it which data class to configure itself for, which properties to exclude from the dropdown, and which values to offer for selection for properties with a restricted list of possible values.

     

    snapshot.PNG

    • Like 1

  5. Just an idea:

    The IShellBrowser interface has a method

    GetControlWindow

    which you can use to get the window handle of a control inside the browser.  You should be able to  use the GetAncestor API function with the GA_ROOT flag with this handle to find the Explorer window itself, and then you can use the SetForegroundwindow API function on that to bring it to front.


  6. 21 hours ago, Mark Williams said:

    Afraid to say "yes". It's used in a document database where the documents can be a random assortment and often poorly titled. Trying to sift through the documents by categories often doesn't work well and the only way to find what you want may be by way of a laborious scroll through the table (even item by item), tedious as it may sound.

     

    Your reply also seems to query the point of the virtual paradigm. Why have the ability to host millions of nodes if there is never any point in hosting them?

     

    I could load 250/500/1000 items a time and have a button "Get Next 1000", but I don't wish to do it that way. I would prefer (and believe it is also better from a usability stand point) to have a tree that purports to provide access to all the relevant documents simply by scrolling and allows the user to get to the point they want to be by scrolling through the tree quickly. Having to do so in chunks is in my view clunky and obstructive. VirtualTreeView, as I have always understood it is the remedy to such clunkiness: if only I could figure out how!

    What I would do in your case is to load only the document title (and the primary key  plus perhaps a submission date, but only the minimum of data you need for display and to later fetch the full record) for all records at first, perhaps ordering the result by submission date descending or alphabetically. The resulting data would be stored client-side in a suitable data structure, and the tree would show it from that structure. Only when the user indicates that he wants to examine a document in more detail would you fetch the actual document content from the server and display it. 

     

    This way your first query should still be fast enough to not cause a too noticable delay; even if it returns 30,000 rows each of the rows would be fairly short, so the total amount of data transferred is not that large.

     

    I'm not familiar with this VirtualTreeview control, but with such virtual controls the time saver is not having to load all the data into the control in one operation. But you still need to know, up front, how many "records" your control has to display, so you can create the necessary number of virtual nodes, and you need to have the data available somewhere, to be able to provide it when the control asks for it. If the ultimate source of the data is a database you need not only the number of records the query you use returns, you also need some piece of data for each of the records that allows you to tie each of the virtual nodes to a record in the database unambiguously, usually a primary key. Only then can you fetch the full data for a node when you need it for display. 

     

    A database is usually not like a sequential file or list, there is no convenient way to partition a query result set into "pages". Even though some SQL databases support the ANSI SELECT syntax with OFFSET and FIRST ROWS in some manner these are very inefficient queries, since the server still has to compose the full result set on each query, it just returns only part of the resulting rows.

     

    Also keep in mind that such list-type controls have a vertical scrollbar. The user can frustrate your attempts at reading the data in chunks sequentially by grabbing the scrollbar thumb and dragging it down, ending up somewhere way below the current page you have laboriously fetched before. You can only react fast to that if you have the data needed to display already stored in some data structure on the client side.

     

    In summary: using this kind of display does not work well with the classical RAD "drop a query and tied that to a databound control" way of design. You are much better served by using a list of objects for the client-side data storage. You fill that with objects holding only the minimum of data at first (primary key and a display string, e.g. your document title) and tie the objects to the virtual nodes of your UI. You fetch the rest of the data only when it is needed for display. If you grow ambitious after having that working you can then use a secondary thread with a second conncection to the database to fill the objects corresponding to the next visual page of the control in background, while the user is still staring at the display of the current page.


  7. 9 hours ago, Mark Williams said:

    I am using TvirtualTree for data display where there can be large numbers of records in a database table (upwards of 30,000). 

     

    Whenever I see such a statement I have to wonder: has the poster ever actually tried to work with a list that large? It is completely useless as an UI element in my opinion, nobody in his right mind wants to scroll around in such a list to find something he is looking for. Give your user a decent search function and show only the database records matching the search criteria. At least, if that is possible, devide the "data space" into chunks of manageable size, e.g all records where a person name starts with the same letter, then allow the user to select the letter to look for.

    • Like 2

  8. 10 minutes ago, chkaufmann said:

    I need a function like stripcslashes() in PHP. Is there something similar in Delphi or do I have to write it on my own.

     

    Christian

    The PHP function converts escaped characters like \n to their actual character (chr(13) in this case), no? Delphi has nothing like that since there is no need to escape characters in Delphi strings, they are all UTF-16 by default. So you have to write your own, I think.


  9. Modifying the DPR file's body can be done safely, if you know what you are doing and where the IDE may step on your modifications 😉.

    In my experience is best to just not add reams of code to the main block directly but only the absolute minimum you get away with, usually a single call to a function that then does the brunt of the work. This has never caused a problem for me if said line was added directly after the Application.Initialize line. What the IDE modifies are the uses clause and the lines to create the autocreated objects (after the first Application.CreateForm line). Additions you make there may get wiped out, so don't touch these sections of the dpr file. Adding procedures, functions, types, constants, even classes to the dpr file works without problems otherwise, although it is a bad idea in my opinion.


  10. 8 hours ago, johnnydp said:

    Hi,

     

    Looking for some realiable method of making screenshot

    BitBlt give sometimes blank screen of desktop 

    Can anyone post working code of other method 

    Speed is also important, I realize it might be little slower

    One way that is fairly reliable is to fake a press of the print screen key. The main problem is that you have to get the image from the clipboard, and that of course overwrites any previous content.

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      Clipboard.Clear;
      PostKeyEx32(VK_SNAPSHOT, [], false);
      Application.ProcessMessages;  // 'ware timers!
      if Clipboard.HasFormat(CF_BITMAP) then
        image1.Picture.Bitmap.Assign(Clipboard)
      else
        label1.Caption := 'No image!';
    end;
    
    {!
    <summary>
     PostKeyEx32 uses keybd_event to manufacture a series of key events
     matching the passed parameters.</summary>
    <param name="key">
     is the virtual keycode of the key to send. For printable keys this is
     simply the ANSI code (Ord(character)). </param>
    <param name="shift">
     encodes the state of the modifier keys. This is a set, so
     you can set several of these keys (shift, control, alt, mouse buttons)
     in tandem. The TShiftState type is declared in the Classes unit.</param>
    <param name="specialkey">
     normally this should be False. Set it to True to pecify a key on the
     numeric keypad, for example.</param>
    <remarks>
     The events go to the control with focus, even if it is part of another
     process.
     Note that for characters key is always the upper-case version of
     the character. Sending without any modifier keys will result in
     a lower-case character, sending it with [ssShift] will result
     in an upper-case character!  </remarks>
    }
    procedure PostKeyEx32(key: Word; const shift: TShiftState;
      specialkey: Boolean);
    type
      TShiftKeyInfo = record
        shift: Byte;
        vkey: Byte;
      end;
      byteset = set of 0..7;
    const
      shiftkeys: array[1..3] of TShiftKeyInfo =
      ((shift: Ord(ssCtrl); vkey: VK_CONTROL),
        (shift: Ord(ssShift); vkey: VK_SHIFT),
        (shift: Ord(ssAlt); vkey: VK_MENU));
    var
      flag: DWORD;
      bShift: ByteSet absolute shift;
      i: Integer;
    begin
      for i := 1 to 3 do begin
        if shiftkeys[i].shift in bShift then
          keybd_event(shiftkeys[i].vkey,
            MapVirtualKey(shiftkeys[i].vkey, 0),
            0, 0);
      end; { For }
      if specialkey then
        flag := KEYEVENTF_EXTENDEDKEY
      else
        flag := 0;
    
      keybd_event(key, MapvirtualKey(key, 0), flag, 0);
      flag := flag or KEYEVENTF_KEYUP;
      keybd_event(key, MapvirtualKey(key, 0), flag, 0);
    
      for i := 3 downto 1 do begin
        if shiftkeys[i].shift in bShift then
          keybd_event(shiftkeys[i].vkey,
            MapVirtualKey(shiftkeys[i].vkey, 0),
            KEYEVENTF_KEYUP, 0);
      end; { For }
    end; { PostKeyEx32 }

     


  11. 20 hours ago, A.M. Hoornweg said:

    Unfortunately tCustomgrid has no methods beginupdate / endupdate.

     

    You can send a WM_SETREDRAW message to the control to block painting, but that may not affect the updating of the scrollbar range.


  12. 4 minutes ago, A.M. Hoornweg said:

    I have found the core of the issue.

     

    When a form receives a wm_dpichanged message, TCustomForm.WMDpiChanged is called which scales all components on the form. 

     

    But TCustomGrid does it in a terrible way;  there's this simple loop that modifies the heights of the rows:

     

    [vcl.grids, TCustomGrid.ChangeScale(), line 1735]

        for I := 0 to RowCount -1 do
          RowHeights := MulDiv(RowHeights, M, D);

     

    and in the debugger I see that this loop needs a whopping 72 seconds to complete on my Core i7 notebook (for 86400 rows).

     

    The reason? It appears that the changing of a single row height triggers an InvalidateGrid() plus an update of the scroll bars (which, incidentally, is why my breakpoints kept landing in USER32.SetScrollInfo)

    And all of this happens inside the handling of a wm_dpichanged message, so the message queue is blocked for ages which freezes my desktop. 

     

     

    Excellent analysis!

    Would you please log a bug report for this problem on quality.embarcadero.com?


  13. 13 hours ago, Alberto Miola said:

    I have to create a Delphi program and since it's going to run only under Windows, I'm going to use VCL instead of FMX. This is an example of a program that I have made using another library (it's called Qt, it uses C++).

    Check this picture: https://i.imgur.com/6owIISN.png

     

    I'd like to do something like this with VCL. Basically the picture shows that there is a black container and I can put inside it the green rectangle (some objects with an associated "view"). How can this be done with VCL?

     

    I have thought that I could create a Frame, put on it the 2 buttons and a label. But then how can I place the frames in the listbox? Do I have to switch to FMX?

     

    Thanks

     

     

    Instead of a listbox, use the VCL TScrollbox as container for the frames.


  14. 2 hours ago, Jacek Laskowski said:

    I have two classes:
     

    
    TFoo = class
       procedure Terminate(aNow: Boolean); virtual;
    end;
    
    TBar = class(TFoo)
       procedure Terminate();
    end;
    
    procedure TBar.Terminate();
    begin
       inherited Terminate(True);
    end;

    but I am getting a warning when compiling:
     

    
    [dcc32 Warning] W1010 Method 'Terminate' hides a virtual method of base type

    How to correctly declare the Terminate () method?

     

    The classical way to handle this is to refactor TFoo like this:

     

    
    TFoo = class(TObject)
    strict protected
      procedure DoTerminate(aNow: Boolean); virtual;
    public
      procedure Terminate; virtual;
    end;
    
    TBar = class(TObject)
    public
      procedure Terminate; override;
    end;
    
    procedure TFoo.Terminate;
    begin
      DoTerminate(false);
    end;
    
    procedure TBar.Terminate;
    begin
      DoTerminate(true);
    end;

     

     


  15. This is the kind of problem the IDE's repository was created for. You do not need a wizard for this, just create a template project to serve as the starting point for new projects, and add it to the repository. It is then available in the repository dialog (File -> New -> Others), pick it, immediately do a file -> save project as and start to work on it. For teams of several developers a shared repository can be created on a network path.

     

    If that is not flexible enough for your purpose I would not create a IDE wizard for this task but a stand-alone program you can tie into the Tools menu as an external helper program. This way the tool is not specific to a particular RAD Studio version, you do not run the risk of destabilizing the IDE due to errors in using the (poorly documented) OpenTools API, and it is easier to maintain. The tool would simply ask for a new folder for the project to create and then create the required files to start with there.

    • Like 2

  16. 21 hours ago, Jacek Laskowski said:

    I have simple table:

     

    
    CREATE TABLE WORKSTATION
    (
      IDWORKSTATION        INTEGER,
      MACHINEID            VARCHAR(32)
    );

    I create TFDQuery with simple select SQL:

     

    
    SELECT * FROM WORKSTATION w
    WHERE W.MACHINEID = :PARAM

     

    When I set as param string longer than 32 chars then I get error:

     

    
    "arithmetic exception, numeric overflow, or string truncation"

     

    I set StrsTrim2Len option to True:

     

    http://docwiki.embarcadero.com/Libraries/Rio/en/FireDAC.Stan.Option.TFDFormatOptions.StrsTrim2Len

     

    ...but the error still occurs.

     

    How to properly config Firedac to eliminate this problem?

    This is, in my opinion, a programmer error, not a problem with the framework. You are using data that does does not fit the declaration of the database table. In such an occasion I would expect the framework to throw an error, since bad input should definitely not be swept under the carpet but brought to the user's attention. So, if the value you use for PARAM comes from an edit control filled by the user, the MaxLength property of that control needs to be set to the length of the database field the input is destined to be used with, so bad length input is simply not possible. If that is impractical you have add your own checks for the input before it is used, and produce a sensibel error message, e.g. "A machine ID cannot be longer than 32 characters.".

     

    Just my 2 Euro-cents...

    • Like 3

  17. I think doing a File -> Save all also saves the list of open modules. The dsk file is basically only legacy, the relevant data is also saved to registry keys.

    You do have the autosave option for the project desktop enabled in the Tools--> Options dialog, I assume?


  18. 10 hours ago, David Schwartz said:

    I'm curious about something that I've run into a few times, and I've never figured out how to solve it nicely. I'm wondering if there are any good solutions.

     

    Basically, I have a couple of things derived from TDataset: TOracleDataset and TPgQuery. Both of them are essentially TQuery variants. The problem is, I guess there are some shortcomings with the design of TQuery such that nobody ever derives query components from it. Or maybe it's that TQuery is too restrictive for their needs.

     

    Regardless, I want to be able to pass a parameter to a function that takes instances of either one of these. The problem is, they both have a SQL property that's not in the TDataset class they inherit from (along with some other things).

     

    procedure Form1.Func1( aQry : <<what_goes_here?>> );

     

    I want to be able to call Func1( aOracleQry ) as well as Func1(aPgQry).

     

    But since TDataset doesn't have an SQL property, if I use that as the parameter type, I have to use code like this:

    
    procedure Form1.Proc1( aQry : TDataset );
    begin
    . . .
      aQry.Close;
      if (aQry is TOracleDataset) then
        TOracleDataset(aQry).SQL.Text := 'select * from xyz'
      else
        TPgQuery(aQry).SQL.Text := 'select * from xyz';
      aQry.Open;
    . . .
    end;

    I don't want the function to have to know what kinds of datasets might be passed. That's what inheritance and polymorphism are supposed to handle.

     

    What I want to simply say is something like this:

    
    procedure Form1.Proc1( aQry : TSomething );
    begin
    . . .
      aQry.Close;
      aQry.SQL.Text := 'select * from xyz';
      aQry.Open;
    . . .
    end;

    The actual variables themselves DO have an SQL : Strings property, but their common ancestor class does not, and I have no way of inserting a class in between the two.

     

    This is just one example; I've encountered the same thing in different contexts. I've just never figured out a good way to address it.

     

    Any ideas?

    Dmitry gave you a possible solution for the specific problem you posted (TDataset descendents).

    I think this is generally a case for the facade pattern: you extract the functionality the method needs to access to an interface type (or a wrapper base class) and make your divergent descendents implement this interface or provide a factory method for a suitable derived wrapper class instance. The method parameter is then typed as the interface or wrapper base class type.

    • Like 2

  19. 5 hours ago, Silver Black said:

    I'm reading now: yes there are many.

     

    Interesting. So how do I clean the project, with the clean option of GExperts? Which files do you advice me to select for cleaning?

    Only *.DCU? In all my sysmte?

    No CN Packs' Input Helper here.

     

    Duplicates? How is this possible?

    Usually by copying a unit file via Windows Explorer instead of using the IDE's Save As menu. If you then just open the copy in the IDE it is not part of the project, that is still holding the original file you copied. It goes downhill from there, believe me. Have done it and have the scars to prove it :classic_blush:...

    • Like 1

  20. 21 hours ago, Silver Black said:

    Delphi 10.3.1, on my project code completion just stopped working completely, even on full rebuild or restarting the IDE. 😞 

    That's a real shame, it never ever happened in 10 years with Delphi 2010 (it was perfect).

     

    ...and it came back, after some coding, building, seraching and runnings… Nice, but worrying.

     

    Not able to reproduce it.

    When that happens to me the project is typically in a state that would not compile, with errors in a unit other than the one I am currently working on. If your project builds, however, I would suspect that you have duplicate files around, so the IDE works on files different from those the build process uses.


  21. 23 hours ago, azrael_11 said:

    Nothing happend i start to believe something going with rio installation.

    No, you are just setting and looking at the wrong search path. You need the one under Delphi compiler, not the one under Resource compiler!

    • Like 2

  22. Have not seen this myself, but my application are not nearly this large (largest was a bit over 10 MBytes). Check the project debug configuration, linker settings. If you now include debug information with the EXE, remove that checkmark. The IDE debugger uses the debug info from the DCUs, not the one in the EXE, so it just makes the file much larger, without any benefit. A smaller EXE may reduce the chance of running into this problem. Just a guess...


  23. 5 hours ago, azrael_11 said:

    Hello

     

    Try to move a project from tokyo 10.2.3 to rio 10.3.1 community edition.

    And have two questions

     

    1. How to remove the inherited values from debug from include file search path?

    2. In rio i install the pasLicVlc and everything is ok. Create a new multi application add the component TFmxPasLibVlcPlayer go to include file search path add the path source and source.fmx like tokyo do but compiler cant find it. Does this change in rio somehow?

     

    P.S. I have both installed in my computer Delphi 10.2.3 CE and Delphi 10.3.1 CE

     

     

    The project configurations form a hierarchy, simple select the top node ("all configurations - all platforms" probably, I have a german language version of RAD Studio) in the project options dialog, compiler options, dropdown list on the top of the right hand pane.


  24. 13 hours ago, CRO_Tomislav said:

    Dear all.

     

    I have a small experminet of reading of RFID tags information's using a RFID reader and attached .dll.

     

    Function is:

    Function Inventory(var ComAdr : byte; var State : byte; var AFI : byte;

    DSFIDAndUID : pchar; var CardNum : byte; frmComPortindex : integer): LongInt; stdcall; external 'RR3036.dll';

     

    I can call successfully function which is reading some data from RFID tags in a range ( I get positive result of this function and number of tag’s):

    Get_UID_RFID_Tag_Data_Status:=Inventory(fComAdr, State, AFI, DSFIDAndUID, CardNum,frmcomportindex);

     

    With this function I read set of data called DSFIDAndUID which are declared:

     

    var

    DSFIDAndUID: array[0..8000] of Char;

     

    In a reader manual description of:

    DSFIDAndUID is --> Output. Pointed to array storing the function result. The unit of the array is 9 bytes including a 1 byte DSFID and 8 bytes of UID. The volume of the results is CardNum*9 bytes with least significant byte first for UID.

    CardNum is --> Output. Point to a number of tags detected. The maximum number of tags one time collected is 16

     

    Problem is in DSFIDAndUID – I do not understand how to decode data from that array to get ASCII values of DSFID and UID. With my function’s I all way's get a wrong data and I do not have any more idea…

     

    I am reading only one RFID tag at a time…

     

    Thx in advance

    Tomislav

    From your description the data you get back are not characters, so do not use an array of char, use an array of byte. In fact you should be able to use something like this:

     

    type
      TTagResult = packed record
        DFSID: byte;
        case boolean of
          false: (UIDasBytes: array [0..7] of byte);
          true:  (UID: Uint64);
        end;
       TagBuffer = packed array [0..15] of TTagResult;

     

    type the DSFIDAndUID  parameter of the Inventory function as

        var DSFIDAndUID : TTagBuffer

     

    and you should be able to just pass a variable of type TTagBuffer and get the result back in a digested format.

     

     

    • Like 1

  25. 1 hour ago, Jacek Laskowski said:

    I knew that you're sticking to it ... 🙂
    I try to eliminate warnings from my own code, but I have warnings from external libraries, eg Synapse, DCPCrypt and others.

     

     

    Why are you rebuilding 3rd-party dcus as part of your build process? IMO only the (prebuild) DCUs should be on the IDE library path, the source code folders should only be on the IDE search path and not on the project's search path.

    • Like 1
×