Skip to content

Commit

Permalink
Windows: Make console work properly
Browse files Browse the repository at this point in the history
This patch adds console functionality for the Windows build. As the Windows .exe
is a Windows subsystem executable it doesn't have a console. We solve this by
checking if there is a console of a parent process that we can attach to.
Additionally the --forceconsole parameter can be passed to force allocating a
new console if no parent console is found.
  • Loading branch information
EchterAgo committed Apr 18, 2019
1 parent 95c69e5 commit 6166c36
Show file tree
Hide file tree
Showing 5 changed files with 114 additions and 3 deletions.
3 changes: 0 additions & 3 deletions contrib/build-wine/deterministic.spec
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,6 @@ pyz = PYZ(a.pure)
#####
# "standalone" exe with all dependencies packed into it

#options = [ ('v', None, 'OPTION')] - put this in the following exe list to debug and turn console=true

exe_standalone = EXE(
pyz,
a.scripts,
Expand All @@ -125,7 +123,6 @@ exe_standalone = EXE(
upx=False,
icon=home+'icons/electron.ico',
console=False)
# console=True makes an annoying black box pop up, but it does make Electrum output command line commands, with this turned off no output will be given but commands can still be used

exe_portable = EXE(
pyz,
Expand Down
10 changes: 10 additions & 0 deletions contrib/deterministic-build/requirements-binaries.txt
Original file line number Diff line number Diff line change
Expand Up @@ -75,3 +75,13 @@ websocket_client==0.56.0 \
wheel==0.33.1 \
--hash=sha256:66a8fd76f28977bb664b098372daef2b27f60dc4d1688cfab7b37a09448f0e9d \
--hash=sha256:8eb4a788b3aec8abf5ff68d4165441bc57420c9f64ca5f471f58c3969fe08668
psutil==5.6.1 \
--hash=sha256:23e9cd90db94fbced5151eaaf9033ae9667c033dffe9e709da761c20138d25b6 \
--hash=sha256:27858d688a58cbfdd4434e1c40f6c79eb5014b709e725c180488ccdf2f721729 \
--hash=sha256:354601a1d1a1322ae5920ba397c58d06c29728a15113598d1a8158647aaa5385 \
--hash=sha256:9c3a768486194b4592c7ae9374faa55b37b9877fd9746fb4028cb0ac38fd4c60 \
--hash=sha256:c1fd45931889dc1812ba61a517630d126f6185f688eac1693171c6524901b7de \
--hash=sha256:d463a142298112426ebd57351b45c39adb41341b91f033aa903fa4c6f76abecc \
--hash=sha256:e1494d20ffe7891d07d8cb9a8b306c1a38d48b13575265d090fc08910c56d474 \
--hash=sha256:ec4b4b638b84d42fc48139f9352f6c6587ee1018d55253542ee28db7480cc653 \
--hash=sha256:fa0a570e0a30b9dd618bffbece590ae15726b47f9f1eaf7518dfb35f4d7dcd21
1 change: 1 addition & 0 deletions contrib/requirements/requirements-binaries.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ pycryptodomex<3.7
websocket-client
PyQt5-sip==4.19.13
sip==4.19.8
psutil==5.6.1
13 changes: 13 additions & 0 deletions electron-cash
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
import os, sys
import ctypes

# Note CashShuffle's .proto files have namespace conflicts with keepkey
# This is a workaround to force the python implementation versus the C++
Expand Down Expand Up @@ -114,6 +115,7 @@ from electroncash.commands import get_parser, known_commands, Commands, config_v
from electroncash import daemon
from electroncash import keystore
from electroncash.mnemonic import Mnemonic
from electroncash.winconsole import create_or_attach_console
import electroncash_plugins
import electroncash.web as web

Expand Down Expand Up @@ -313,6 +315,17 @@ def init_plugins(config, gui_name):
if __name__ == '__main__':
# The hook will only be used in the Qt GUI right now
util.setup_thread_excepthook()

# On windows, allocate a console if needed
if sys.platform.startswith('win'):
force_console = '-v' in sys.argv or '--verbose' in sys.argv
if not create_or_attach_console(create=force_console, title='Electron Cash Console') and force_console:
# Force console specified and we couldn't get a console, fail
MB_ICONERROR = 0x10
MB_OK = 0
ctypes.windll.user32.MessageBoxW(0, 'Failed to get a console', 'Electron Cash', MB_OK | MB_ICONERROR)
sys.exit(1)

# on osx, delete Process Serial Number arg generated for apps launched in Finder
sys.argv = list(filter(lambda x: not x.startswith('-psn'), sys.argv))

Expand Down
90 changes: 90 additions & 0 deletions lib/winconsole.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
# Electron Cash - lightweight Bitcoin client
# Copyright (C) 2019 Axel Gembe <derago@gmail.com>
#
# Permission is hereby granted, free of charge, to any person
# obtaining a copy of this software and associated documentation files
# (the "Software"), to deal in the Software without restriction,
# including without limitation the rights to use, copy, modify, merge,
# publish, distribute, sublicense, and/or sell copies of the Software,
# and to permit persons to whom the Software is furnished to do so,
# subject to the following conditions:
#
# The above copyright notice and this permission notice shall be
# included in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
# BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
# ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

"""
This module is for to handling console attaching and / or creation in Windows binaries that are
built for the Windows subsystem and therefore do not automatically allocate a console.
"""

import sys
import os
import ctypes

STD_OUTPUT_HANDLE = -11
FILE_TYPE_DISK = 1

def parent_process_pids() -> int:
"""
Returns all parent process PIDs, starting with the closest parent
"""
try:
import psutil
pid = os.getpid()
while pid > 0:
pid = psutil.Process(pid).ppid()
yield pid
except psutil.NoSuchProcess:
# Parent process not found, likely terminated, nothing we can do
pass

def create_or_attach_console(attach: bool = True, create: bool = False, title: str = None) -> bool:
"""
First this checks if output is redirected to a file and does nothing if it is. Then it tries
to attach to the console of any parent process and if not successful it optionally creates a
console or fails.
If a console was found or created, it will redirect current output handles to this console.
"""
std_out_handle = ctypes.windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE)
if std_out_handle > 0:
if ctypes.windll.kernel32.GetFileType(std_out_handle) == FILE_TYPE_DISK:
# Output is being redirected to a file, do nothing
return

has_console = std_out_handle > 0

if not has_console and attach:
# Try to attach to a parent console
for pid in parent_process_pids():
if ctypes.windll.kernel32.AttachConsole(pid):
has_console = True
break

if not has_console and create:
# Try to allocate a new console
if ctypes.windll.kernel32.AllocConsole():
has_console = True

if not has_console:
return False

if title:
# Set the console title
ctypes.windll.kernel32.SetConsoleTitleW(title)

# Reopen Pythons console input and output handles
conout = open('CONOUT$', 'w')
sys.stdout = conout
sys.stderr = conout
sys.stdin = open('CONIN$', 'r')

return True

0 comments on commit 6166c36

Please sign in to comment.