Last active
February 8, 2019 22:15
-
-
Save az0/06be331b83ba82f6bdbe8763a3cdeb4a to your computer and use it in GitHub Desktop.
Workaround for "SAS Message Log" dialog
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# | |
# Purpose: | |
# Close the "SAS Message Log" dialog caused by the logging message | |
# "ODBC: COMMIT performed on connection #" because this hangs the SAS process, | |
# so control is not turned back over to SJS. | |
# | |
# Requirement: | |
# Python version 3 | |
# | |
# By: | |
# Andrew Ziem, January 2019 | |
# | |
# | |
# See article https://heuristicandrew.blogspot.com/2019/01/sas-message-log-with-odbc-commit.html | |
# | |
# Some code thanks to https://gist.github.com/inaz2/6f72f18af15c47ab7432 | |
# | |
import os | |
import ctypes | |
import logging | |
import time | |
from ctypes import CFUNCTYPE, POINTER, c_bool, c_int, cdll, create_unicode_buffer | |
# ctypes functions | |
EnumWindows = cdll.user32.EnumWindows | |
EnumWindowsProc = CFUNCTYPE(c_bool, POINTER(c_int), POINTER(c_int)) | |
GetWindowText = cdll.user32.GetWindowTextW | |
GetWindowTextLength = cdll.user32.GetWindowTextLengthW | |
IsWindowVisible = cdll.user32.IsWindowVisible | |
SendMessage = ctypes.windll.user32.SendMessageW | |
# constant | |
WM_CLOSE = 0x0010 | |
TARGET_WINDOW_NAME = 'SAS Message Log' | |
POLLING_FREQUENCY_IN_SECONDS = 30 | |
# environment setup | |
logging.basicConfig(format='%(asctime)s %(message)s', level=logging.DEBUG) | |
def get_mutex_fn(): | |
"""Return the filename of them mutex""" | |
import tempfile | |
return os.path.join(tempfile.gettempdir(), 'close_sas_message_log_mutex') | |
def is_other_process_running(): | |
"""Is there another process like this?""" | |
if not os.path.exists(get_mutex_fn()): | |
return False | |
file_mtime = os.stat(get_mutex_fn()).st_mtime | |
file_age_seconds = time.time() - file_mtime | |
def update_mutex(): | |
"""Let other processes know this process is alive""" | |
logging.debug('Touching mutex: %s', get_mutex_fn()) | |
from pathlib import Path | |
Path(get_mutex_fn()).touch() | |
def print_last_error(rc, ctx): | |
"""Print last Windows error, if there was an error""" | |
if not rc == 0: | |
return | |
last_error_code = ctypes.GetLastError() | |
last_err_msg = ctypes.FormatError(last_error_code) | |
print('error %s in %s: %s' % (last_error_code, ctx, last_err_msg)) | |
def enum_func(hwnd, lParam): | |
"""Callback function for enumerating windows""" | |
if IsWindowVisible(hwnd): | |
length = GetWindowTextLength(hwnd) | |
buff = create_unicode_buffer(length + 1) | |
GetWindowText(hwnd, buff, length + 1) | |
if buff.value: | |
if buff.value == TARGET_WINDOW_NAME: | |
logging.warning('Closing window with title "%s"', buff.value) | |
SendMessage(hwnd, WM_CLOSE, 0, 0) | |
# True means to keep enumerating | |
return True | |
def loop(): | |
"""The main loop to keep checking for the window to close""" | |
if is_other_process_running(): | |
logger.error('Another detected because of mutex: %s', get_mutex_fn()) | |
return | |
while True: | |
logging.debug('Enumerating titles of active windows') | |
rc = EnumWindows(EnumWindowsProc(enum_func), 0) | |
print_last_error(rc, 'EnumWindows') | |
update_mutex() | |
logging.info('Sleeping for %s seconds' % POLLING_FREQUENCY_IN_SECONDS) | |
time.sleep(POLLING_FREQUENCY_IN_SECONDS) | |
try: | |
loop() | |
except KeyboardInterrupt: | |
logging.warning('Keyboard interrupt detected, so deleting mutex') | |
os.remove(get_mutex_fn()) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment