-
Content Count
1053 -
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
It's not a threading issue, it's 64-bit issue! According to MSDN, it's declared as: BOOL UnpackDDElParam( [in] UINT msg, [in] LPARAM lParam, [out] PUINT_PTR puiLo, [out] PUINT_PTR puiHi ); In Delphi, however: Now, we are feeding it an LParam, which is NativeInt. Time to re-import it, correctly this time 🙂 -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Your original idea was good, I'm indeed sending WM_DDE_INITIATE first. However attempts to purge the queue is unsuccessful, as there is nothing in the message queue: msghwnd := AllocateHWnd(nil); Try atomservice := GlobalAddAtom(PChar(_service)); atomtopic := GlobalAddAtom(PChar(_topic)); Try SendMessage(inDDEServerHWND, WM_DDE_INITIATE, msghwnd, Makelong(atomservice, atomtopic)); Finally GlobalDeleteAtom(atomservice); GlobalDeleteAtom(atomtopic); End; While PeekMessage(msg, msghwnd, 0, 0, PM_REMOVE) Do Begin Sleep(20); End; I split LParam by LoWord and HiWord, in that case FreeDDElParam(msg.message, msg.lParam) throws an AV... it's like LParam is malformed somehow... -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
...any idea why this throws an AV? DDE is initialized inside the thread's context, I think the PeekMessage's section runs in the thread's context too... The message goes through though, file is opened in the IDE, msg.WParam indeed contains the handle of the DDE server, msg.hwnd is equal to the handle of the window created in the beginning... I don't know what else to check 😞 msghwnd := AllocateHWnd(nil); Try [...] PostMessage(inDDEServerHWND, WM_DDE_EXECUTE, msghwnd, commandhandle); SetTimer(msghwnd, 1, inTimeOutInMs, nil); Repeat If PeekMessage(msg, 0, 0, 0, PM_REMOVE) Then Begin If msg.message = WM_TIMER Then Break; If msg.message = WM_DDE_ACK Then Begin If UnpackDDElParam(msg.message, msg.lParam, @pLo, @pHi) Then // AV is thrown on this line Begin GlobalUnlock(pHi); GlobalFree(pHi); FreeDDElParam(msg.message, msg.lParam); PostMessage(msg.wParam, WM_DDE_TERMINATE, msghwnd, 0); Exit; End; End; TranslateMessage(msg); DispatchMessage(msg); End; Sleep(200); Inc(wait, 200); Until wait >= inTimeOutInMs; Finally DeallocateHWnd(msghwnd); End; -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Okay, this version works! Probably the delayed freeup of resources, in my case when the message arrived the handles were already invalid. Now, just to get rid of the GetMessage loop, as it breaks the ability to be run in a thread. Or I need a message pump 🙂 -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
I made a discovery... At the moment there are 3 possible outcomes: - You start it from the IDE, works. Period. - You start it from outside of the IDE, WHILE the IDE is running -> Getting the lust succeeds but file is not opened: - You start it from outside of the IDE, and NO IDE is running -> 16349. This makes me believe that 16349 should be handled separately, it means there is no list to be gathered... which equals to no Delphi instances running. Does this make sense...? -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
If HWND(inMessage.WParam) = _ddehwnd Then If UnpackDDElParam(inMessage.Msg, inMessage.lParam, @pLo, @pHi) Then Begin GlobalUnlock(pHi); GlobalFree(pHi); FreeDDElParam(inMessage.Msg, inMessage.lParam); End; The innermost section never gets executed, so yes, it indeed stops the AV from happening 🙂 Now only unable to connect from outside the IDE remains. I'm still trying to find that article. 😄 -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
It only fails on one machine, on mine it works. Maybe it's an AV-related thing... Edit: DDE version locks up the IDE completely if ran on the machine which is affected, also throws an AV here after the WM_DDE_INITIATE message is sent: When starting it outside from the IDE, reports the same DDE error: I also can confirm, running my program on my machine outside of the IDE also fails to join the conversation. Crap, I remember reading something about DDE behaving differently from the IDE somewhere... -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
So basically the difference is the cleanup in the message handler? Afaik ackINIT does nothing, as the window is not even alive in that section. Question, why are you reading back out the partner and service name? In theory they will always come back as you specified in DdeConnectList because you are defining both parameters. Edit: first test, after PC restart, no IDE was opened yet: 😄 -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
My code is your initial version, I'm not processing anything 🙂 msghwnd := AllocateHwnd(nil); Try atomservice := GlobalAddAtom(PChar(DDESERVICE)); atomtopic := GlobalAddAtom(PChar(DDETOPIC)); Try SendMessage(_ddehwnd, WM_DDE_INITIATE, msghwnd, Makelong(atomservice, atomtopic)); Finally GlobalDeleteAtom(atomservice); GlobalDeleteAtom(atomtopic); End; cmd := '[open("' + inFileName + '")]'; commandhandle := GlobalLockString(cmd, GMEM_DDESHARE); Try PostMessage(_ddehwnd, WM_DDE_EXECUTE, msghwnd, commandhandle); Finally GlobalUnlock(commandhandle); GlobalFree(commandhandle); End; Finally DeAllocateHwnd(msghwnd); End; Maybe I need to switch up that PostMessage to SendMessage. I'll give that modification a spin. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
Hehe, you are right. Just this moment I entered a locked-up state again. 16394 until restarting the IDE. Starting the application from within our outside the IDE makes no difference. Double-clicking a file in explorer indeed does open the file in Delphi. It's strange but I THINK it started with the usual command-sending issue: WM_DDE_EXECUTE goes out but file is NOT opened in the IDE. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
The window message version caused more havoc than it could solve. While collecting DDE targets worked more reliably, it interfered with sending commands. The finding no instances symptom was caused by the list connect, not Delphi's DDE server; adding error handling revealed that: convlist := DdeConnectList(ddeid, svchandle, topichandle, 0, nil); If convlist = 0 Then Raise EDelphiVersionException.Create('Retrieving the list of Delphi DDE servers failed, DDE error ' + DdeGetLastError(ddeid).ToString); threw DMLERR_NO_CONV_ESTABLISHED when something was "locked up". I'm now playing around with res := DdeInitializeW(ddeid, nil, APPCMD_CLIENTONLY, 0); Seems to be stable so far. -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
This idea won't work as intended, as you are sending all the messages to all TPUtilWindows of all instances you'll find. At least for my purpose, where I am separating Delphi IDE instances by versions. The theory is interesting though. You think one Delphi instance can have more DDE servers, which are constantly being destroyed / created? -
How to open a file in the already running IDE?
aehimself replied to aehimself's topic in Delphi IDE and APIs
And with this, you made all calls work fine from a background thread! -
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.