-
Content Count
1090 -
Joined
-
Last visited
-
Days Won
23
Everything posted by aehimself
-
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
I'm using the code to start an IDE if nothing was detected and open a file in it. Once the first instance can not even be detected anymore I still can control the second just fine. This makes me believe the issue is with the DDE server of a specific instance. There are two options. Either we lock up Delphi's DDE server with our code, or Delphi's DDE server is buggy and crashes by itself. Once an instance locks up I'll see if I can double-click on a file in Explorer to open it. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Btw, stability issue seem to be solved by calling DdeDisconnectList when finished. I'll push an update soon. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
I took the liberty to include everything in a small, easy to use package: https://github.com/aehimself/AEFramework/blob/master/AE.DelphiVersions.pas Usage: Var dv: TDelphiVersions; v: TDelphiVersion; begin dv := TDelphiVersions.Create; Try For v In dv.InstalledVersions Do If v.IsRunning Then v.OpenFile('C:\a.pas'); Finally FreeAndNil(dv); End; End; Multiple DDE queries clearly lock something up, I suspect calling .IsRunning in an infinite loop will turn from True to False after a while. Also, OpenFile will raise an AV if there are no instances. I'll add some error handling later. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Had that in mind but I'm afraid of using version numbers, as you have to consider patch levels in each version. I guess it might change bds.exe version number too. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Yep, similar to my approach. I moved this to a separate function though, but logic is the same. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Happened to me too, however a simple Delphi restart solved it. Maybe DDE server died in Delphi, as DdeQueryNextServer found no matches. Anyway, I added some information gathering and now I have the full process name of each process which was found. From here, I only need to enumerate the bds paths in Registry to find which Delphi version that executable is for. Little bit more code than I expected, but at least it works! Thank you! -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Perfection, works like a charm. It triggered my antivirus though, hope won't happen in the real application 🙂 -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
How...?! It's the same garbage here, only it appears in the IDE. I thought autoconnecting makes a difference, but that doesn't seem to be the case... I did not find the needed WinApi calls yet. I might look into it, seems something is broken in TDDEClientConv between XE4 and 11.2... -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
I remember seeing it on a really old newslist that TDDEClientConv heavily suffers from bad coding and I tend to believe something is not right with it in this case. I already tried with simple casting, StrPCopy, sending @PAnsiString[1] instead of PAnsiChar, even like this: Var pac: PAnsiChar; s: String; tb: TBytes; len: Integer; begin s := '[open("' + Edit1.Text + '")]'; tb := TEncoding.Default.GetBytes(s); len := Length(tb); GetMem(pac, len + 1); ZeroMemory(pac, len + 1); Move(tb[0], pac^, len); ... but BDSLauncher always replies with some Chinese characters what it tried to execute as a command. I can not get more PAnsiChar than this. I'll try to find a different component to check. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Strange to me too, but yes, I am: -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
When stepping in .ExecuteMacro, a message box actually pops up but disappears right after the line hdata := DdeClientTransaction(Pointer(hszCmd), DWORD(-1), FConv, 0, FDdeFmt, XTYP_EXECUTE, TIMEOUT_ASYNC, @ddeRslt); What it said: So I guess the command is incorrect, Delphi really didn't like the casting. Now attempting to properly build my command, because with Var pac: PAnsiChar; s: AnsiString; begin s := '[open("' + Edit1.Text + '")]'; pac := PAnsiChar(s); If Not DDEClientConv1.ExecuteMacro(pac, False) Then Begin my result is: 🙂 -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Getting close 🙂 Never worked with DDE so it'll take some attempts to succeed I believe. So far I have the following code: Memo1.Lines.Add(sLineBreak + '----------------------------------------'); Memo1.Lines.Add('Opening ' + Edit1.Text); // DDEClientConv1.ServiceApplication := '"C:\Program Files (x86)\Embarcadero\Studio\22.0\bin\bdsLauncher.exe" "C:\Program Files (x86)\Embarcadero\Studio\22.0\bin\bds.exe" /np'; DDEClientConv1.ServiceApplication := 'C:\Program Files (x86)\Embarcadero\Studio\22.0\bin\bdsLauncher.exe'; Memo1.Lines.Add('Setting DDE link...'); If Not DDEClientConv1.SetLink('bdsLauncher', 'system') Then Begin Memo1.Lines.Add('Setting link failed!'); Exit; End; Memo1.Lines.Add('Opening DDE link...'); If Not DDEClientConv1.OpenLink Then Begin Memo1.Lines.Add('Opening link failed!'); Exit; End; Try Memo1.Lines.Add('Invoking DDE command...'); If Not DDEClientConv1.ExecuteMacro(PAnsiChar('[open("' + Edit1.Text + '")]'), False) Then Begin Memo1.Lines.Add('Invoking command failed!'); Exit; End; Finally Memo1.Lines.Add('Closing DDE link...'); DDEClientConv1.CloseLink; End; No errors are shown but nothing gets executed and I get a ding sound. No popups, no entries in the event log. Anyone has experience debugging DDE, where should I look for errors? I took all the keywords from the registry, HKCU\BDE.pas. ServiceApplication came from Command\Default, SetLink parameters from ddeexec\application\Default and ddeexec\topic\Default, the macro from ddeexec\default. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
My guess was on some tricky Windows messages, but DDE is a valid option as well. As I have no source for BDSLauncher I can not say. I took a peek at @dummzeuch's dzBdsLauncher but that also is executing a direct bds.exe call, probably resulting in a new IDE. Finding the method and replicating it would be the goal of this topic. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
I do believe that's more than enough, please try not to steer the conversation away. Can we get back to the topic, please? -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
No, it does not 🙂 First, CurrentKey has a zero value after .OpenKeyReadOnly. Second, you are assigning a wrong registry path, not what the API expects: https://learn.microsoft.com/en-us/windows/win32/api/shellapi/ns-shellapi-shellexecuteinfoa hkeyClass Type: HKEY A handle to the registry key for the file type. The access rights for this registry key should be set to KEY_READ. This member is ignored if fMask does not include SEE_MASK_CLASSKEY. Edit: wrong parameter copied -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
That is really surprising as that's not how you open a registry key with TRegistry AND even if you correct that, reg.CurrentKey won't be changed after a single .OpenReadOnly. Running the code on a PC with Delphi 10.4 and 11 installed also confirms failure: it opens up the file in Delphi 10.4. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Tried. Bds.exe never starts, bdslauncher.exe never quits, just hangs. No windows, no errors, nothing. Also tried adding the necessary (?) -pDelphi switch, same result. Tried giving these parameters only to Bdslauncher and only the source file. No effect. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
This is exactly what I did. This introduces the new instance issue, which this topic is about. I'm probably missing a parameter - I just don't know which. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Please read the question again. To be able to launch with a specific Delphi version you need to start bds.exe, not just the .pas file; this is how it was until now. -
Var TokenHandle: THandle; Begin If LogonUser(PChar(UserEdit.Text), PChar(DomainEdit.Text), PChar(PasswordEdit.Text), LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT, TokenHandle) Then Try // Credentials are valid... // In case needed: If ImpersonateLoggedOnUser(TokenHandle) Then Try // Do stuff in the context of user Finally RevertToSelf; End; Finally CloseHandle(TokenHandle); End; End; User must have network login right to the PC, though.
-
The benefit is, that you are not actually storing and handling data in a visual component, e.g.: TTreeNode.Data := TMyClass.Create; While it does work it's considered bad practice as you are not separating UI and data / business logic. Anyway, let's not hijack the topic, shall we. OP might not be interested in this discussion.
-
If you mean https://github.com/pglowack/DelphiJSONComponents/blob/master/JSONTreeView.pas this only visualizes the JSON as far as I can see from the code. You still have to manually append your extra data to each node, or search in each node's children to find your OnClik, URL and Description nodes. I did, and first I used .Data to actually store data too, it was just too convenient. Lately I'm keeping my data in a separate store and .Data only points to the data in this store, if needed. This way I don't even need to free up memory when a node is deleted.
-
We had a custom component which was only a wrapper for a TStringList, you can do something like this. On the other hand I feel like we are wasting memory; to store textual data you simply can use string constants or if you don't want to convert your TXT files into Delphi strings, build a .res file from them and embed it to the compiled application. In case you wish to convert, I have a method which seemed to work in simple cases and properly splits the input into 255-length chunks while preserving line breaks and escapes apostrophes: Function ToDelphiString(Const inString: String): String; Const ADDITION = #39' + '#39; BREAKAT = 80; Var line, a, max: Integer; sb: TStringBuilder; strarr: TArray<String>; Begin sb := TStringBuilder.Create; Try sb.Append(#39); strarr := AdjustLineBreaks(inString).Split([sLineBreak]); For line := Low(strarr) To High(strarr) Do Begin max := strarr[line].Length Div BREAKAT; For a := 0 To max Do Begin sb.Append(strarr[line].Substring(a * BREAKAT, BREAKAT).Replace(#39, #39#39)); sb.Append(#39); If a <> max Then sb.Append(' +' + sLineBreak + #39); End; If line <> High(strarr) Then sb.Append(' + sLineBreak +' + sLineBreak + #39); End; Result := sb.ToString; Finally FreeAndNil(sb); End; End;
-
TTreeView is basically a wrapper for the old Windows TreeView component, which was created way before JSON. You MIGHT have some success finding JSON export in TVirtualTreeView but I wouldn't be so sure; most probably you'll have to extract that by yourself. As @haentschman mentioned storing data in a visual component is a bad practice. What I'd do is... Have a TJSONObject which stores your node information. By adding childnodes, you will already have the parent / children relationship and since each JSON object has a name, they only now have to store URL, OnClick javascript handler and description. For quick access to this information when building your TreeList based on the TJSONObject, you can assign the related object to the TTreeNode's .Data pointer. This way you didn't just separate UI / data, but simply can call TJSonObject(TreeView.Selected.Data).ToString to immediately get the selected node's JSON representation. To save the whole tree, call your main TJSONObject.ToString, to rebuild it TJSONObject(TJSONObject.ParseJSONValue). This is by using the Delphi provided System.JSON unit, but you can use any other library you prefer. The idea behind can stay the same.
-
Possible changes to string/char handling in Delphi 11(.2)?
aehimself replied to omnibrain's topic in General Help