GPX files can't be opened from Explorer/command line once QMapShack is already open

Issue #464 new
Kevin Frederick created an issue

In version 1.11.0, I could double-click in windows explorer, or type "qmpashack.exe file.gpx", and the file would appear in QMapShack, even if QMapShack was already running.

When I try to open a file by the same method in v1.13.0, nothing happens.

If QmapShack is not already running, then file(s) specified on the command line/shell command are correctly opened.

I am on Windows 7.

This might seem minor but is a major disruption to my work-flow. I tried to re-install v1.11 but it doesn't seem to be available anywhere.

Comments (31)

  1. kiozen

    This has to be solved by someone using Windows. It works on Linux without a problem.

    There has been some discussion already on a German forum

    https://www.naviboard.de/showthread.php?p=509967

    But so far support by the Windows community is low as usual and everyone is waiting for someone else to fix stuff. So do not expect this to be fixed anytime soon.

  2. wthaemelt

    I'm more or less convinced that this issue is a Windows one. Looking into CSingleInstanceProxy.cpp a second call to open QMS should give immediately a debug message in the logfile - this message isn't there. Thus, I suspect the call doesn't reach QMS.

    I think, the main issue is having correct file associations/registry settings in Windows. And with newer versions of Windows it seems to get rather difficult to establish correct and complete settings. Microsoft descriptions (e.g. https://docs.microsoft.com/de-de/windows/desktop/shell/fa-file-types) make clear, that one should be very careful when working on this topic. And more: with Windows 10 updates it happened to me again and again that some file associations for multimedia files are reset without informing me to some Windows default values.

    I'm not sure, if debugging on the QMS side could lead to any results.

  3. wthaemelt

    @kiozen: I played a bit with file associations and with CSingleInstanceProxy.cpp. I added an additional debug statement as follows:

           QDataStream stream(&socket);
            qDebug() << filenames;
            stream << filenames;
            socket.waitForBytesWritten(10000); 
    

    The debug log gives me now:

    2019-04-29 10:20:46.800 [debug] ("D:\\Scripts\\Project5.gpx")
    2019-04-29 10:20:46.801 [debug] Sent parameters to primary instance. Result false
    2019-04-29 10:20:46.801 [debug] There can only be one. Exit.
    

    My understanding of C++ is very limited. Could you help me to understand the stream << filenames line? filenames is a list of filenames. Does the stream object digest such a list (that means stream should send each filename in the list to the socket or even open each file in the list and send the content to the socket)? In the QDataStream documentation I can only see that the << operator is used for bytes or strings.

  4. kiozen

    The << operator is equal to append (). Filenames is a list of strings with the paths to files to be opened. It's sent via the socket to the primary instance. Qt is taking care of how it is sent over the socket.

    On the primary instance the list is received and each file is read from the disk by the primary instance.

  5. wthaemelt

    Thanks.

    One more observation:

    • I increased the waiting times from 3000 to 10000 - no visible change in behaviour.
    • I open the first QMS instance by double-clicking a GPX file - no problems.
    • I try to open a second GPX file by double-clicking it
      • The second QMS instance starts
      • The second instance gets the correct filename
      • In the first instance the slotNewConnection() method is always called
      • Quite often (always(?)) the first instance gets the correct filename and handles it correctly.
      • Next, I double-click again some GPX file
      • The new instance works as expected
      • In the still running first instance the slotNewConnection() method is called
      • The statement stream >> filenames; returns often (always/sometimes (?)) an empty list and the other instance gets the Result: false.

    Reading the debug file is a bit complicated: 2 instances use the same debug file and the messages in the file are not sorted by time.

    Could I add some parameter to qDebug so that a second debug file is written from one of the instances? Is flushing possible?

    Any recommendations for further debugging? How to get details about socket state in the case of an empty filename list?

    Btw: I noticed yet another issue, this time with drag-and-drop: Drag a GPX file to the workspace window doesn't work always as expected. From time to time I can't drop the file to the workspace, instead I have to drop it to the database window. Dropping the file there, opens it in the workspace window. Can there be some relation to the original issue?

  6. kiozen

    I am not close to a computer right now. So I can't look up stuff in the code. But it sounds like Windows fucks up the socket after the first connection. Maybe reading out error codes would give a hint. Qt docs should tell you how to do.

    Why is it that Microsoft keeps on breaking the most simple stuff in computer science?

  7. kiozen

    Drag-n-drop is unrelated. I have to get on Linux if this is another feature of Windows or a real issue.

  8. wthaemelt

    Just playing with my old-fashioned debug style I added a few debug output statements to slotNewConnection():

    void CSingleInstanceProxy::slotNewConnection()
    {   qDebug() << "New connection entry";
        QLocalSocket * socket = server->nextPendingConnection();
        if(socket == nullptr)
        {
            return;
        }
    
        // Each secondary instance will send a QStringList with files to open
        // The list can be empty.
        qDebug() << "New connection";
        qDebug() << socket->isValid();
    
        qDebug() << socket->state();
        if(socket->waitForReadyRead(10000))
        {
            QStringList filenames;
            QDataStream stream(socket);
            stream >> filenames;
    
            qDebug() << "Before status";
            qDebug() << filenames;
            qDebug() << stream.status();
            qDebug() << "After status";
            CMainWindow& w = CMainWindow::self();
            w.loadGISData(filenames);
    
            // confirm that files are loaded
            qDebug() << "Follows true";
            stream << true;
            socket->waitForBytesWritten(3000);
    
            // raise the application window to top of desktop
            w.raise();
            QApplication::setActiveWindow(&w);
        }
    

    Surprising result: I can open one GPX file after the other by double-clicking on it. I can even select several GPX files in the explorer and open them at once in QMS. No problems found.

    Question: I noticed that it can take a little while (seconds!) to close a QMS instance completely. What happens, if the second instance is still running and the next one gets already started?

  9. kiozen

    Maybe the socket is not closed when the second instance terminates leaving it in a bad state. As far as I know there are API calls to wait for a socket. qt docs give examples how to use. But my expectations are that a file or socket is closed properly once an instance terminates.

  10. wthaemelt

    I compiled kiozen/qmapshackprivate with the modified CSingleInstanceProxy.cpp.

    Results:

    • Can open the first GPX file, but only that one. Double-clicking again GPX files gives the Result: false message.

      I changed the time-outs from 3000 to 10000: no changes.

    • Now, the difference between the default (and in principle the kiozenprivate) version and my version (see above) is in principle only in a few additional debug lines. And my version is still doing what it is expected to do. Nice effect of a few meaningless code lines.

  11. Kevin Frederick reporter

    Thanks for the effort so far in chasing this bug. Is there anything that has changed in this code between v1.11 and v1.13? As noted in the bug report, this problem did not exist in v1.11.

  12. wthaemelt

    To Kevin: I my understanding the discussed issue may result from 2 different sources:

    • Wrong (or modified as mentioned above) file association settings in Windows. I had to play with these settings for quite a while before they worked as expected. And I even don’t know what the decisive step was. The QMS debug log showed me that I had finally correct settings - QMS was called.
    • Failing communication between 2 QMS instances (mainly discussed in this issue)
  13. kiozen

    There hasn’t been a code change in CSingleInstanceProxy for sure since V1.11. Not sure if @Helmut Schmidt changed the Qt version.

    @wthaemelt I committed another patch. This time waiting for the incoming socket to be active for sure. Please try.

  14. Helmut Schmidt

    Qt versions used for the recent Windows Installers:

    QMS 1.11.0 → Qt 5.5.1

    QMS 1.11.1 → Qt 5.5.1

    QMS 1.12.0 → Qt 5.11.1

    QMS 1.12.1 → Qt 5.11.1

    QMS 1.12.2 → Qt 5.12.1

    QMS 1.12.3 → Qt 5.12.1

    QMS 1.13.0 → Qt 5.12.1

  15. wthaemelt

    Quoted from https://stackoverflow.com/questions/44544684/is-it-ok-to-use-waitforreadyread-instead-of-creating-a-slot-for-readyread:

    To your question: yes you can use QSslSocket::waitForReadyRead() but on Widows it can timeout even when the data came to the socket. So if timeout occurs you have to check if it is timeout or the method failed. The check is simple just if QAbstractSocket::bytesAvailable() > 0 then data are ready for read otherwise it's timeout.

    This approach is ok when you use small timeout and your application isn't sensitive on delay (e.g. communication between temperature sensor and cloud with temperature history). But if any unnecessary delay is not acceptable for you then you should use the signal/slot interface.

    For more information you can look at the bug report on the Qt's bug tracker.

  16. wthaemelt

    I added once more a debug statement:

    ```c++
        stream >> filenames;
    
        qDebug() << "Filenames: " << filenames;
    
    ```
    

    Output in debug log:

    2019-05-01 8:35:06.752 \[debug\] Filenames:  \(\)  
    2019-05-01 8:35:09.970 \[debug\] Filenames:  \(\)  
    2019-05-01 8:35:11.767 \[debug\] Filenames:  \(\)
    

    i.e. in my understanding the receiver tries reading too early or the filename was somewhere lost between sender and receiver .

    (Backslashes are not in the debug log but added by bitbucket!)

  17. wthaemelt

    One more modification/debug message. This time on the sender side:

        stream << filenames;
        socket.waitForBytesWritten(3000);
    
        qDebug() << "Sender socket error: " << socket.error();
    

    Result:

    2019-05-01 11:04:07.954 [debug] Sender socket error: QLocalSocket::UnknownSocketError

    And with additional debug in the same method

    socket.connectToServer(serverName);
    qDebug() << "Sender socket connect error: " << socket.error();
    

    I got

    2019-05-01 11:12:34.287 \[debug\] Sender socket connect error: QLocalSocket::UnknownSocketError  
    2019-05-01 11:12:34.287 \[debug\] Sender socket error:  QLocalSocket::UnknownSocketError
    

    One more observation: Looking in the ProcessExplorer I can see the files used/opened by QMS. There are many entries ( I noticed 8!) of the form \Device\NamedPipe\QMapShack-k-wth. Is this understandable?

  18. wthaemelt

    At the very end: add the following 3 harmless debug statements to kiozenprivate and get a version that opens GPX files by double-clicking. But: If QMS is started without a GPX file and GPX thereafter are double-clicked, then it fails again to open the files.

    ```
    // Each secondary instance will send a QStringList with files to open
    // The list can be empty.
    
        qDebug() << "New connection";
        qDebug() << socket->isValid();
        qDebug() << socket->state();   
    ```
    
  19. kiozen

    Reading a socket error if the socket is not in error state does not provide any useful information. This can be anything.

  20. wthaemelt

    2019-05-02 20:15:00.933 [debug] CSingleInstanceProxy: Other instance is connected. Read list of filesnames.
    2019-05-02 20:15:00.933 [debug] CSingleInstanceProxy: Received list of filenames: ("D:\Scripts\RedSquare.gpx")
    2019-05-02 20:15:00.949 [debug] CSingleInstanceProxy: Confirm list of filenames.
    2019-05-02 20:15:05.212 [debug] CSingleInstanceProxy: Other instance is connected. Read list of filesnames.
    2019-05-02 20:15:05.212 [debug] CSingleInstanceProxy: Received list of filenames: ()
    2019-05-02 20:15:05.212 [debug] CSingleInstanceProxy: Confirm list of filenames.
    2019-05-02 20:15:05.212 [debug] CSingleInstanceProxy: Failed to write confirmation QLocalSocket::UnknownSocketError "Unbekannter Fehler"
    2019-05-02 20:15:09.621 [debug] CSingleInstanceProxy: Other instance is connected. Read list of filesnames.
    2019-05-02 20:15:09.621 [debug] CSingleInstanceProxy: Received list of filenames: ()
    2019-05-02 20:15:09.621 [debug] CSingleInstanceProxy: Confirm list of filenames.
    2019-05-02 20:15:09.621 [debug] CSingleInstanceProxy: Failed to write confirmation QLocalSocket::UnknownSocketError "Unbekannter Fehler"
    2019-05-02 20:15:12.759 [debug] CSingleInstanceProxy: Other instance is connected. Read list of filesnames.
    2019-05-02 20:15:12.759 [debug] CSingleInstanceProxy: Received list of filenames: ()
    2019-05-02 20:15:12.759 [debug] CSingleInstanceProxy: Confirm list of filenames.
    2019-05-02 20:15:12.759 [debug] CSingleInstanceProxy: Failed to write confirmation QLocalSocket::UnknownSocketError "Unbekannter Fehler"

    and

    2019-05-02 20:15:09.621 [debug] CSingleInstanceProxy: Connect to other instance...
    2019-05-02 20:15:09.621 [debug] CSingleInstanceProxy: Failed to read confirmation QLocalSocket::UnknownSocketError "Unbekannter Fehler"

    Description:

    First clicked GPX: QMS starts with this file opened in workspace

    Second clicked GPX: file appears in workspace

    Third, … GPX: failure

  21. kiozen

    Looks like the second instance trashes the socket. Hard to tell if this is a Windows “feature” or a bug in the Qt lib. As it’s not broken in older releases I would bet on a bug in Qt. Is it possible to test a more recent or older version?

  22. wthaemelt

    One more result:

    • click the next GPX file only a short time after the previous one: loading fails as described
    • wait quite a while (minute or so) and then click the next GPX file: file is loaded. Wait again for about a minute (maybe, also some shorter interval): next file loaded without a problem. This is true even if inbetween loading of a GPX file failed!

    Here is part of the logfile:

    2019-05-17 16:52:39.511 [debug] CSingleInstanceProxy: Received list of filenames: ("D:\Scripts\TestActivity.gpx")
    2019-05-17 16:55:48.893 [debug] CSingleInstanceProxy: Received list of filenames: ("D:\Scripts\TestCutLoops.gpx")
    2019-05-17 16:56:07.339 [debug] CSingleInstanceProxy: Received list of filenames: ()
    2019-05-17 16:57:13.715 [debug] CSingleInstanceProxy: Received list of filenames: ("D:\Scripts\TestData.gpx")
    2019-05-17 16:57:53.261 [debug] CSingleInstanceProxy: Received list of filenames: ("D:\Scripts\TestAnimation.gpx")
    2019-05-17 16:58:02.214 [debug] CSingleInstanceProxy: Received list of filenames: ("D:\Scripts\Teilnehmeradressen.gpx")
    2019-05-17 16:58:04.697 [debug] CSingleInstanceProxy: Received list of filenames: ()
    2019-05-17 16:58:25.573 [debug] CSingleInstanceProxy: Received list of filenames: ("D:\Scripts\t2.gpx")

    How to implement within CSingleInstanceProxy as early as possible a waiting time before touching a socket?

  23. kiozen

    The second instance tries to connect to the socket in

    CSingleInstanceProxy::CSingleInstanceProxy(const QStringList filenames)
    {
        serverName = CMainWindow::self().getUser();
        if(serverName != "QMapShack")
        {
            serverName = "QMapShack-" + serverName;
        }
    
        QLocalSocket socket;
        socket.connectToServer(serverName);
        if(socket.waitForConnected(1000))
        {
    

    connectToServer() is the call. You can add a

    QThread::sleep(1)
    

    to wait a second.

    But I doubt that this is a feasible workaround. It sounds more like the second instance has problems closing the socket and the socket is blocked until a timeout unblocks it.

  24. wthaemelt

    Would certainly a bad workaround. Blocking might be involved here. But the point I don't understand yet is that the first instance gets only an empty list of filenames. The second instance has sent a complete list for sure(?). Thus, this data should be somewhere in the socket buffer. Can it happen, that the first instance doesn't read enough data from the buffer and that the above mentioned Windows issue with the waitForReadyRead method results from time to time in an incomplete read of the buffer (and then the buffer is flushed automatically after a while by the OS)? Would it be possible to use/implement the QIODevice::bytesRead signal as recommended in the Qt docu in this case (see QAbstractSocket::waitForReadyRead docu)?

    Other idea: If the first instance was called by the second one, then either

    • the second one was started without a file to open or
    • the list of filenames received by the first instance should be not-empty otherwise.

    The second instance can recognize the first case (no filenames) and could close itself without any further use of the socket for data transfer.

    In the second case, the first instance may get an empty filenames list and can recognize this situation. Assuming that the filenames list has been sent correctly and completely would imply that the first instance should do more to read the missing data from the socket. Is this reasoning correct and could it be implementd?

  25. kiozen

    As far as I can see from the logs it’s a blocked/not ready socket. This assumption is backed up by your last test waiting some time.

  26. Log in to comment