PeterBelow
Members-
Content Count
508 -
Joined
-
Last visited
-
Days Won
13
Everything posted by PeterBelow
-
Looks like the design-time package in the D11 folder (22.0 is the folder for that version) is in fact the version for D12, that is, should be in the 23.0 branch. But it may also be the correct version but tries to load a run-time package from the wrong folder. That is a common problem if the packages are not named properly with a version tag (280 for D11, 290 for D12). Run-time packages are loaded using Window's search strategy for DLLs (since they are DLLs in fact), which includes searching folders on the PATH. If you have several RAD Sudio versions installed each will have its BPL folder on the path, so the search may find the wrong version of a package if they all have the same filename. A possible cure is to move the run-time package in question to BIN folder of the matching Delphi version (where bds.exe resides), since Windows searches that folder first. Another is to start the IDE not via the start menu links the installer created but from a batch (cmd) file where you first redefine the PATH to only contain the folders for this Delphi version. Oh, and if you can rebuild the packages you can of course change the project options to use the proper version tags for the produced binaries.
-
If the DLL is COM-enabled you can use the Component -> Import component dialog from the IDE main menu: A pure .NET assembly without COM support cannot be used from Delphi with the tools available out of the box, but there are 3rd-party libraries available, some freeware. A google search for "host the .net framework in delphi" turns up this for example. I have no personal experience with such libraries.
-
12.1 IDE start up error personality not supported
PeterBelow replied to bdw_nz20's topic in Delphi IDE and APIs
Have you configured the IDE to load the last open project at start? The message may be caused by a corrupted dproj file. If you get the error without loading a project check the registry key Computer\HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Embarcadero\BDS\23.0\Personalities. It should look like this: Depending on your licence you have more entries, but the one after "(Standard)" is what counts here. There is a matching key for the current user: Computer\HKEY_CURRENT_USER\SOFTWARE\Embarcadero\BDS\23.0\Personalities. It should have the same content. -
The reason is quite simple: the component is based on a Windows common control which is intended to serve as storage for small images/icons used for menu items, buttons, listview items. To reduce the consumption of GDI handles the control merges all added bitmaps into one large bitmap for storage. That's the reason all bitmaps added are forced to the same size, this way the imagelist can calculate where on the combined bitmap each image resides and then paint that part to the target location. There is a maximum size to a GDI bitmap, so this puts limits on the size and number of the images you can put into a TImagelist. Looks like the control is simply not suitable for your task.
-
Stretching bitmaps to a larger size (e.g. a 16x16 bitmap to 64x64) has this effect. The Windows API function used by the VCL is not very good at reducing the unavoidable degradation in image quality by interpolating the new pixels added between the original ones. You get much better results using vector formats like SVG as source, which can be rendered to practically any size without quality loss as long as the aspect ratio is not too far off the original. For bitmaps used for UI elements (buttons, menus, listview items etc.) Delphi offers TImageCollection and TVirtualImagelist. The former can store multiple versions (different sizes) of an image and the latter is used to then pick the best fitting version for a particular screen resolution.
-
Works for me without problems (Win32 project with debug build, halted on a breakpoint). But dcc32270.dll does not belong to Delphi 12.1, that uses dcc32290.dll. 270 would be Delphi 10.3 if memory serves. Check the registry key HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node\Embarcadero\BDS\23.0\Debugging\Embarcadero Debuggers\Evaluators, that names the defaullt debug evaluator.
-
CompareString function for UTF8 strings or buffers?
PeterBelow replied to MarkShark's topic in RTL and Delphi Object Pascal
Depends on what you need to compare for. Equality is easy, that is a simple memory comparison. Larger/smaller implies a collation sequence, which is language-specific. Here converting to UTF-16 first is, in my opinion, the only practical way. Same if you need case-insensitive comparison, since case conversion is also language-specific and may not be applicable for the language in question at all (e.g. chinese). -
Delphi and "Use only memory safe languages"
PeterBelow replied to Die Holländer's topic in General Help
Consequent use of interface references (with rerf counting for lifetime control) instead of object references avoids that problem completely. -
Best Practices for Database Development in Delphi with Firebird
PeterBelow replied to Noor's topic in VCL
Oy, that's a really open-ended subject. The RAD approach (using data-aware controls and connection/query components you create and configure at design-time) gets you results (= working application) fast but it tends to get messy fast as well, with business logic mixed into form and data moduls, poor reuse of code (unless you plan well ahead). This approach makes proper separation of application layers harder and is excellent at producing apps that are a nightmare to maintain and document. Code can be written to be self-documenting and serve as source for project documentation as well, but how do you deal with all the stuff set at designtime and scattered across dozens or even hundreds of dfm files? In my opinion RAD is workable for smaller projects with a few database tables and a few forms as well. For anything more complex and expected to have a lifetime of many years with frequent updates it pays to use a proper layered approach (e.g. model-view-controller or similar), with a object-relational mapper (ORM) as interface to the database and a client UI that works with data objects instead of directly talking to database tables, queries, or stored procedures. All the FireDac stuff would be hidden inside the ORM. Unfortunately Delphi does not come with an ORM, but there are some 3rd-party offerings in that area, including open-source projects like MORMOT. The learning curve can be steep if you have never dealt with such a design before, but it is worth it IMO. I wrote my own ORM for a larger project at work 20 years ago, took about half a year (part time, programming was not my main task) since nothing suitable was available at that time. It has served me well and some of the programs are still in some use... As for partitioning the app into modules that can be maintained independently: that is what packages are for, but it is not as straightforward as one would hope. It's DLL hell in disguise and you have to invest some serious though into how you partition your application. Packages are all or nothing, every unit used can only be contained in one single package. All other units requiring such a unit have to get it from the (shared) package, so you have to deploy not only your own packages but also all used RTL and VCL (or FMX) and 3rd-party packages. As long as you do not change anything in the interface part of a packaged unit you can replace the build package in the production app with an updated package. If you change an interface you have to rebuild all packages using the modified one, plus the host app. Since forms etc. are declared in a unit interface that severely limits what you can change in such a form unit without major hassle. Note that this applies to units you want to use from other packages directly. It does not apply to units only used internally by a package. So it is possible to achieve a better isolation of modules if the only exposed part is a factory function for an interface the host app then uses to talk to the internal stuff of the package. In this scenario you can get away with only building with the core RTL and VCL packages and one shared package declaring the interface types used, and actually you can use DLLs (build with packages) instead of full-fledged packages. But this can still degenerate into a maintenance nightmare if you are nor careful... -
GlobalAlloc rounds the requested size up to the next multiple of 8 (at least), so it is normal for GlobalSize to return more than the requested size if that is not a multiple of the used granularity. In fact the MS docs for GlobalAlloc explicitely state that GlobalSize should be used to get the allocated size of the block requested. Depending on the use of the block you may have to store the actual data size with the data itself if the extra bytes allocated by GlobalAlloc may cause a problem for the data recipient. Note that the granularity used may depend on OS version, Microsoft is pretty vague on this.
-
It may not be logical but Windows has a lot of these little inconsistencies that have accumulated over time by bolting more functionality on existing control classes. TButton is a wrapper around the Windows button control, and its behaviour is determined by the set of button control styles, in this case the BS_MULTILINE style, which the Multiline property surfaces.
-
Delphi should let me use a const array reference as a constant
PeterBelow replied to PiedSoftware's topic in RTL and Delphi Object Pascal
A typed constant (that's what your IfTkns constant is) is actually implemented as a compiler-initialized variable, and that's the reason you cannot use it as a constant expression. It may be counter-intuitive but that's the way it works and you have to live with it. -
Oh, I'm farther behind the times than I was aware of . Didn't know these properties exist now...
-
The control (a Windows common control under the VCL surtace) has no real concept of an "empty" state. The usual way to use it is to set the shown date to Today (it does that by default if memory serves) and accept that if the user does not change it. If your requirements really need a way to detect that the user has entered a date you can use an additional TCheckbox that disables the picker unless it is checked.
-
You can experiment with the ScaleBy method all TWinControl descendants inherit, including forms.
-
Anonymous methods as interfaces
PeterBelow replied to EugeneK's topic in RTL and Delphi Object Pascal
But that's the way it currently works! Of course you can design your own interface + implementor that acts exactly like anonymous methods act now, but that would still not be the same implementation the compiler creates behind the curtain now. -
But the compiler error message should pinpoint the uses clause the (now missing) unit appears in, no?
-
Casting an untyped pointer to a typed when using with New() and Dispose()
PeterBelow replied to PeaShooter_OMO's topic in RTL and Delphi Object Pascal
It will work but there is no point in doing it this way. Declare LPointer as type PMyRec and work with that. Don't use C idioms with Delphi, that only confuses things. -
If this is for VCL: I centralize such code by overriding the UpdateActions method of the form and updating the control states from there. Much easier than fiddling with multiple event handlers or live bindings.
-
This has come up before. Microsoft changed the UI guidelines and now recommends to not show an icon for confirmation dialogs. D12's vcl.dialogs unit was modied accordingly.
-
You should not show a dialog inside a BeginUpdate/EndUpdate block in my opinion. That may be the source of your problem and it also serves no purpose i can see here.
-
You cannot, it is only possible for TWinControl descendants.
-
You cannot, subcomponents in a custom component are created in an overridden constructor. But there is an alternative: Create a new frame in the designer and place groupbox and edits on it. Save the frame unit (after changing the Name property of the frame to something descriptive), best into a folder where you stash code units you want to reuse in other projects. To use the frame in another project just add its unit to the project. A frame behaves much like a component, you can drop multiple instances of it on forms, panels or other containers. But it can also hold code like a form, in event handlers or methods, and this code is shared between all frame instances. You can modify properties of the controls on the frame in the designer for each instance separately.
-
A Memory Game in Delphi
PeterBelow replied to LaryRose's topic in Algorithms, Data Structures and Class Design
Well, there are many ways to approach such a project. Using 20 images and 20 buttons may look appropriate at first glance but you have already identified the problem of code duplication and it is not the only one. Even getting the layout riight may be more work than you think. If you have a number of identical objects think array, and for visual stuff think grid. If your target platform is Windows and you intend to use the VCL instead of the FMX framework I would use a TDrawGrid as the main UI. For the start and exit buttons use TButtons hosted on a TPanel aligned alBottom on the form. The TDrawgrid is placed above the panel and aligned alClient. With 20 images you probably want 4 rows of 5 images each, so set the rowcount and colcount to 4 and 5, respectively and the FixedColCount and FixedRowCount to 0. Add a handler for the grid's OnDrawCell event, that is where you place the code to draw button (for an image not uncovered yet) or image. Then you need a way to store the state (covered/uncovered) for each cell and an identifier for the image a cell is holding. To keep the info for a cell together the natural storage is a record, so add a type definition like this above the form class: type TGameCell = record ImageIndex: Integer; Uncovered: Boolean; end; The game state is then a 4x5 array of TGameCell: const CNumRows = 4; CNumCols = 5; type TGameState = array [0..CNumRows-1, 0..CNumCols-1] of TGameCell; Add a field to the protected section of the form class: FGameState: TGameState; Each element of the game state array corresponds to a cell in the drawgrid. OK so far. Now you need some storage for the images. The most appropriate is a TImageList, so drop one on the form, set its Height and Width to the size of the images you want to use and then load the images into the list. The index of a given image will be used to identify it in the TGameCell. I asume with 20 cells you will have 10 images. Set the defaultcolwidth and defaultrowheight properties of the grid to values a bit larger than the images, say 4 pixels more. That gives you spacing between the tiles you have to draw. OK so far. Add a handler to the form's OnCreate event. There you place the code to initialize the FGameState array. The Uncovered members of the TGameCell records will start out as false, so you only need to set the ImageIndex to specify which image to show in each cell, using the index from the imagelist. That is the basic setup. Now you have to figure out how to draw the cells to resemble the usual memory game card. The event handler for the grid's OnDrawCell event has the following parameter list: procedure (Sender: TObject; ACol, ARow: Longint; Rect: TRect; State: TGridDrawState) Sender is the grid, you will have to draw on its Canvas, so add a variable for that to the handler sceleton the IDE created for you: var LCanvas: TCanvas; begin LCanvas := (Sender as TDrawgrid).Canvas; ACol and ARow tell you which cell to draw and, conveniently, these directly correspond to the matching TGameCell indices in FGameState. If you leave the DefaultDrawing property of the grid at the default True the VCL will have already drawn the cell background, so you can concentrate on drawing the image or button. Which it is you determine by looking at FGameState[aRow, aCol].Uncovered. If true you use the imagelists Draw method, passing it the LCanvas and the Left and Top members of the Rect parameter, adding 2 for the spacing to both. To draw the button the simplest way would in fact be to add a suitable image to the imagelist as well and draw it the same way. An alternative would be the DrawFrameControl Windows API method, but that is much more complicated to use. OK, so the form will now show the grid when you run the project (at designtime the grid is empty). Now you need to detect clicks on the cells, so add a handler to the grid's OnMouseDown event. Its X and Y parameter tell you where the mouse went down inside the grid but not which cell that is. Fortunately the grid has a MouseToCell method, so call that to get the cell, look at the Uncovered member of the corresponding TGameCell. If true exit directly, if false set it to true and call the grid's Invalidate method to get it to redraw. This would also be the place from which you then evaluate the game state, i. e. see if this action has uncovered the mate to the last tile uncovered (if so stop the timer), has uncovered the first tile (if so remember the cell coordinates and start the timer), and so on. If the timer fires you would just set both cells to Unconvered:= false, stop the timer, and Invalidate the grid to redraw. OK enough loafing around! Now get to work -
What root key do you use? HKEY_CURRENT_USER depends on the user running the process in question (IIS probably) and that may not be what you expect. And which access rights do you specify when you try to open the key in question?