Jump to content

aehimself

Members
  • Content Count

    1090
  • Joined

  • Last visited

  • Days Won

    23

Posts posted by aehimself


  1. 21 minutes ago, Fr0sT.Brutal said:

    That's religious question and so discussions are useless. There are pro's and contra's for both options, however I'm the fan of portable self-sufficient apps personally

    I completely agree with you there. However, I dislike redundancy. if you have 3 portable 32 bit and 2 portable 64 bit programs using libmysql.dll to connect to a database I'd find it wasteful and harder to maintain in case you are updating the client library.

     

    But as you said - it all comes down to user preferences.


  2. This level of nesting is just awful. Please do not use it, instead refactor your DB structure or modify your query to return the data exactly as you expect it.

    I'm leaving this here only for you to see what spaghetti you need because of bad DB design.

    Type
      TDateOfBirthPhoneNumber = TDictionary<TDateTime, String>;
      TSurnameAndRest = TObjectDictionary<String, TDateOfBirthPhoneNumber>;
      TUsernameAndRest = TObjectDictionary<String, TSurnameAndRest>;
    
      TUserCache = Class
      strict private
        _cache: TUsernameAndRest;
      public
        Constructor Create; ReIntroduce;
        Destructor Destroy; Override;
        Procedure AddRecord(Const inUserName, inSurName: String; Const inDateOfBirth: TDateTime; Const inPhoneNumber: String);
        Function ContainsRecord(Const inUserName, inSurName: String; Const inDateOfBirth: TDateTime; Const inPhoneNumber: String): Boolean;
      End;
    
    implementation
    
    Procedure TUserCache.AddRecord(Const inUserName, inSurName: String; Const inDateOfBirth: TDateTime; Const inPhoneNumber: String);
    Begin
      If Not _cache.ContainsKey(inUserName) Then _cache.Add(inUserName, TSurnameAndRest.Create([doOwnsValues]));
      If Not _cache[inUserName].ContainsKey(inSurname) Then _cache[inUserName].Add(inSurname, TDateOfBirthPhoneNumber.Create);
    
      _cache[inUserName][inSurname].AddOrSetValue(inDateOfBirth, inPhoneNumber);
    End;
    
    Function TUserCache.ContainsRecord(Const inUserName, inSurName: String; Const inDateOfBirth: TDateTime; Const inPhoneNumber: String): Boolean;
    Begin
      Result := _cache.ContainsKey(inUserName) And _cache[inUserName].ContainsKey(inSurname) And
                _cache[inUserName][inSurName].ContainsKey(inDateOfBirth) And
                (_cache[inUserName][inSurName][inDateOfBirth] = inPhoneNumber);
    End;
    
    Constructor TUserCache.Create;
    Begin
      inherited;
    
      _cache := TUsernameAndRest.Create([doOwnsValues]);
    End;
    
    Destructor TUserCache.Destroy;
    Begin
      FreeAndNil(_cache);
    
      inherited;
    End;

    For your information, I did not try this. High possibility of AVs!


  3. On 12/15/2022 at 4:12 PM, Fr0sT.Brutal said:

    I was sure the manner of trashing Windows folder with 3rd party libs have gone long ago.

    There's more than enough trash there already even at a clean installation so it doesn't really count 🙂

     

    Anyway, it's the easiest solution to support all your 32 and 64 bit apps with everything they might need. After all, Microsoft is doing the same with msvcr*.dlls-s when you install the runtime...

    • Like 1

  4. Sorry about it, then. I really thought these methods are already in 10.2.

     

    You can check where an exception is raised in System.JSON in that earlier version and then simply backtrack to see what conditions you need to meet.

     

    Unfortunately I don't have a 10.2 installation anymore to check.


  5. Result:

    image.thumb.png.c16f1feb251a0d10847cabc1fcdb797a.png

     

    The code was written in D11 but I tried to keep it as simple as possible. I hope D4 supports the below calls...

    Type
    
      TTreeNodes = Array Of TTreeNode;
    
    
    procedure TForm1.Button3Click(Sender: TObject);
    Var
     added: TTreeNodes;
    begin
       SetLength(added, 0);
    
       CopyNodes(Source, Target, added);
       Target.Expanded := true;
    end;
    
    
    function TForm1.IsInArray(inTreeNode: TTreeNode; inArray: TTreeNodes): Boolean;
    Var
     a: Integer;
    begin
      Result := False;
    
      For a := 0 To Length(inArray) - 1 Do
        If inArray[a] = inTreeNode Then
        Begin
          Result := True;
          Exit;
        End;
    end;
    
    
    procedure TForm1.CopyNodes(inSource, inDestination: TTreeNode; var inCopied: TTreeNodes);
    Var
     a: Integer;
     tv: TTreeView;
     tn: TTreeNode;
    begin
     tv := inSource.TreeView As TTreeView;
    
     For a := 0 To inSource.Count - 1 Do
      Begin
        If Not IsInArray(inSource.Item[a], inCopied) Then
        Begin
          tn := tv.Items.AddChild(inDestination, inSource.Item[a].Text);
    
          // Do data copying, whatever
    
          SetLength(inCopied, Length(inCopied) + 1);
          inCopied[Length(inCopied) - 1] := tn;
    
          If inSource.Item[a].Count > 0 Then
            CopyNodes(inSource.Item[a], tn, inCopied);
        End;
      End;
    end;

     

    • Thanks 1

  6. 42 minutes ago, Mark- said:

    Thanks, it is not. I have 10.4 but use 10.2. I think I have 10.4 installed on a VM somewhere.

    What overloaded versions you have? Any with TJSONParseOptions will do.


  7. I only have 10.4.2 so I don't know if this version is available on 10.2, but you can try with

     

    var
     jv: TJSONValue;
     tb: TBytes;
    begin
     tb := TEncoding.UTF8.GetBytes(Edit1.Text);
    
     Try
         jv := TJSONObject.ParseJSONValue(tb, 0, [TJSONObject.TJSONParseOption.IsUTF8, TJSONObject.TJSONParseOption.RaiseExc]);
         jv.Free;
     Except
       On E:EJSONParseException Do
       Begin
         ShowMessage('JSON can not be parsed at path ' + E.Path + ', line ' + E.Line.ToString + ', position ' + E.Position.ToString);
       End
       Else
         Raise;
     End;


     


  8. Aaaah, okay, it's clear now. I thought LibraryLocation isn't working for you for some reason.

    @miab's version is a lot more elegant though, that way all your apps can access their correct version of DB libs without any further effort.


  9. In theory LibraryLocation should work just fine, I'm using it in my main application without issues. There's no reason copying it right next to the executable would be needed.

    This might worth looking into.

     

    Can this be reproduced in a minimal app?


  10. Just now, superc said:

    When compile Zeos for 64 bit ZComponentDesign not compile for 64 bit with error:

     

    [dcc64 Fatal Error] ZComponentDesign.dpk(36): E2202 Required package 'designide' not found. It's normal?

    Completely. Pure design time packages can only be compiled in 32 bit as the Delphi IDE itself is 32 bit only.

    If the runtime packages already compiled to 64 bit you are good to go. Just install the design time package and start coding 🙂


  11. We have a fairly large C# solution at work, currently using Visual Studio as an IDE. Slow as hell, sluggish and memory-hog, but Intellisense did not stop to function for me; not even once in the past 4 years.

    Delphi's LSP was never "stable". I tried D10.2, 10.4, 10.4.2 and now 11.2. Even in relatively small projects (~30-60 units) Code navigation (Ctrl + Click) is uncommon, Code completion usually shows only the default options, wiggly lines everywhere but you still can run your project just fine.

     

    I prefer Delphi and do dislike VS as an IDE, but Intellisense is superior in every way in my opinion. LSP is just still immature.


  12. 11 hours ago, superc said:

    The bug you mention has nothing to do with Zeos. Delphi 11 messed up the default library location and attempted to use 32bit DCUs on Win64 platforms. You can even correct it by hand after installation, there was even a post about it here in the D11 thread. I believe this was the faulty entry, just add "\Win64" after it and it starts to work.

    Capture.PNG

     

     


  13. 10 hours ago, superc said:

    I'm tryied now to set LibraryLocation on runtime but I'm obtain same error, it's so frustating. But the question is, on Delphi 11.2 someone use Zeos on 64 bit?

    I do, on a daily basis; I'm not connecting to PostgreSQL though (Oracle, MsSQL, MySQL and sometimes SQLite are my main targets). I can confirm that Zeos works on 64 bit with D11.2.

     

    The error above means that the given library was found but can not be used for whatever reason (LoadLibrary failed). I am playing this with MySQL x86 lately, as probably the DLL is so out-of-date (Oracle is not providing a fresh libmysql.dll for a while) that some needed functions do not exist.

    If you are absolutely sure the architecture of the DLL matches your project target, try using a more recent version. Or an alternative, if that exists - in my example, libmariadb.dll x86 works just fine.

     

    Edit: consider using Zeos 8. It's not yet considered as "release-ready" officially but it's rock solid and brings some great improvements. Afaik, it mostly lacks documentation only.

    Edit-edit: Zeos 8 comes with a D11 package so you can just double-click and install.


  14. As it turns out , manually you can use TJSONValue.ParseValue and TJSONByteReader just like TJSONValue.ParseJSONValue does. But instead of throwing an exception, just mark the wrong items, using TJSONByteReader.OffsetToPos to get the line / character position from the offset.

    When an error happens, just skip 1 byte in TJSONByteReader and repeat.

     

    In theory, this could work.


  15. 1 minute ago, Stano said:

    Kinda OT

    As a lay amateur, I am puzzled by the fact that there are several files in one BLOB. Too much.
    I would save each file separately from the very beginning. I was already taught that with modern DBs the number of records in the table is not a problem.
    But I don't know your program, nor its philosophy and function.

    There can be multiple reasons

     

    Wrong database design from the beginning

    Either the person had no understanding how it should be done or thought that the small amount of data what it will hold won't cause further issues. Then it grew out of control.

    To be honest (especially in newer applications) developers tend to completely ignore edge cases, lower hardware specs. "It works on my PC"

     

    Legacy dragging on

    This is exactly the case at us at work. Application was created ~20 years ago, when DB backends worked completely different and the architecture was functioning correctly. Now with thousands of tables and TBs of data stored it's not an easy task to start to split these so when a customer complaint comes in, we fix that one. At this speed the redesign won't be done in the next 5 years for sure and expose us to a risk that once done we'll have to start it again due to backend changes.

     

    Can't reproduce

    Not just the amount of data but the data itself can cause issues. Generally speaking, due to GDPR and confidentiality we, the developers aren't allowed to access and work with production data. When an export is needed, data is obfuscated before loaded in our test systems and this obfuscation itself is already changing the data. Once I spent days before figuring out that a rendering issue was caused by a BOM left in a BLOB field (customer just uploaded a file instead of using our editor), which was simply trimmed during obfuscation.

     

    As quick as possible, no future in mind

    This is especially true for commercial software unfortunately. Customers want more and more, quicker and quicker. In lots of occasions new features are added on a "quantity over quality"-basis. Just get it to work, get us paid, customer is happy - at least for now. This isn't necessarily a management issue as recognizing the need for, as well as optimization itself needs knowledge, time and manpower. Depending on the size, a company can lack many of this.

     

    These are just my 2 cents. Nothing is unfixable, question is always: at what cost.

    • Like 1

  16. I implemented the DDE API's execution code and there is a significant difference immediately. If you first open a large project (.dproj) which takes about 5-10 seconds to load, then a form / frame which takes about the same in rapid success, when using WIndows messages the form is often not loaded however the DDE server says it was accepted and processed.

    When using the DDE API, both are opened successfully.

     

    Maybe something is missing from the WIndows message version...?

     

    If it doesn't disturb you or you are not opening multiple files right after each other, you can safely ignore this.


  17. @Attila Kovacs If you are interested, I managed to get execution of DDE commands to work with the API so you can ditch all windows message logic:

    Procedure TAEDDEManager.ExecuteCommand(Const inCommand: String; Const inConv: HConv; Const inTimeoutInMs: Cardinal = 5000);
    Var
      datahandle: HDDEData;
      res: LongInt;
    Begin
      datahandle := DdeCreateDataHandle(_ddeid, @PChar(inCommand)[0], Length(inCommand) * SizeOf(Char), 0, 0, CF_TEXT, 0);
      If datahandle = 0 Then
        Raise EAEDDEManagerException.Create('Creating data handle failed, DDE error ' + DdeGetLastError(_ddeid).ToString);
    
      If DdeClientTransaction(Pointer(datahandle), DWORD(-1), inConv, 0, CF_TEXT, XTYP_EXECUTE, inTimeOutInMs, @res) = 0 Then
        Raise EAEDDEManagerException.Create('Executing command failed, DDE error ' + DdeGetLastError(_ddeid).ToString);
    
    //  If Not DdeFreeDataHandle(hszCmd) Then
    //    Raise EDelphiVersionException.Create('Could not free data handle, DDE error ' + DdeGetLastError(_ddeid).ToString);
    End;

    This way the only method which needs reimporting is DdeInitializeW. inConv can come from DdeQueryNextServer or DdeConnect. As HDATA_APPOWNED is NOT defined as flag, freeing up the data handle will be performed on the DDE server side - that's why that last part is commented out.


  18. As it turns out, the AV isn't caused by CreateProcess or anything related to it. The reason I wasn't able to reproduce it in a small bench application is I have a TDelphiDDEManager class created, which is however unused in the sample, but it calls DdeInitializeW. In the moment I comment that line out (or won't create the DDEManager instance) the AV disappears, PID is shown correctly, everything works like a charm.

     

    Question is, is it because of improper parameterization (like in UnpackDDElParam and FreeDDElParam) or I'm calling that wrong...

     

    Anyway, getting closer 🙂


  19. 5 hours ago, Remy Lebeau said:

    Using the Addr() intrinsic in that way will give you the memory address of the _TaskDialogIndirect variable itself, not the memory address that the variable holds.  Try inspecting just _TaskDialogIndirect by itself, or at least cast it to a Pointer.

    Unfortunately (?) yeah, it has a valid address:

    image.png.5a6ccec20765700841896a941ea62c48.png

     

    There is also a check around this block in CommCtrl, if the method is not found we wouldn't even come to this block:

        if ComCtl32DLL <> 0 then
        begin
          @_TaskDialogIndirect := GetProcAddress(ComCtl32DLL, 'TaskDialogIndirect');
          if Assigned(_TaskDialogIndirect) then
            Result := _TaskDialogIndirect(pTaskConfig, pnButton, pnRadioButton,
              pfVerificationFlagChecked) // Exception is raised here
        end;

     


  20. 25 minutes ago, Remy Lebeau said:

    That means you are trying to call a function through a nil function pointer.

    Did you check to see whether _TaskDialogIndirect is nil or not?

    I only called ShowMessage, which requires nothing but a string to display. I checked all parameters and confirmed that pszContent indeed contains the PID I'm about to display, but never thought of _TaskDialogIndirect being nil... it's in Delphi VCL after all.

    I just checked and yes it seems, it does point to a valid memory address:

     

    image.png.5e2bf6b9793173c8ad0c3df1d4a48236.png

×