Jump to content

PeterBelow

Members
  • Content Count

    460
  • Joined

  • Last visited

  • Days Won

    13

Posts posted by PeterBelow


  1. 5 minutes ago, Kevin Smith said:

    Hello,

    I'm trying to remove Word metadata using OLE. I can remove Author but I can't remove "Last author". I use following code:

    
      vEditor:= CreateOleObject('Word.Application');
      vEditor.Visible:=True;
      vDoc:= vEditor.Documents.Open('sample.docx');
      vDoc.BuiltInDocumentProperties.Item['Author']):= '';//It works!
      vDoc.BuiltInDocumentProperties.Item['Last Author']):= '';//It doesn't work
      vDoc.Save;
      vDoc.Close;
      vEditor.Quit;

    I hope you have some experience with such a task and you can help me.

    If there is other solution than using OLE please also give me an info.

     

    Isn't the Last Author set when the file is saved? That would simply undo your change...


  2. 5 hours ago, Wagner Landgraf said:

    Sure, that is already done. I was just wondering if that was a possible thing to happen. But it looks like that's not the case.

    There is a case, though: if the code inside the try block calls something in a DLL and that DLL function does not trap any exceptions raised inside the DLL code these would bubble up the exception handler stack into your try except block, be trapped there but not recognized as deriving from the Exception class. This would even be the case if the DLL is written in Delphi, unless DLL and EXE are build with the same Delphi version and with run-time packages enabled for both projects.

    • Thanks 1

  3. 17 hours ago, milurt said:

    var ballnumber:^BYTE;

    Begin
      GetMem(ballnumber,70000);
      For i:=0 to 70000
      do begin ballnumber^:=5;ballnumber:=ballnumber+1;
    End;
    End.

    at the next SEGM:0000 which will be written is a
    runtime error

     

    Which OS/CPU are we talking about here? What you show has not been a problem since 16-bit (DOS, Win 2.x, 3.x) programs have gone the way of the dinosaurs. 32 and 64 bit CPUs use a different memory architecture. 32 bit CPUs (Intel) do in fact still have segment registers but their content is constant during normal program runs, and the offset (address) registers are 32 bit wide, so a memory block can be up to 4GB large in theory. Due to OS requirement (Windows) 32 bit application programs can only use 2GByte of this theoretical address space though (3GByte with a special executable flag).


  4. 17 hours ago, milurt said:

    if i use getmem's it seems that i need memory paging or segmentation to get the values.

    can somebody explain an asm,c,delphi source, that is usable like

    number=Pointer1^;

     

    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)^;

     


  5. 2 hours ago, Stano said:

    I have a policy that if the user is in Insert/Edit mode, they can't "click" anywhere else. In other words. I will not allow him to take an action that would interfere with working on a record.

    Very sensible policy, it saves you from serious headaches. :classic_dry:


  6. 2 hours ago, Zazhir said:

    Hello!

    I am facing Bad unit format issue for Indy component. I was trying to update it, following the steps in this link: https://github.com/IndySockets/Indy/wiki/Updating-Indy.

    My version of Delphi is XE6, in this case I tried to compile the ```Indy150.groupproj``` file, but the project has not been compiled. It all started at that point.

    As I ended up giving up, I decided to go back to the old version of the Indy component. But when returning the files that had been removed and also adding them inside Delphi's IDLE the component paths:

    • Indy 10 Core Design Team
    • Indy 10 Protocols Design Time
    • IP Abstraction Indy Implementation Design Time

    All of my projects that use the component cannot be compiled. All of them that use the SMTP library have stopped working. Which generates the error shown below:

    
    [dcc32 Fatal Error] UfrmSendEmail.pas(47): F2048 Bad unit format: 'IdSMTP.dcu' - Expected version: 27.0, Windows Unicode(x86) Found version: 27.0, Windows Unicode(x64)

    This unit sends emails using the SMTP server configuration.

    Do you know how I can resolve this error?

    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.


  7. 27 minutes ago, PenelopeSkye said:

    Unless I want to have a lot of undeclared identifiers I have to have multiple var sections, see below.  That is so wrong!

     

    Also, this array declaration doesn't work under any circumstances even though I lifted it from another unit in the project and made sure it has the same uses list.

    x: array[0..9] of variant;

    The procedure in the other unit was not a (Sender: TObject) procedure (sorry for my ignorance of what to call that).

     

    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 :classic_dry:; 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;

     

    • Like 2

  8. 17 hours ago, MarkShark said:

    I was writing a simple record helper for TBytes and realized that some of my routines call SetLength.  I'm wondering if this is a problem since the array could/will be reallocated in memory.

     

    Simple example:

    
      TTestBytesHelper = record helper for TBytes
      private
        function GetLength: Integer; inline;
      public
        function ReadFromStream(AStream: TStream; ACount: Integer): Integer;
        property Length: Integer read GetLength;
      end;
    
    { TTestBytesHelper }
    
    function TTestBytesHelper.GetLength: Integer;
    begin
      Result := System.Length(Self);
    end;
    
    function TTestBytesHelper.ReadFromStream(AStream: TStream; ACount: Integer): Integer;
    begin
      // Wondering about this SetLength call!
      System.SetLength(Self, ACount);
      Result := AStream.Read(Self, ACount);
    end;

    In my testing the ReadFromStream function does seem to work fine.  I'm just wondering if calling SetLength on Self while in a record helper is ok to do.  Thanks for any insight!

    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.

    • Like 2

  9. 20 hours ago, Martin Liddle said:

    I am porting some code from the Gnu Pascal compiler that makes extensive use of WriteStr which is an ISO 10206 Extended Pascal extension; is there a Delphi equivalent?   For those not familiar with WriteStr, WriteStr(S,Args) behaves like Write, except that it stores its output in the string variable S instead of a file. 

    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.


  10. On 1/29/2023 at 10:15 AM, Fons N said:

    With the example code I have tried to translate the Keybd_Event code. But unfortunately it does function (behave) the same.

    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 :classic_dry:.

     

     


  11. 3 hours ago, Fons N said:

    In a KeyDown event (VCL) I have the following code:

     

    procedure CodeExample.KeyDown(Sender: TObject; var Key: Word; Shift: TShiftState);
    begin

      if Key = 37 then
      begin
        Shift:= [ssShift];
        Key:= 9;
      end;

      if Key = 39 then
      begin
        Shift:= [];
        Key:= 9;
      end;

    end;
     

    The code in the Key = 39 part works. The pressed key is now a tab. The code in the Key = 37 part however does not work. Yes, the pressed key is still a tab, but the state of the Shift key does not act like it is pressed.

    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. :classic_dry:

     

    Quote

     

     

    • Thanks 1

  12. 10 minutes ago, limelect said:

    Thank you for your help (too many).

    I used the Skia4Delphi_4.1.0_Setup.exe and things are OK

     

    From this experience, I am left with bad knowledge.

    I thought. that components TABs are written in the registry.

    They are not.

    Does anyone know where are they written? In a DB?

    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.


  13. 14 hours ago, msd said:

    Hello,

     

    On the form OnFormKey UP or DWN, we can use the entire keyboard, but for the numeric part, we have VK_ADD, VK_MULTIPLY,...

    I need some advice for using those keys in OnFormKeyDown, Up, or Press like this sample below:

    When a user searches for data in the TEdit VCL by typing letters and numbers and then clicking on the numeric + key VK_ADD, the app does something BUT WITHOUT adding +char to the TEdit VCL control.

    Simple: I need to use those keys but without the characters they represent: +, -, /, and *.

     

    Thanks for the advice in advance...

    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.

    • Thanks 1

  14. 5 hours ago, Delphi65 said:

    Hi guys,

    I have been using the following for earlier version of windows.

    Many thanks in advance!

    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.


  15. 1 hour ago, lisichkin_alexander said:

    l have to explain why I started all this 🙂

     

    There is a Web service, written in Java, that we use to convert and compress images to Png.
    It was noticed that this service does not correctly convert colors for some Png images.
    I was given the task of building an algorithm that would determine that two pictures are identical.
    Moreover, with the condition, that colors are identical - the images themselves are considered the same.
    I came up with the idea of comparing the illumination histograms of two pictures.
    And this algorithm worked well, until I find some images with transparency - for which I did not correctly determine the color of some points.


    The question can be reformulated: how to get the R G B color of a point, taking into account transparency.

    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.


  16. 8 hours ago, karl Jonson said:

    Hi,
    I am reading a database & adding to a file the following info:
       

    
    user_name, surname, date of birth, phone number


    One customer can have multiple records in db.
    Which is the best way to keep track of data I am adding to text file & make sure the data I am copying in it is unique

    
    Loop through records in db
      if combination user_name, surname, date of birth, phone number doesn't exist in text file then
        add
      else
        read next record

    TIA
     

    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.

     


  17. 1 minute ago, RDP1974 said:

    hi

    I have a TGridPanel with 3 columns set as ssPercent 33.3% and the proportions are fine

    now

    I set at runtime the column 3 at 0% then the screen redraws ok with the first 2 columns at 50%

    then I set back the column 3 at 33.3% but the proportions are not ok, the resulting column is smaller

    any hint?

    thanks

    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. :classic_dry:


  18. 57 minutes ago, Henry Olive said:

    Thank you so much Programmer, Pat, David

     

    I'm really so sorry, i think i couldnt express my problem well cause of my unsuffient english.

     

    Suppose you have Form1 (Auto Create), Form2 (NOT Auto Create) , Form3 (NOT Auto Create)   

    Lets say there is a Combobox1 in Form1 and ComboBox1.Items are 'A', 'B'

    Also there is a Button1 on Form1

    According to above explanations here below my codes

     

    procedure TForm1.ShowForm (MyText : String);
      var
      MyForm : TForm;
    begin
      if MyText ='A' then
         MyForm := Form2 else
      if MyText ='B' then
         MyForm := Form3;


      if MyForm = Nil then
         MyForm := MyForm.Create(Self); // Problem is here,  even if i use  MyForm := TMyForm.Create(Self)

     MyForm.Show;
    end;

     

    procedure TForm1.Button1Click(Sender: TObject);
    begin
      ShowForm (ComboBox1.Text);
    end;

     

    Again i'm so sorry for not explaining the problem well.

     

    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.


  19. 11 hours ago, Jeff Steinkamp said:

    I've dropped those files into the project manager.  I see nothing that resembles the Deployment screen you show above.  I get this:

    image.thumb.png.89274f89e1b2858e40d95ed49b236492.pngimage.thumb.png.fcbd68d8d1de1a13244620e8a1a5a622.png 

     

    But, at this stage of the game I'm not even close to project deployment, I am barely into the development phase.  But I do need these files in the output folder for continued development.

     

    I'm not sure exactly how the build events are helpful unless there are some commands that can be used to copy these files during the pre-build event.

     

    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.


  20. 9 hours ago, Mr. E said:

    Greetings,

     

    How long does it take to update Delphi 11?

    The resource and performance monitor do not show any activity (hdd, network).


    It is a Windows 11 Pro VM on Proxmox, with adequate resources; I disabled the Firewall and it has no antivirus.

    I have almost an hour and I don't see any progress in the update/installation (web install).

     

    I'm going to wait another hour; but, I am tempted to stop the VM, restore it (I did a previous backup) and retry.

     

    image.thumb.png.562bad52fcc1bb5981ef7aadd6bbfea2.png

    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.

    • Like 2

  21. 5 hours ago, limelect said:

    After so many years of successful Delphi, I almost give up

    on my free program.

    After out of memory  I made a new test program and this is what I came up with

    1. NO PROBLEM with SQL.

     

     

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


  22. 51 minutes ago, brk303 said:

    I have a design time package which has a form linked to a datamodule.

    The same code is used for a standalone app, in which everything works fine, but in design time package the links between forms/datamodules are not restored.

     

    I have used RegisterFindGlobalComponentProc to hook in my function that does this:

      if AnsiSameText(Name, 'dmxxx) then
        Result := dmxxx

    This fixes the links from form to dmxxx, but I then found the dmxxx has name = '' and ComponentCount = 0, there should be 3 components on datamodule.

    All of this runs fine in standalone app outside IDE.

     

    Does anyone know what is different with component name/linking/restoring in IDE compared to standalone ?

    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.


  23. 8 hours ago, SteveHatcher said:

    I am trying to understand some relatively simple Delphi code for a GUI and am wondering if there is any tool which can tell me what which classes call what, and when etc. (Delphi 10.4 Community Edition.)

    I have looked at class explorer and Model View, but they both show just the classes (and hierarchies) which are used, not necessarily when one calls another.

    What I am after is something generated that would look as follows (just making this up):

     

    ObjectA

      ObjectA.Prop1

      ObjectA.Prop1.doesSomething

    ObjectA.ButtonClick

      tmpObjectCreate

      tmpObjectCreate.Prop1.doesSomethingElse

    ObjectA.CallSomeMethod

     

    Sort of what you might get from adding a breakpoint and steeping through line by line, but wondering if there is any way to generate this automatically?  

    This might not exist but would love some input.
    Thanks

    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.

×