Jump to content

aehimself

Members
  • Content Count

    1073
  • Joined

  • Last visited

  • Days Won

    23

Everything posted by aehimself

  1. aehimself

    AE BDSLauncher

    There were numerous updates since the last post: Auto-detect Delphi version is now a valid option in rules Window sizes are saved and restored when launched again Added extensive logging in case something goes wrong An icon now appears on the taskbar if only version selector window is shown Rules can be rearranged with drag & drop and can be renamed Changed how rules appear in the list, making it a bit more eye-friendly Version selector window can be closed with the ESC key If Shift is pressed while a file is launched, rule checks will be bypassed and the version selector will show up instead A single file can be launched from the rule editor (main) window. The launching process is exactly the same so it can be used to test the rules The logic for rule processing and opening a file by any rules has been moved to the main form, now RuleEngine only stores the rules - as it should I also had the chance to move it to my developer VM and can confirm that the following are working properly: Taking over file extensions do preserve icons and descriptions Project Version detection is working properly: Delphi 7, 10, 10.1, 10.2, 10.4 and 11.2 is detected and can be controlled: I did have an issue when starting a file width Delphi 7 as it kept popping messages up that some packages couldn't be loaded. As any file could be launched from the command line I suspect maybe UAC / other permission issue and I might need to add some more parameters to the CreateProcess I'm currently using. However, after I pressed Yes on all of these the problem simply went away by itself... so I'll not be able to experiment and debug it anymore 😞
  2. aehimself

    AE BDSLauncher

    Some items from the to-do list are completed: File associations are now created by copying all details from the current Delphi association. This means that (in theory) icons and descriptions will stay the same, but launched with AE BDSLauncher. Unfortunately I could not test this because my associations were already messed up by Delphi and have no icons of anything... 😕 If no rule was able to launch the file, the version selector will select the Delphi version based on the contents of the .dproj, .bdsproj or .dof file. If it was created with a version which is not installed, an error message is shown, and the latest version is selected. Rules are now parsed based on an actual order, not alphabetically. For the time being reordering only can be done with the "Move up" or "Move down" option, I'll extend this with the classic drag & drop in the future. Also, Fixed some appearance issues and an AV if the program is started up with no rules Fixed parameter handling. Quotes are now removed if quoted, if unquoted all parameters will be appended to a single file name. Therefore, these two commands will be able to open the file correctly: BDSLauncher "C:\Path to my project\Project1.dproj" and BDSLauncher C:\Path to my project\Project1.dproj I'll probably beautify the detection code to similar of what dzBdsLauncher has as it's a lot more sophisticated than my if-s but at least it was quick to write 🙂 Due to detection is now possible, I'll probably add it as a possible option for a Delphi version in the rules.
  3. aehimself

    AE BDSLauncher

    Source can be obtained at https://github.com/aehimself/BDSLauncher. Some small changes have been made: If no caption filter is set, it means any running instance can be chosen to open the file with. Always use a new instance is now a separate option If there's only one installed version and no running instances are found, no selector will be shown, a new instance is launched automatically File associations can be taken over and given back to the previous Delphi. For the time being this only includes .pas, .dpr, .dproj, .dfm and .groupproj Left to do: Everything mentioned in the first post At the moment all the file associations are taken over with the description "AE BDSLauncher file" and the same icon as the application. In reality it should be separated, like how original Delphi does it Attempting to guess the Delphi version in .dpr and .dproj files based on @dummzeuch's request Feel free to try it out but don't expect it to work perfectly and look nice 🙂
  4. aehimself

    AE BDSLauncher

    That is actually a good idea and wouldn’t be too hard I presume. If the information was found that version will be selected in the combo box by default. I’ll also have to rethink when the selector appears. It might be good for some to launch a new instance for each file and will probably find it very annoying that a window pops up each time a file was double clicked… As a last resort a “catch-all” rule could be set up to hide the popup… Anyway, I’ll have to think this through.
  5. aehimself

    App crash on close when windows style = windows

    I had issues like this when I used a combo box to change the style of the application in the OnChange handler. I suppose it had to do something with the dropdown window still visible. On the other hand don't be surprised, VCL themes are quite unstable. Since upgrading to 11.2 my main application started to throw similar AVs upon closure, but coming from DBGrid's style hook.
  6. aehimself

    Delphi & WM_DDE_INITIATE

    I am in a bit of a dazzle here. However it wouldn't be a surprise in general, but is it possible that the Delphi IDE doesn't (and never did) handle WM_DDE_INITIATE correctly? In a different post, @Attila Kovacs managed to get DDE functioning properly on a modern IDE, but I realized that Delphi 7 for example doesn't reply to any conversation request with service "bds" and topic "system". Service is usually the executable name, this is confirmed by the registry entry: However it says nothing about a topic. I experimented with sending empty strings, nulls, "system" or "DELPHI32" directly to windows and HWND_BROADCAST without success. According to Microsoft: Basically, broadcasting 0-0 will end up discovering all available DDE services and topics for use. explorer.exe behaves correctly: However Delphi 7 and Delphi 10.4.2 does not reply with anything. Is it possible that the Delphi IDE never handled WM_DDE_INITIATE messages correctly? If yes, what documentation I have to dig up to get DDE details for earlier Delphi IDEs? The code I used is: unit Unit1; interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls; type TForm1 = class(TForm) Memo1: TMemo; procedure FormCreate(Sender: TObject); private procedure DdeWndProc(var AMsg: TMessage); public { Public declarations } end; var Form1: TForm1; implementation Type TDDEInfo = Record WindowHandle: HWND; ServiceAtom: Word; TopicAtom: Word; End; PDDEInfo = ^TDDEInfo; {$R *.dfm} Function FindDelphiWindow(inHWND: HWND; inParam: LParam): Boolean; StdCall; Var ppid: Cardinal; title, classname: Array[0..255] Of Char; Begin // https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/ms633498(v=vs.85) // Result := True -> Continue evaluation // Result := False -> Do not continue evaluation Result := True; GetWindowThreadProcessID(inHWND, ppid); // if ppid <> 7196 then // Exit; GetWindowText(inHWND, title, 255); GetClassName(inHWND, classname, 255); Form1.Memo1.Lines.Add('PID: ' + ppid.ToString + ', title: "' + title + '", class: "' + classname + '"'); SendMessage(inHWND, WM_DDE_INITIATE, PDDEInfo(inParam)^.WindowHandle, Makelong(PDDEInfo(inParam)^.ServiceAtom, PDDEInfo(inParam)^.TopicAtom)); End; procedure TForm1.DdeWndProc(var AMsg: TMessage); Var service, topic: PChar; begin if Amsg.Msg = WM_DDE_ACK then Begin GetMem(service, 256); GetMem(topic, 256); Try GlobalGetAtomName(AMsg.LParamLo, service, 255); GlobalGetAtomName(AMsg.LParamHi, topic, 255); Memo1.Lines.Add('ACK from ' + AMsg.wParam.ToString + ': service: ' + service + ', topic: ' + topic); Finally FreeMem(service); FreeMem(topic); End; // PostMessage(HWND(AMsg.wParam), WM_DDE_TERMINATE, AMsg., 0); End; end; procedure TForm1.FormCreate(Sender: TObject); Var di: PDDEInfo; Begin New(di); di^.WindowHandle := AllocateHWND(DdeWndProc); Try di^.ServiceAtom := 0; // GlobalAddAtom(PChar('DELPHI32')); di^.TopicAtom := 0; // GlobalAddAtom(PChar('DELPHI32')); Memo1.Lines.BeginUpdate; Try Memo1.Lines.Clear; EnumWindows(@FindDelphiWindow, LParam(di)); Finally Memo1.Lines.EndUpdate; End; // GlobalDeleteAtom(di^.ServiceAtom); // GlobalDeleteAtom(di^.TopicAtom); Finally DeallocateHwnd(di^.WindowHandle); End; end; end.
  7. aehimself

    Delphi & WM_DDE_INITIATE

    My idea seems to be correct after all, I should add my daughter right next to the rubber duck on my desk. I don't know what happened when I tried to run the code under D7 but if I change every call to the WinApi "A" version in D10.4.2 it simply works... And yes, I can confirm... Delphi 7 supports DDE, needs the service "DELPHI32" and the topic "system". It just has to be ANSI, not Unicode.
  8. aehimself

    Delphi & WM_DDE_INITIATE

    I know, this is one of the questions in this topic. As far as I understand, a DDE server should reply with one WM_DDE_ACK message for each service / topic pair it supports, but Delphi doesn't seem to do that. Neither 7, neither 10.4.2. As for being Unicode it was a good guess but it doesn't work. Running the same code from D7, or changing GlobalAddAtom to GlobalAddAtomA yields no results. I guess there's only one question. As detection doesn't seem to work, in which documentation / source file I can look for supported DDE service / topic strings for Delphi? Edit: Ummm...
  9. aehimself

    Delphi & WM_DDE_INITIATE

    Sometimes it's good to take a break from something. I went to play with my daughter and realized... D7 doesn't understand Unicode! Maybe it receives the message but it will see only it's first character... Will have to test this theory later out though.
  10. So, the sample data you mentioned is not a valid JSON document, but one separate JSON object per line as far as I can see. Maybe this is why TJSONTextReader fails - I don't know, I never used it. If the data will always arrive in this format and efficiency / optimization is no concern, you can do something like... Var line: String; jo: TJSONObject; // jv: TJSONValue; Begin For line In TFile.ReadAlltext('C:\[...]\restaurants.json') Do Begin jo := TJSONObject(TJSONObject.ParseJSONValue(line)); If Not Assigned(jo) Then Continue; // invalid JSON code in one line Try // ShowMessage(jo.GetValue('borough').Value); // For jv In jo.GetValue('grades') As TJSONArray Do // Begin // ShowMessage((jv As TJSONObject).GetValue('grade').Value); // ((jv As TJSONObject).GetValue('score') As TJSONNumber).AsInt // End; Finally jo.Free; End; End; End; The bad thing about this logic is that it reads the whole contents of the file in the memory, which will stay there until processing is complete. With 12 Mb this is not an issue, but you might hit a barrier if the data is in the GBs range. In that case you can use a TFileStream, read into a String until sLineBreak (or .Position = .Size) and build each JSON object from this. The processing logic can stay the same.
  11. I'm building a tool which searches for and opens Delphi source files. Until now execution was a simple ShellExecute call to the .pas file and everything was working just file. Now, as a second Delphi version is introduced I had to change the code to support opening said file in a specific Delphi version. My issue is that a simple call to bds.exe will always launch a new Delphi instance, and I need to re-use the one currently running. Basically I need to mimic the IDEs behavior when double-clicking a .pas file in Explorer. Unfortunately I could not find any documentation about the parameters of BDSLauncher, nor any relevant parameters of Bds.exe which could achieve this. I also built a small program which only lists the parameters it was started with and temporarily replaced BdsLauncher.exe and then bds.exe to see what it received when something is double-clicked, but all I got was the full path of bds.exe. Can anyone point me to the right direction? Thanks!
  12. aehimself

    How to open a file in the already running IDE?

    @Attila Kovacs Your discovery method is inefficient. DDE_INITIATE is meant to be sent to HWND_BROADCAST and whoever supports it will reply. You also can identify the window who replied, as a WM_DDE_ACK message sent as a reply to DDE_INITIATE contains the DDE server window handle as WParam. Basically... Procedure TAEDDEManager.DiscoveryHandler(Var inMessage: TMessage); Var whandle: HWND; pid: Cardinal; Begin If inMessage.Msg <> WM_DDE_ACK Then Exit; whandle := inMessage.WParam; GetWindowThreadProcessId(whandle, pid); // Do something with the PID and the window handle of the process End; Procedure TAEDDEManager.RefreshServers; Var discoverywnd: HWND; serviceatom, topicatom: Word; msg: TMsg; res: Cardinal; Begin discoverywnd := AllocateHWnd(DiscoveryHandler); Try serviceatom := GlobalAddAtom(PChar(_service)); Try topicatom := GlobalAddAtom(PChar(_topic)); Try SendMessageTimeout(HWND_BROADCAST, WM_DDE_INITIATE, discoverywnd, Makelong(serviceatom, topicatom), SMTO_BLOCK, 1, res); While PeekMessage(msg, discoverywnd, 0, 0, PM_REMOVE) Do Begin TranslateMessage(msg); DispatchMessage(msg); End; Finally GlobalDeleteAtom(topicatom); End; Finally GlobalDeleteAtom(serviceatom); End; Finally DeallocateHWnd(_discoverywnd); End; End; And yes, I'll change my logic to window messages too. After long weeks of troubleshooting discovery still locks up after the fourth run when I'm using the DDE API 🙂
  13. aehimself

    Zeos on Delphi 11.2

    Again - you are correct but only if said library is redistributed with a software. For example, libmysql and oci is not allowing this (libmariadb might but I never read their EULA) and Oracle is notorious of shipping faulty software. Therefore, I'd update them at all places even if I have issues in only one software. LibSSL is also a good example from this perspective. If a serious security flaw is found, it's easier to update only 2 files per architecture than scanning your entire drive.
  14. aehimself

    Zeos on Delphi 11.2

    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.
  15. aehimself

    ClientDataset.Filter ( Non English Character )

    If you don't have a chance to upgrade, you can take a look at Zeos. It handles Unicode filters and should be able to connect to your preferred RDBMS.
  16. 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!
  17. A TList would be better choice but I'm not sure D4 has it... that's why I settled with an array.
  18. aehimself

    Zeos on Delphi 11.2

    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...
  19. aehimself

    JSON text validation...

    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.
  20. Result: 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;
  21. aehimself

    JSON text validation...

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

    JSON text validation...

    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;
  23. aehimself

    Zeos on Delphi 11.2

    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.
  24. aehimself

    Zeos on Delphi 11.2

    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?
×