Closed nod5 closed 4 years ago
When you're running it from command-line, then none of the Sumatra windows is active.
This is a corner case that doesn't have an obviously correct behavior. The same document is opened in 2 places. We have to pick one. Sumatra does a simple thing and picks the first place it finds.
@nod5 I have raised a query / issue in the past https://github.com/sumatrapdfreader/sumatrapdf/issues/1260# that the only way since tabs were introduced that it worked was with usetabs = true then -reuse-instance behaves well
but have not recently tested what happens with the recent NEW window feature, it has always been stated that having multiple windows can cause odd results
I agree it is a corner case. That said, my background use case is controlling the active SumatraPDF window/tab via external scripts. Such scripts can run the command line action in the background while one SumatraPDF window is active.
I have explored different options for controlling the active SumatraPDF window in this use case.
1. command line -reuse-instance -page <pageNo>
method
Silent and reliable, but has this particular corner case issue.
2. Send keys to the window Works, but can be unreliable and not silent because it has to wait for the goto page popup to appear if SumatraPDF is fullscreen or else send keys to the toolbar page control.
3. Send DDE commands I haven't gotten that to work. But AFAICT it would have the same corner case issue as the command line method.
4. Send windows messages
I haven't found a way to go to a specific page with this method.
Sending windows messages does work reliably and silently for several of the other items in https://github.com/sumatrapdfreader/sumatrapdf/blob/master/src/resource.h
For example in AutoHotkey scripts we can make the active SumatraPDF window do "Save Annotations" like this SendMessage, 0x111, 439,0,, A
Could a new windows message be added to go to a page, with page number specified as a message parameter? Then we can target that message at the active SumatraPDF window, or some other window, as we please.
Old similar requests #359 #964
Thanks, that's useful background.
Could you describe in more detail how you do DDE and how they fail?
Sumatra sends DDE commands to itself and it works. Here's how we do it for reference: https://github.com/sumatrapdfreader/sumatrapdf/blob/master/src/SumatraStartup.cpp#L202
Maybe the issue is that you send ascii vs. unicode strings but this is just guessing.
Currently DDE would pick the first matching window as well but here I can add code to prioritize the window to which the DDE command was sent.
I can also add more DDE commands if they would be useful.
@kjk @nod5
of the cuff testing so perhaps a red herring but when focus is the secondary NEW window then that is actioned rather than the first [edit] sorry if the first has the same file it is the first that is actioned and I should have remembered that "favorites" actioned in second instance will open file in first instance
@nod5 I am trying to understand why two copies of same file need to be accessible
I know many a user wants two views of the same file especially if they need to reference back and forth and your bookmark helper app would potentially need control of each window but favorites and .vbkm updates are in progress that may eventually address much of those requirements.
If it is for adding and subtracting annotation one potential problem is the shared resource adding an object in one windowed part of file can negate subtraction in another window, one reason you use instant autosave ? but when editing and previewing it is best if only one window does the writing.
@kjk it would help to know if .bkm will be user configurable for adding personal/temporary bookmarks currently .vbkm is perhaps too heavy for on the fly usage.
@nod5 Not sure if it may help but an observed "feature" of the current builds is that favorites in one window (call it 2on the left) can drive the other window (call it 1 on the right)
@nod5 @kjk I have found a way that seems to reasonably prioritise the second window First taskkill any running copy then start SumatraPDF as a portable ( Nod5 so can be done from AHK location) then call the same file via normal start and calls to installed copy, naturally all subsequent calls from the portable version should be prioritised in the installed version Proof of concept
@kjk
Could you describe in more detail how you do DDE and how they fail?
Sorry for the late reply. I've only tried reusing this AutoHotkey DDE code https://github.com/Ixiko/AHK-libs-and-classes-collection/blob/master/lib-a_to_h/DDEMessage.ahk with this kind of tests, which had no effect.
sServer := "SUMATRA"
sTopic := "control"
sItem := "[GotoPage(""C:\a.pdf"", 14)]"
(note: double double-quotes are for escaping in AutoHotkey expressions)
The issue is probably me being too inexperienced with DDE. I'll have to read and troubleshoot more before I know what the problem is.
I did look into using SendMessage to interact with SumatraPDF instead though.
Cloned the SumatraPDF repo and poked around. Uphill effort since I don't know C++ 🙂, but we learn by trying. I've muddled through to code that at least compiles and work in my tests.
Here's a gist that shows my edits to make the active SumatraPDF window accept a IDM_GOTO_PAGE_NUM message with target page number as lParam. https://gist.github.com/nod5/002853fcac619789798afe1c1eb59ba5/revisions
After compiling with those edits my external script can change page in the active SumatraPDF document with a oneliner
SendMessage, 0x111, 521, 14,, A
So from AutoHotkey at least much leaner to use compared to DDE.
What are you thoughts on making SumatraPDF more controllable via SendMessage commands? Is it likely that you'll include such code, if properly reworked? (I assume my work in progress code needs more checks along the way and probably has some mistakes.) Or are there some negatives with that kind of approach?
I've also pieced together edits to do "copy active file path" and "return cursor x y position in canvas in pt unit" via SendMessage. These are all to ease script interaction with highlights and .smx files. The SendMessage approach so far seem faster and more reliable compared to my old method of reading text from controls and boxes in the SumatraPDF window.
@GitHubRulesOK
@nod5 I am trying to understand why two copies of same file need to be accessible ... I know many a user wants two views of the same file especially if they need to reference back and forth
Yeah that's the use case. For example viewing two different chapters in some pdf reference book at the same time via two SumatraPDF windows, and then wanting to use external scripts to jump to a page or the next highlight in one of the two windows.
@nod5 there a several ways to drive SumatraPDf via DDE from Editors thus the editor window is the controler and invokes SumatraPDf via DDE or CLI and expects -reverse-search controls via DDE or PIPES or CLI this is the way many editors interface using SumatraPDF as a "previewer" to jump from one PDF page line to another.
In the past Notepad++ used CMCDDE to achive that effect however I think like many other editors it has now migrated to simpler plain CLI calling. The notable exceptions are LYX which uses a very problematic PIPE methodology and TexnicStudio which insists it needs DDE in the -inverse call for its single instance working
For a brief guide / description of CMCDDE and two other DDEviaCLI helpers see https://www.robvanderwoude.com/ddecommandline.php For adobe the japanese favour an OPEN/CLOSE PDF utility but thats because Acrobat has a DDEClose which SumatraPDF does not have, or need upto the file locking limit (Now 32Mb)
@nod5 It's better to use WM_COPYDATA and pack the data as DDE instead of using DDEExecute(). That allows you to target specific window. See https://github.com/sumatrapdfreader/sumatrapdf/blob/master/src/SumatraStartup.cpp#L228
I'm sure it's possible to replicate that in Auto-Hotkey but I'm not using it so don't have a snippet ready,
I skimmed Auto Hotkey docs. It's unclear if the way you escape "
is correct. It's also unclear if the string is sent Unicode (as sumatra expects) or ascii. They have functions for explicitly converting to unicode.
@kjk Am I understanding this correctly:
On those assumptions I tried repurposing the AutoHotkey send_WM_COPYDATA function here https://www.autohotkey.com/docs/commands/OnMessage.htm#SendString
But with no effect.
One difference is the first member in this SumatraPDF source code line
COPYDATASTRUCT cds = {0x44646557 /* DdeW */, (DWORD)(cmd.size() + 1) * sizeof(WCHAR), cmd.Get()};
What is 0x44646557 /* DdeW */
for? The DDE command text is in the third member (lpData), so what does the first member (dwData) do here?
Reference: https://docs.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-copydatastruct
Also, to make sure I'm not missing some step on the SumatraPDF side:
To get SumatraPDF to react to the DDE command [GotoPage("c:\file.pdf", 37)]
all we need to do prior to that is open the file C:\file.pdf
in SumatraPDF. No extra advanced setting or other such steps?
@nod5
with this kind of tests, which had no effect.
sServer := "SUMATRA" sTopic := "control" sItem := "[GotoPage(""C:\a.pdf"", 14)]"
(note: double double-quotes are for escaping in AutoHotkey expressions)
SumatraPDF & DDE can be exceptionally pedantic so in CMCDDE this works note for DOS \ IS THE ESCAPE CHARACTER
start "" %LOCALAPPDATA%\SumatraPDF\SumatraPDF.exe
REM use ping to create a wait state
PING localhost -n 3 >NUL
cmcdde sumatra control "[Open(\"c:/test/a.pdf\", 0, 1, 1)]"
cmcdde sumatra control "[GotoPage(\"c:/test/a.pdf\", 14)]"
cmcdde sumatra control "[SetView(\"c:/test/a.pdf\", \"continuous\", -2, 0, 400)]"
I find it much simpler to use
start "" %LOCALAPPDATA%\SumatraPDF\SumatraPDF.exe "c:\test\a.pdf" -page 14 -view "continuous single page" -zoom "fit width" -scroll 0,400
REM optionally open second window at page 14 (yes in dos this is a very slow method)
echo >script.vbs set shell = CreateObject("WScript.Shell"):shell.SendKeys "^+N"
echo >>script.vbs shell.SendKeys "%%Gg14{ENTER}" & script.vbs
@nod5 WM_COPYDATA
sends arbitrary data to Sumatra process. DdeW
is an arbitrary prefix we chose to indicate that this message should be interpreted the same way as DDE string. So you need to send e.g. 0x44646557
bytes + unicode string [GotoPage("C:\a.pdf", 14)]
via WM_COPYDATA
.
You have to make sure that this is unicode string and that it's properly formatted (""
seems like a strange way to escape "
but maybe that's how Auto Hotkey works).
@kjk Thank you, I got it working now.
In case anyone else finds this by search, here is my working code in a gist on how to control a SumatraPDF window from AutoHotkey through DDE command text packed in SendMessage WM_COPYDATA
As mentioned earlier I'm also requesting ways to more neatly extract from SumatraPDF
Now, WM_COPYDATA only returns true/false, so cannot pass such data in a SendMessage return. Alternatives: A DDE message to make SumatraPDF clipboard the active filepath. Or a more complex DDE conversation where SumatraPDF sends the filepath back in a separate WM_COPYDATA message. Ditto for cursor position.
Another alternative, as mentioned above, is to add in SumatraPDF non-DDE simple messages for these two requests. I'll shortly put up my cobbled together code for that, for illustration at least. Here too can SumatraPDF clipboard the filepath. X Y canvas pt position even fits in the SendMessage LRESULT return LONG value if we omit decimals and the position whole numbers are at most 4 digits long each, for example return 100000000 + (xposition * 10000) + yposition;
But perhaps I should split these requests into new issues?
Let's consider this issue solved and open a new one for new functionality (you can reference this issue there).
I don't know what is a good way to send the data back.
WM_COPYDATA (and sending messages in general) is only one way because processes (i.e. Sumatra process and Auto HotKey process) can't see each other's memory. That's why WM_COPYDATA exists - the OS does the memory copying between processes.
I'm not sure Sumatra can send a message back to Auto HotKey process because it requires a window and Sumatra knowing HWND of that window.
A very hacky option would be to use channel that both processes have access to, like clipboard or registry. E.g. Sumatra could write result to clipboard in an agreed-upon format. Wouldn't be 100% reliable.
A reliable, but complicated, way would be to an IPC channel like shared memory or socket connection. Sumatra would open shared memory under a known path and that would allow responding to request.
But let's have this discussion in a new issue.
If we open
a.pdf
in multiple SumatraPDF windows then the command line actionSumatraPDF.exe C:\test\a.pdf -reuse-instance -page 5
changes to page 5 in the SumatraPDF window that was first opened, even if one of the other windows was active.I request that SumatraPDF in this case instead change page in the active SumatraPDF
a.pdf
window (if one is active).The requested behaviour is how it already works if we open multiple
a.pdf
instances in tabs in one and the same SumatraPDF window and then run the above command line .Tested in daily
SumatraPDF-prerelease-11974-64.zip