Jump to content

PeterBelow

Members
  • Content Count

    508
  • Joined

  • Last visited

  • Days Won

    13

Everything posted by PeterBelow

  1. PeterBelow

    memory paging or segmentation

    In Delphi you very rarely need to go as far down to the metal as GetMem. If you need arrays of some element type and don't know the size needed at coding time use a dynamic array type. Once your code has figured out the size needed use SetLength on the array to allocate the needed memory for it, then access members of the array with the classical array syntax ([index]). For array of characters use the String type in the same manner. This way you never have to deal with raw memory allocation or pointer manipulation (where you better know what you are doing if you value your sanity). You can use pointer stuff in Delphi if you really need to, but there is rarely any need. If you need a pointer to pass to some low-level API the @ operator is your friend, you can allocate memory using a dynamic array and then pass @A[0] (the address of the first array element) as the pointer. Just be aware that this only works for one-dimensional arrays. Multidimensional dynamic arrays do not occupy one single memory block. Refer to the Delphi Language Guide to learn about such pesky details, it is part of the main online help. Oh, to get a value a pointer points at you best use a specific pointer type that defines the type of data the pointer points at, e.g you use a PInteger (defined as type PInteger = ^Integer; in the run-time library units, System.Types I think) in preference to the raw Pointer type if you know the pointer points at an integer (or an array of integers). You can then just dereference the pointer to get the pointed-at value into an integer variable: var N: Integer; P: PInteger; begin ...assign an address to P N:= P^; If you only have a raw pointer you can typecast it to the specific pointer type for the assignment. var N: integer; P: Pointer begin ...asign an address to P N:= PInteger(P)^;
  2. PeterBelow

    Refresh dbedit after insert

    Very sensible policy, it saves you from serious headaches.
  3. DEF files are a C/C++ thing, no? Delphi definitely does not need them.
  4. I hope you have the source code of this library. You need to recompile its units to use the Indy version you just installed. And make sure you only have one instance of the Indy dcus on the pathes the compiler searches for files (IDE library path, project search path).
  5. This looks like the Indy folders listed in the IDE library path for 32 bit projects are actually for the 64 bit version of the Indy components. Correct the path entries to point at the 32 bit version of the dcus.
  6. PeterBelow

    Must have multiple var sections

    There are two ways to declare variables inside a method or standalone procedure or function. Sherlock's reply shows the traditional way of declaring all variables in a single var section before the begin keyword that starts the body of the method. That has been part of the Pascal language since the time of its inception by Wirth; variables you declare this way are accessible in every place inside the body of the method. What you showed in your post is a fairly new way to declare variables inline, near the place of first use. In my opinion this should only be used if you need to reduce the scope of a variable to a specific block (e.g. a begin ... end block for an if statement or for loop). In fact in my real opinion it should not be used at all ; for one it is alien to the general structure of the language, and some IDE features do not work correctly (in the current version) when they are used, e.g. refactorings and things depending on the LSP server, like code completion. If you need a lot of variables in a method this is actually a "code smell", it indicates your method is too large and tries to do too many things. Refactor it to call several smaller methods that each do a single task. If that gets complex (i.e. you find you actually need a lot of methods to partition the code correctly) the solution may be to create a new class that does the required work internally and isolates it from the calling code. The array issue you mentioned probably has a different cause. If you declare a variable with a syntax like x: array [1..9] of variant; you are using what is called an anonymous type for the variable. This is allowed for classical variables declared at the top of the method but may not be allowed for inline variables. If in doubt declare a proper type and use that for the variable: procedure.... type T9Variants = array [1..9] of variant; var x: T9Variants;
  7. It's OK, a record or helper is just way to add methods for a type, they always work on the instance of the type you call the added method on.
  8. PeterBelow

    Is there a Delphi equivalent of WriteStr ?

    It used to be possible to kind of redirect the output of Write/WriteLn using something called a "text-file device driver". I found some ancient unit of mine in the newsgroup archives, see https://codenewsfast.com/cnf/article/0/permalink.art-ng1618q5913 . If the link does not work search for "Unit StreamIO". It contains a AssignStream procedure you can use to attach a stream (in this case a TStringstream would be appropriate) to a Textfile variable. Output to this variable would then end up in the stream, from which you can get the content as a string. As I said this is old code, I have no idea how it behaves under Unicode-enabled versions of Delphi.
  9. PeterBelow

    KeyDown and Shift state

    It is expected to act the same since SendInput is just the recommended alternative to the old keybd_event API, which is basically legacy from 16-bit Windows; still supported by current Windows versions but it may be removed sometime in the future, though that seems unlikely based on past experience with other "legacy" APIs. Windows carries a loads of old APIs along, since dropping them will cause old applications still in use at many customers to fail, and the resulting uproar would be bad for business .
  10. PeterBelow

    KeyDown and Shift state

    Why should it? The Shift parameter is not a Var parameter so you just change the local value of it, not the one the caller passed. Even changing that (if you could) would not do what you think it should since it would not change the Windows-internal state of the modifier keys. And the WM_CHAR message for the tab key is already in the message queue anyway. Using keybd_event (in fact thats deprecated, you should use SendInput instead) puts a completely new Shift-Tab combination into the message queue, so you better set Key := 0 to abort processing of the 37 key. You should use virtual key codes instead of numeric values however, those make it clear which key you are processing here. VK_LEFT is much more readable than 37.
  11. PeterBelow

    Skia component and IDE

    Nowhere in fact. Components are installed via design-time packages and those register their components when the package has been loaded, through calls to the Register procedure a unit in the package defines in its interface section (in fact several units can have such a procedure, though that is not recommended). The procedure contains calls to RegisterComponents that tells the IDE how the component class is named and on which page of the palette to display the associated icon. The installed design-time packages are listed in the registry.
  12. PeterBelow

    Using VK_XXX numeric part of keyboard

    Use the OnKeyPress event of the edit control. Like this: procedure TForm2.Edit1KeyPress(Sender: TObject; var Key: Char); begin if (Key = '+') and (GetKeyState(VK_ADD) < 0) then begin Key := #0; label1.Caption := 'VK_ADD'; end else label1.Caption := string.Empty; end; The GetKeyState API function allows you to check which key is currently down. The code above will let the '+' key of the normal keyboard add the character to the edit but block the numpad plus key input.
  13. PeterBelow

    Turn display Off/On

    Have you tried running the code under an admin account? This may be a rights issue, since you are changing a setting that effects all users.
  14. You cannot, since the pixel color in question depends on the background the image is rendered on. Just replace transparent pixels with a default color for the comparison, e.g. black.
  15. The best way is to not have duplicate records in your query result in the first place. If you are using a SQL-supporting database just do not access the table directly (via TDBTable or analog), instead use a query component and give it a SELECT DISTINCT <columnlist> FROM <tablename> statement, optionally with more restrictions on the records to return (WHERE clause) and ordering. This way the result set will not contain duplicates in the first place. If the database you use does not support this kind of statement your best bet is to keep track of what you have already written to the file in an in-memory structure that supports quick lookup. You could concatenate all field values of a record into a string (basically the line you write to the text file) and store them into a TDictionary<string,byte>, using the byte part to keep a count of the duplicates you find. Depending on the number of records you have to process this may use too much memory, though. You could cut down on that by calculating a hash value from the string and storing the hash instead of the string. That is not completely failsafe, though, since different strings can result in the same hash value. You can deal with that by writing any such duplicate detected to separate file, which will end up with only few such records. When done with the task load that file into a stringlist, sort the list, and then do a final run over the record file and compare the lines read with the content of the stringlist, marking each match found there. If you end up with any unmarked items in the stringlist those are false duplicates and need to be added to the record file.
  16. PeterBelow

    tgridpanel resize

    That is an old problem you also have at design-time: using ssPercent each change of a column width recalculates all column widths, including the one you just changed, so you end up with a value different from what you just entered. The algorithm used is faulty, but at design-time it is fairly easy to work around by changing the width in the DFM file directly. If you need to change the width at run-time it is easier to work with absolute column widths and do the calculations yourself, at least it is better for your mental health.
  17. PeterBelow

    Create Form

    If you have forms that are not autocreated the first thing you should do is to delete the form variable (form2, form3 in your example) the IDE creates in the form unit since these variables will never be initialized unless you do it in code. Such forms, when only used modally, should be created inside the method showing them modally, and destroyed after their ShowModal method has returned and any user-entered data has been retrieved from the form. In such a scenario you use a variable local to the method showing the form to hold the form instance. Your problem is basically to select the correct form class to create the form identified by the string passed to your ShowForm method. If you can live with needing to add the units containing the form classes in question to the Uses clause of the TForm1-Unit (use the one in the implementation section) you can do something like this: procedure TForm1.ShowForm (MyText : String); var MyFormClass : TFormClass; MyForm: TForm; begin if MyText ='A' then MyFormClass := TForm2 else if MyText ='B' then MyFormClass := TForm3 else MyFormClass := nil; if Assigned(MyFormClass) then begin MyForm := MyFormClass.Create(Self); try MyForm.ShowModal; finally MyForm.Free; end; end else ShowMessage('Unknown form class requested: '+MyText); end; If you really need to show the created form modeless you need a different approach. The safest way in my opinion is to simply search for the form in the Screen.Forms collection, using the form class as the search parameter. function FindOrCreateFormByClass( aFormClass: TFormClass ): TForm; var i: Integer; begin for i:= 0 to Screen.formCount - 1 do begin Result := Screen.Forms[i]; if Result.ClassType = aFormClass then Exit; end; Result := aFormClass.Create( Application ); end; procedure TForm1.ShowForm (MyText : String); var MyFormClass : TFormClass; MyForm: TForm; begin if MyText ='A' then MyFormClass := TForm2 else if MyText ='B' then MyFormClass := TForm3 else MyFormClass := nil; if Assigned(MyFormClass) then begin MyForm := FindOrCreateFormByClass(MyFormClass); MyForm.Show; end else ShowMessage('Unknown form class requested: '+MyText); end; This works if you only ever need one instance of a form class. If you need access to the created form later you do not use the IDE-created form variable (if you did not delete it) but search through the screen.forms collection again. There are ways to eliminate the need to add the form units to the TForm1 unit uses clause but they again require a different approach. You create a "form factory" singleton object in this case (google for "factory pattern") in a separate unit. The form classes would get registered with this factory together with the string identifying the class; in the form unit's Initialization section. The TForm1 unit would the only need to add this factory unit to its uses clause, and the factory would have a public method that corresponds to your current ShowForm method.
  18. PeterBelow

    Include External Text Files in Output Folder

    Build events allow you to execute shell commands, like "copy" or "rename", using macros that get replaced with project related pathes etc.. Since the files are used by the finished program I would use the post build event for that. The deployment stuff is for non-Windows targets only if I understood that correctly.
  19. PeterBelow

    How long does it take to update Delphi 11?

    The update has problems with uninstalling some GetIt packages, e. g. Parnassus Bookmarks and Navigator. It is recommended to manually uninstall these first before you run the update. I dimly remember a blog post about this issue but cannot find it at the moment. Anyway: the GetIt uninstall opens a command window that then fails to close. Just close it yourself, you may actually be able to type "exit" at its prompt for that, otherwise shoot it down via task manager. The process will then continue normally.
  20. PeterBelow

    No one can help

    Have you tried to build the program as a 64 bit executable? You also need the 64 bit version of MySQL of course.
  21. PeterBelow

    Component names cleared in design package

    As far as I know forms (all TCustomForm descendants in fact, including TDatamodule) are not meant to be added to design-time packages. If you want to make them reusable from other projects add them to the object repository instead, this way they are accessible from the File -> New -> Others dialog.
  22. PeterBelow

    View program flow and calls to help understand code?

    Pascal Analyzer may be helpful in this context. It is not free but if I remember correctly you can evaluate it for a period until paying for it. Have not used it myself, though.
  23. PeterBelow

    Find, Replace and Save

    If you do multiple passes over the file you can end up replacing bytes in a part you replaced in a previous pass, which is usually not what you want.
  24. PeterBelow

    Find, Replace and Save

    This approach only works if you have one single search/replace pair.
  25. PeterBelow

    Find, Replace and Save

    OK; i'm done with the example code. Find a small test project attached as a zip file. It contains a unit named PB.ByteSearchAndReplaceU that implements a class TByteSearchAndReplace that encapsulates all the code for this task. It compiles and runs but I have not checked that it does do the actual replacements properly, due to the lack of test data. I leave that to you, good luck. ByteS&R.zip
×