Jump to content

Attila Kovacs

Members
  • Content Count

    1936
  • Joined

  • Last visited

  • Days Won

    25

Everything posted by Attila Kovacs

  1. Attila Kovacs

    How to open a file in the already running IDE?

    PeekMessage "Dispatches incoming nonqueued messages, checks the thread message queue for a posted message, and retrieves the message (if any exist)." "During this call, the system dispatches (DispatchMessage) pending, nonqueued messages, that is, messages sent to windows owned by the calling thread using the SendMessage, SendMessageCallback, SendMessageTimeout, or SendNotifyMessage function. Then the first queued message that matches the specified filter is retrieved." ok, that means, it should be fine
  2. Attila Kovacs

    How to open a file in the already running IDE?

    first of all, PeekMessage is blocking, therefor the repeat until is not just superfluous but could be a problem, do you know where exactly the AV happens? sorry, that was getmessage, the blocking one. well, then I don't think I can comment on that
  3. Attila Kovacs

    No one can help

    Try to find some resources on "relational database" first. Instead of stuffing everything into a giant BLOB in one record, split them up in other tables in many rows and have a reference to the main document.
  4. I'm not sure on that. Look the code in Berlin, it's searching from the left and returning the first matching element. Edit: oh no, I see. Edit edit, well, no-oh no, can't see again. ๐Ÿ˜„ If the list is sorted I can't create a list where binsearch would not return the first element in the list. Berlin U2.
  5. Why did they change it in the first place?
  6. Attila Kovacs

    How to open a file in the already running IDE?

    you need one, a window handle too
  7. Attila Kovacs

    How to open a file in the already running IDE?

    Eventually I got rid of ackMode. I should really stop it now. ๐Ÿ˜„ uBdsLauncher2.pas
  8. Attila Kovacs

    How to open a file in the already running IDE?

    There is no such thing as a central DDE server or anything, the DDE lib sends a message to every enumerated main window a WM_DDE_INITIALIZE with Service and Topic and the application will either answer with a WM_DDE_ACK or not. That said, on 16349, or if the list is zero, you have to start bds.exe /np filename.pas. That is what's in the registry for bdsLauncher.
  9. Attila Kovacs

    How to open a file in the already running IDE?

    of course, if no IDE is running there is no DDE to answer the requests
  10. Attila Kovacs

    How to open a file in the already running IDE?

    Ok, I just tested this on 4 different pc's under 3 different os's and 3 different IDE's, no errors or lockups. (Could still contain bugs) I'll just leave it there for now, if someone has a better implementation just share with us. uBdsLauncher2.pas
  11. Attila Kovacs

    How to open a file in the already running IDE?

    Well, the whole ackINIT / ackEXEC thing is just a crap because I have no idea who sent the ACK.... also, there is difference between posted and sent messages, which I also cant distinguish. The whole DDE fuck is just some botching from MS. I'm sure they can't even document it as they don't know why is it working. So it's possible that I get an ACK message and I wan't to free the globals from it's parameters, but it was not an ACK for the EXEC, so AV on nil handle raises. You could try to check the return value of "UnpackDDElParam" and only free the globals if it's != 0. if UnpackDDElParam(AMsg.Msg, AMsg.lParam, @pLo, @pHi) <> False then begin GlobalUnlock(pHi); GlobalFree(pHi); FreeDDElParam(AMsg.Msg, AMsg.lParam); end; this would eventually solve the AV but not the problem
  12. Attila Kovacs

    How to open a file in the already running IDE?

    The ackINIT was for the other version, (see attachment), I just left it there, you can put (FAckMode := ackINIT;) into the initialization, instead. (It won't affect anything though) Double-check your implementation, both versions are stable on my PC. The problem was calling GlobalUnlock(ddeCommandH) and GlobalFree(ddeCommandH) right after WM_DDE_EXECUTE. If you are still getting the 16394, I suspect, you still have this in the code. uBdsLauncher2.pas
  13. Attila Kovacs

    How to open a file in the already running IDE?

    Sorry, got a little intermezzo, I broke my IDE had to debug it to make it work again.... Well, here is the thing, user32 version with ~proper ACK handling, seems to work. Could you check it? (It's still just ~proper as the command global won't be released if there is no ACK. But I would implement it only if it became stable.) uBdsLauncher2.pas
  14. Attila Kovacs

    How to open a file in the already running IDE?

    Don't. It's Postmessage on purpose. Wait until I make the changes.
  15. Attila Kovacs

    How to open a file in the already running IDE?

    "When processing the WM_DDE_ACK message that the server posts in reply to a WM_DDE_EXECUTE message, the client application must delete the object returned by the WM_DDE_ACK message." This could cause that eventually, freeing up that global right after the execute instead of waiting for the ACK. This could also be the problem for the user32 version.
  16. Attila Kovacs

    How to open a file in the already running IDE?

    No way, this was the first thing I tried, 2 days ago. Nothing changed..
  17. Attila Kovacs

    How to open a file in the already running IDE?

    What do you mean it interfered with sending commands? Can you be more specific? I have this error handling and you will get error 16394 after the 4th call. (Again, look into the DDE logs)
  18. Attila Kovacs

    How to open a file in the already running IDE?

    There is an array with the window handles which answered to DDE_INITIATE, there is GetWindowExeName to get the executable, you have everything just like before, the only difference to your needs is, that I'm trying to open a file in every running IDE's in my demos.
  19. Attila Kovacs

    How to open a file in the already running IDE?

    by the way, if you are looking into my logs, DdeConnectList does the same. The only thing I don't know, how "DdeConnectList" is enumerating the windows. It was sending it to 128 ones.
  20. Attila Kovacs

    How to open a file in the already running IDE?

    Yes, but only the ones answering with ACK who's listening to DDE. It works very fine. Refresh the post, I've updated several times.
  21. Attila Kovacs

    How to open a file in the already running IDE?

    ok, here a native approach, finding the dde window with winapi and sending dde messages, no dde@user32 involved let's see, if it's more stable unit uBdsLauncher2; interface procedure OpenFile(const AFileName: string; ALog: boolean = False); implementation uses Winapi.Windows, Winapi.Messages, Winapi.PsAPI, System.SysUtils, System.Classes; const cDdeService = 'bds'; cDdeTopic = 'system'; var FDDEHWND: THandle = 0; FLog: boolean; FWHandles: TArray<HWND>; aService, aTopic: WORD; type TDdeHelper = class public class procedure DdeWndProc(var AMsg: TMessage); end; function EnumWindowsProc(wHandle: HWND; lParam: THandle): BOOL; stdcall; export; var ClassName: array [0 .. 255] of char; begin Result := True; GetClassName(wHandle, ClassName, 255); if SameText(ClassName, 'TPUtilWindow') then SendMessage(wHandle, WM_DDE_INITIATE, FDDEHWND, Makelong(aService, aTopic)); end; // make a string global, dont forget to unlock/free it function GlobalLockString(AValue: string; AFlags: UINT): THandle; var DataPtr: Pointer; begin Result := GlobalAlloc(GMEM_ZEROINIT or AFlags, (Length(AValue) * SizeOf(char)) + 1); try DataPtr := GlobalLock(Result); Move(PChar(AValue)^, DataPtr^, Length(AValue) * SizeOf(char)); except GlobalFree(Result); raise; end; end; function GetWindowExeName(wHandle: HWND): string; var PID: DWORD; hProcess: THandle; nTemp: Cardinal; Modules: array [0 .. 255] of THandle; Buffer: array [0 .. 4095] of char; begin Result := ''; if GetWindowThreadProcessId(wHandle, PID) <> 0 then begin hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, PID); if hProcess <> 0 then if EnumProcessModules(hProcess, @Modules[0], Length(Modules), nTemp) then if GetModuleFileNameEx(hProcess, 0, Buffer, SizeOf(Buffer)) > 0 then Result := Buffer; end; end; procedure OpenFile(const AFileName: string; ALog: boolean = False); var i: integer; ddeCommand: string; ddeCommandH: THandle; begin FLog := ALog; FDDEHWND := AllocateHWnd(TDdeHelper.DdeWndProc); try SetLength(FWHandles, 0); aService := GlobalAddAtom(PChar(cDdeService)); aTopic := GlobalAddAtom(PChar(cDdeTopic)); try EnumWindows(@EnumWindowsProc, 0); finally GlobalDeleteAtom(aService); GlobalDeleteAtom(aTopic); end; // send the DDE command to every running BDE instances ddeCommand := Format('[open("%s")]', [AFileName]); ddeCommandH := GlobalLockString(ddeCommand, GMEM_DDESHARE); try for i := 0 to High(FWHandles) do begin if FLog then WriteLn(Format('Sending command "%s" to %s', [ddeCommand, IntToHex(FWHandles[i], 8)])); PostMessage(FWHandles[i], WM_DDE_EXECUTE, FDDEHWND, ddeCommandH); PostMessage(FWHandles[i], WM_DDE_TERMINATE, FDDEHWND, 0); end; finally GlobalUnlock(ddeCommandH); GlobalFree(ddeCommandH); end; finally DeAllocateHWND(FDDEHWND); end; end; class procedure TDdeHelper.DdeWndProc(var AMsg: TMessage); var l: integer; begin // ack debug if AMsg.Msg = WM_DDE_ACK then begin l := Length(FWHandles); SetLength(FWHandles, l + 1); FWHandles[l] := AMsg.WParam; if FLog then WriteLn(Format('ACK from: %x %x', [AMsg.WParam, AMsg.lParam])); end else if FDDEHWND <> 0 then DefWindowProc(FDDEHWND, AMsg.Msg, AMsg.WParam, AMsg.lParam); end; end.
  22. Attila Kovacs

    How to open a file in the already running IDE?

    Here are two DDE log files, This is what BDS is getting by calling "DdeConnectList()"..... (why??? 128 conversations are opened. wtaf?) 1(-3) working, 4th, last terminate is missing and the returned list by DdeConnectList is empty. 1.sxl 4.sxl Ok, these are all the window handles on my system, not just bds however I was filtering for the one hwnd.... never mind. But, 00C70C4C is a TPUtilWindow (AllocateHwd), which belongs to bds.exe <000280> 00C70C4C S WM_DDE_INITIATE hwnd:003109E0 aApp:C24F ("bds") aTopic:C009 ("System") <000281> 003109E0 S WM_DDE_ACK hwnd:00C70C4C aApp:C24F ("bds") aTopic:C009 ("System") <000282> 003109E0 R WM_DDE_ACK <000283> 00C70C4C R WM_DDE_INITIATE <000400> 00C70C4C P WM_DDE_TERMINATE hwnd:003109E0 <---- this is missing from the 4th run Code Meaning P The message was posted to the queue with the PostMessage function. No information is available concerning the ultimate disposition of the message. S The message was sent with the SendMessage function. This means that the sender doesnโ€™t regain control until the receiver processes and returns the message. The receiver can, therefore, pass a return value back to the sender. s The message was sent, but security prevents access to the return value. R Each โ€˜Sโ€™ line has a corresponding โ€˜Rโ€™ (return) line that lists the message return value. Sometimes message calls are nested, which means that one message handler sends another message.
  23. Attila Kovacs

    How to open a file in the already running IDE?

    but opening files from the shell still works which is also DDE from bdsLauncher
  24. Attila Kovacs

    How to open a file in the already running IDE?

    something is still not ok, after 4 calls the IDE won't respond anymore
  25. Attila Kovacs

    How to open a file in the already running IDE?

    there is a small change since then in at the beginning of the DDE part lHszApp := DdeCreateStringHandleW(FDdeInstId, PChar(DdeService), CP_WINUNICODE); DdeKeepStringHandle(FDdeInstId, lHszApp); lHszTopic := DdeCreateStringHandleW(FDdeInstId, PChar(DdeTopic), CP_WINUNICODE); DdeKeepStringHandle(FDdeInstId, lHszTopic); try ConvList := DdeConnectList(FDdeInstId, lHszApp, lHszTopic, 0, nil); finally DdeFreeStringHandle(FDdeInstId, lHszApp); DdeFreeStringHandle(FDdeInstId, lHszTopic); end; also, I dropped vcl.ddeman and initialize DDE myself ..... function DdeMgrCallBack(CallType, Fmt: UINT; Conv: HConv; hsz1, hsz2: HSZ; Data: HDDEData; Data1, Data2: ULONG_PTR): HDDEData; stdcall; begin Result := 0; end; initialization // DdeInitialize FDdeInstId := 0; InitRes := DdeInitializeW(FDdeInstId, DdeMgrCallBack, APPCLASS_STANDARD, 0); if InitRes <> 0 then raise Exception.CreateFmt('DDE Error: %d', [DdeGetLastError(InitRes)]); finalization DdeUninitialize(FDdeInstId); and headless dde windows messages (from console), optional: FDDEHWND := AllocateHWnd(TDDeHelper.DDEWndProc); try // fist initiate the communication, this results an ACK from the host aService := GlobalAddAtom(PChar(sService)); aTopic := GlobalAddAtom(PChar(sTopic)); try SendMessage(ci.hwndPartner, WM_DDE_INITIATE, FDDEHWND, Makelong(aService, aTopic)); finally GlobalDeleteAtom(aService); GlobalDeleteAtom(aTopic); end; // send dde script ddeCommandH := GlobalLockString(ddeCommand, GMEM_DDESHARE); try PostMessage(ci.hwndPartner, WM_DDE_EXECUTE, FDDEHWND, ddeCommandH); finally GlobalUnlock(ddeCommandH); GlobalFree(ddeCommandH); end; finally DeAllocateHWND(FDDEHWND); end; whereas class procedure TDDeHelper.DDEWndProc(var AMsg: TMessage); begin // ack debug if AMsg.Msg = WM_DDE_ACK then begin if FLog then WriteLn(Format('ACK from: %x %x', [AMsg.WParam, AMsg.LParam])); end else if FDDEHWND <> 0 then DefWindowProc(FDDEHWND, AMsg.Msg, AMsg.WParam, AMsg.LParam); end; as a whole: uBdsLauncher2.pas
ร—