Skip to content

Instantly share code, notes, and snippets.

What would you like to do?
Workaround for "SAS Message Log" dialog
# 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
# Some code thanks to
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
# 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
def print_last_error(rc, ctx):
"""Print last Windows error, if there was an error"""
if not rc == 0:
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())
while True:
logging.debug('Enumerating titles of active windows')
rc = EnumWindows(EnumWindowsProc(enum_func), 0)
print_last_error(rc, 'EnumWindows')
update_mutex()'Sleeping for %s seconds' % POLLING_FREQUENCY_IN_SECONDS)
except KeyboardInterrupt:
logging.warning('Keyboard interrupt detected, so deleting mutex')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.