pythonqtpysidepyside6qfiledialog

How to select app-execution reparse points in QFileDialog


When I select a reparse point from %LocalAppData%\Microsoft\WindowsApps (e.g. julia.exe) using a QFileDialog and click the Open button an error dialog pops up

Error dialog

Clicking Ok just closes the error dialog but doesn't close the file browser window.

Here's a minimal example that demonstrates the problem. Click the Browse button and select any .exe file from %LocalAppData%\Microsoft\WindowsApps and the error dialog pops up.

from PySide6.QtWidgets import QApplication, QDialog, QPushButton, QFileDialog, QLineEdit, QVBoxLayout

class Main(QDialog):
    def __init__(self):
        super().__init__()
        self.button = QPushButton("Browse")
        self.le = QLineEdit()
        self.lo = QVBoxLayout()
        self.lo.addWidget(self.button)
        self.lo.addWidget(self.le)
        self.setLayout(self.lo)
        self.button.clicked.connect(self.open_browser)

    def open_browser(self):
        self.le.clear()
        answer = QFileDialog.getOpenFileName(self, "Open")
        if answer[0] != "":
            self.le.setText(answer[0])
        return

if __name__ == '__main__':
    app = QApplication()
    window = Main()
    window.show()
    app.exec()

I'm aware that %LocalAppData%\Microsoft\WindowsApps is in PATH and I do not actually need to select the reparse point file anyway but this is an error dialog that some users of my app may encounter and I'd like to deal with it somehow.

The question is, how can I select the reparse point file and then return it from the QFileDialog? Is there a way to catch the error that causes the error dialog to pop up and then simply return the full path as the answer in the above code snippet?

Win: 10
Python: 3.10.8
PySide6: 6.5.2

Things tried & considered:


Solution

  • You can't do that in the current implementation, as you can see in the source. The FOS_NOVALIDATE flag is required to remedy a limitation for reparse points. (The Win32 and COM file dialog API are pretty old and do not support reparse points).

    So, your best bet is using another GUI library. One such way is calling the Win32 API directly using the pywin32 like the following.

    import os
    import win32gui, win32con
    
    dir = os.path.expandvars('%LocalAppData%\\Microsoft\\WindowsApps')
    print(dir)
    r = win32gui.GetOpenFileNameW(
        InitialDir = dir,
        Flags = win32con.OFN_EXPLORER | win32con.OFN_NOCHANGEDIR |
            win32con.OFN_PATHMUSTEXIST | win32con.OFN_FILEMUSTEXIST |
            win32con.OFN_NOVALIDATE,
        Title = 'Open',
    )
    print(r)