mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-08 18:54:09 -04:00
Use winutil for the file dialog helper
This commit is contained in:
parent
e48db37839
commit
c6ca0a95db
@ -10,7 +10,6 @@ import sys
|
|||||||
from threading import Thread
|
from threading import Thread
|
||||||
from uuid import uuid4
|
from uuid import uuid4
|
||||||
|
|
||||||
from PyQt5.Qt import QEventLoop, Qt, pyqtSignal
|
|
||||||
|
|
||||||
from polyglot.builtins import filter, string_or_bytes, unicode_type
|
from polyglot.builtins import filter, string_or_bytes, unicode_type
|
||||||
|
|
||||||
@ -90,22 +89,15 @@ class Helper(Thread):
|
|||||||
self.callback = callback
|
self.callback = callback
|
||||||
self.data = data
|
self.data = data
|
||||||
self.daemon = True
|
self.daemon = True
|
||||||
self.rc = 0
|
self.rc = 1
|
||||||
self.stdoutdata = self.stderrdata = b''
|
self.stdoutdata = self.stderrdata = b''
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
self.stdoutdata, self.stderrdata = self.process.communicate(b''.join(self.data))
|
try:
|
||||||
self.rc = self.process.wait()
|
self.stdoutdata, self.stderrdata = self.process.communicate(b''.join(self.data))
|
||||||
self.callback()
|
self.rc = self.process.wait()
|
||||||
|
finally:
|
||||||
|
self.callback()
|
||||||
class Loop(QEventLoop):
|
|
||||||
|
|
||||||
dialog_closed = pyqtSignal()
|
|
||||||
|
|
||||||
def __init__(self):
|
|
||||||
QEventLoop.__init__(self)
|
|
||||||
self.dialog_closed.connect(self.exit, type=Qt.QueuedConnection)
|
|
||||||
|
|
||||||
|
|
||||||
def process_path(x):
|
def process_path(x):
|
||||||
@ -178,8 +170,20 @@ def run_file_dialog(
|
|||||||
app_uid = app_uid or current_app_uid
|
app_uid = app_uid or current_app_uid
|
||||||
if app_uid:
|
if app_uid:
|
||||||
data.append(serialize_string('APP_UID', app_uid))
|
data.append(serialize_string('APP_UID', app_uid))
|
||||||
|
|
||||||
|
from PyQt5.Qt import QEventLoop, Qt, pyqtSignal
|
||||||
|
|
||||||
|
class Loop(QEventLoop):
|
||||||
|
|
||||||
|
dialog_closed = pyqtSignal()
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
QEventLoop.__init__(self)
|
||||||
|
self.dialog_closed.connect(self.exit, type=Qt.QueuedConnection)
|
||||||
|
|
||||||
loop = Loop()
|
loop = Loop()
|
||||||
server = PipeServer(pipename)
|
server = PipeServer(pipename)
|
||||||
|
server.start()
|
||||||
with sanitize_env_vars():
|
with sanitize_env_vars():
|
||||||
h = Helper(subprocess.Popen(
|
h = Helper(subprocess.Popen(
|
||||||
[HELPER], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE),
|
[HELPER], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE),
|
||||||
@ -293,79 +297,43 @@ def choose_save_file(window, name, title, filters=[], all_files=True, initial_pa
|
|||||||
class PipeServer(Thread):
|
class PipeServer(Thread):
|
||||||
|
|
||||||
def __init__(self, pipename):
|
def __init__(self, pipename):
|
||||||
Thread.__init__(self, name='PipeServer')
|
Thread.__init__(self, name='PipeServer', daemon=True)
|
||||||
self.daemon = True
|
from calibre_extensions import winutil
|
||||||
import win32pipe, win32api, win32con
|
self.client_connected = False
|
||||||
FILE_FLAG_FIRST_PIPE_INSTANCE = 0x00080000
|
self.pipe_handle = winutil.create_named_pipe(
|
||||||
PIPE_REJECT_REMOTE_CLIENTS = 0x00000008
|
pipename, winutil.PIPE_ACCESS_INBOUND | winutil.FILE_FLAG_FIRST_PIPE_INSTANCE,
|
||||||
self.pipe_handle = win32pipe.CreateNamedPipe(
|
winutil.PIPE_TYPE_BYTE | winutil.PIPE_READMODE_BYTE | winutil.PIPE_WAIT | winutil.PIPE_REJECT_REMOTE_CLIENTS,
|
||||||
pipename, win32pipe.PIPE_ACCESS_INBOUND | FILE_FLAG_FIRST_PIPE_INSTANCE,
|
1, 8192, 8192, 0)
|
||||||
win32pipe.PIPE_TYPE_BYTE | win32pipe.PIPE_READMODE_BYTE | win32pipe.PIPE_WAIT | PIPE_REJECT_REMOTE_CLIENTS,
|
winutil.set_handle_information(self.pipe_handle, winutil.HANDLE_FLAG_INHERIT, 0)
|
||||||
1, 8192, 8192, 0, None)
|
|
||||||
win32api.SetHandleInformation(self.pipe_handle, win32con.HANDLE_FLAG_INHERIT, 0)
|
|
||||||
self.err_msg = None
|
self.err_msg = None
|
||||||
self.data = b''
|
self.data = b''
|
||||||
self.start()
|
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
import win32pipe, win32file, winerror, win32api
|
from calibre_extensions import winutil
|
||||||
|
|
||||||
def as_unicode(err):
|
|
||||||
try:
|
|
||||||
self.err_msg = unicode_type(err)
|
|
||||||
except Exception:
|
|
||||||
self.err_msg = repr(err)
|
|
||||||
try:
|
try:
|
||||||
try:
|
try:
|
||||||
rc = win32pipe.ConnectNamedPipe(self.pipe_handle)
|
winutil.connect_named_pipe(self.pipe_handle)
|
||||||
except Exception as err:
|
except Exception as err:
|
||||||
as_unicode(err)
|
self.err_msg = f'ConnectNamedPipe failed: {err}'
|
||||||
return
|
|
||||||
|
|
||||||
if rc != 0:
|
|
||||||
self.err_msg = 'Failed to connect to client over named pipe: 0x%x' % rc
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
self.client_connected = True
|
||||||
while True:
|
while True:
|
||||||
try:
|
try:
|
||||||
hr, data = win32file.ReadFile(self.pipe_handle, 1024 * 50, None)
|
data = winutil.read_file(self.pipe_handle, 64 * 1024)
|
||||||
except Exception as err:
|
except OSError as err:
|
||||||
if getattr(err, 'winerror', None) == winerror.ERROR_BROKEN_PIPE:
|
if err.winerror == winutil.ERROR_BROKEN_PIPE:
|
||||||
break # pipe was closed at the other end
|
break # pipe was closed at the other end
|
||||||
as_unicode(err)
|
self.err_msg = f'ReadFile on pipe failed: {err}'
|
||||||
break
|
|
||||||
if hr not in (winerror.ERROR_MORE_DATA, 0):
|
|
||||||
self.err_msg = 'ReadFile on pipe failed with hr=%d' % hr
|
|
||||||
break
|
|
||||||
if not data:
|
if not data:
|
||||||
break
|
break
|
||||||
self.data += data
|
self.data += data
|
||||||
finally:
|
finally:
|
||||||
win32api.CloseHandle(self.pipe_handle)
|
|
||||||
self.pipe_handle = None
|
self.pipe_handle = None
|
||||||
|
|
||||||
|
|
||||||
def test(helper=HELPER):
|
|
||||||
pipename = '\\\\.\\pipe\\%s' % uuid4()
|
|
||||||
echo = '\U0001f431 Hello world!'
|
|
||||||
secret = os.urandom(32).replace(b'\0', b' ')
|
|
||||||
data = serialize_string('PIPENAME', pipename) + serialize_string('ECHO', echo) + serialize_secret(secret)
|
|
||||||
server = PipeServer(pipename)
|
|
||||||
p = subprocess.Popen([helper], stdout=subprocess.PIPE, stdin=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
||||||
stdout, stderr = p.communicate(data)
|
|
||||||
if p.wait() != 0:
|
|
||||||
raise Exception('File dialog failed: ' + stdout.decode('utf-8') + ' ' + stderr.decode('utf-8'))
|
|
||||||
if server.err_msg is not None:
|
|
||||||
raise RuntimeError(server.err_msg)
|
|
||||||
server.join(2)
|
|
||||||
parts = list(filter(None, server.data.split(b'\0')))
|
|
||||||
if parts[0] != secret:
|
|
||||||
raise RuntimeError('Did not get back secret: %r != %r' % (secret, parts[0]))
|
|
||||||
q = parts[1].decode('utf-8')
|
|
||||||
if q != echo:
|
|
||||||
raise RuntimeError('Unexpected response: %r' % server.data)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
choose_save_file(None, 'xxx', 'yyy')
|
from calibre.gui2 import Application
|
||||||
test(sys.argv[-1])
|
app = Application([])
|
||||||
|
print(choose_save_file(None, 'xxx', 'yyy'))
|
||||||
|
del app
|
||||||
|
Loading…
x
Reference in New Issue
Block a user