From 207925956bb8ca0174d40523fda70bfb5bc7bd38 Mon Sep 17 00:00:00 2001 From: Glenn Smith Date: Mon, 26 Sep 2022 23:12:20 -0400 Subject: Interaction::RunProgressDialog --- python/interaction.py | 43 ++++++++++++++++++++++++++++++++++++++++++- python/plugin.py | 40 ++++++++++++++++++++-------------------- 2 files changed, 62 insertions(+), 21 deletions(-) (limited to 'python') diff --git a/python/interaction.py b/python/interaction.py index 2331b586..9fb5d9c7 100644 --- a/python/interaction.py +++ b/python/interaction.py @@ -21,7 +21,7 @@ import ctypes import traceback import webbrowser -from typing import Optional +from typing import Optional, Callable # Binary Ninja components from . import _binaryninjacore as core @@ -29,6 +29,7 @@ from .enums import FormInputFieldType, MessageBoxIcon, MessageBoxButtonSet, Mess from . import binaryview from .log import log_error from . import flowgraph +from . import mainthread class LabelField: @@ -493,6 +494,7 @@ class InteractionHandler: self._cb.getFormInput = self._cb.getFormInput.__class__(self._get_form_input) self._cb.showMessageBox = self._cb.showMessageBox.__class__(self._show_message_box) self._cb.openUrl = self._cb.openUrl.__class__(self._open_url) + self._cb.runProgressDialog = self._cb.runProgressDialog.__class__(self._run_progress_dialog) def register(self): self.__class__._interaction_handler = self @@ -708,6 +710,17 @@ class InteractionHandler: log_error(traceback.format_exc()) return False + def _run_progress_dialog(self, title, can_cancel, task, task_ctxt): + try: + def py_task(progress: Callable[[int, int], bool]): + progress_c = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t, lambda ctxt, cur, max: progress(cur, max)) + task(task_ctxt, progress_c, None) + + return self.run_progress_dialog(title, can_cancel, py_task) + except: + log_error(traceback.format_exc()) + return False + def show_plain_text_report(self, view, title, contents): pass @@ -762,6 +775,10 @@ class InteractionHandler: webbrowser.open(url) return True + def run_progress_dialog(self, task: Callable[[Callable[[int, int], bool]], None]) -> bool: + mainthread.execute_on_main_thread_and_wait(lambda: task(lambda cur, max: True)) + return True + class PlainTextReport: def __init__(self, title, contents, view=None): @@ -1359,3 +1376,27 @@ def open_url(url): :rtype: bool """ return core.BNOpenUrl(url) + + +def run_progress_dialog(title: str, can_cancel: bool, task: Callable[[Callable[[int, int], bool]], None]) -> bool: + """ + ``run_progress_dialog`` runs a given task in a background thread, showing an updating + progress bar which the user can cancel. + + :param title: Dialog title + :param can_cancel: If the task can be cancelled + :param task: Function to perform the task, taking as a parameter a function which should + be called to report progress updates and check for cancellation. If the progress function + returns false, the user has requested to cancel, and the task should handle this appropriately. + :return: True if not cancelled + """ + + progress_type = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p, ctypes.c_size_t, ctypes.c_size_t) + task_type = ctypes.CFUNCTYPE(None, ctypes.c_void_p, progress_type, ctypes.c_void_p) + + def do_task(ctxt: ctypes.c_void_p, progress: progress_type, progress_ctxt: ctypes.c_void_p): + def py_progress(cur: int, max: int) -> bool: + return progress(progress_ctxt, cur, max) + task(py_progress) + + return core.BNRunProgressDialog(title, can_cancel, task_type(do_task), None) diff --git a/python/plugin.py b/python/plugin.py index f1720ccd..eec25683 100644 --- a/python/plugin.py +++ b/python/plugin.py @@ -358,8 +358,8 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): @classmethod def register( - cls, name: str, description: str, action: Callable[[binaryview.BinaryView], None], - is_valid: Optional[Callable[[binaryview.BinaryView], bool]] = None + cls, name: str, description: str, action: Callable[['binaryview.BinaryView'], None], + is_valid: Optional[Callable[['binaryview.BinaryView'], bool]] = None ): r""" ``register`` Register a plugin @@ -384,8 +384,8 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): @classmethod def register_for_address( - cls, name: str, description: str, action: Callable[[binaryview.BinaryView, int], None], - is_valid: Optional[Callable[[binaryview.BinaryView, int], bool]] = None + cls, name: str, description: str, action: Callable[['binaryview.BinaryView', int], None], + is_valid: Optional[Callable[['binaryview.BinaryView', int], bool]] = None ): r""" ``register_for_address`` Register a plugin to be called with an address argument @@ -410,8 +410,8 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): @classmethod def register_for_range( - cls, name: str, description: str, action: Callable[[binaryview.BinaryView, int, int], None], - is_valid: Optional[Callable[[binaryview.BinaryView, int, int], bool]] = None + cls, name: str, description: str, action: Callable[['binaryview.BinaryView', int, int], None], + is_valid: Optional[Callable[['binaryview.BinaryView', int, int], bool]] = None ): r""" ``register_for_range`` Register a plugin to be called with a range argument @@ -436,8 +436,8 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): @classmethod def register_for_function( - cls, name: str, description: str, action: Callable[[binaryview.BinaryView, function.Function], None], - is_valid: Optional[Callable[[binaryview.BinaryView, function.Function], bool]] = None + cls, name: str, description: str, action: Callable[['binaryview.BinaryView', 'function.Function'], None], + is_valid: Optional[Callable[['binaryview.BinaryView', 'function.Function'], bool]] = None ): r""" ``register_for_function`` Register a plugin to be called with a function argument @@ -462,9 +462,9 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): @classmethod def register_for_low_level_il_function( - cls, name: str, description: str, action: Callable[[binaryview.BinaryView, lowlevelil.LowLevelILFunction], + cls, name: str, description: str, action: Callable[['binaryview.BinaryView', 'lowlevelil.LowLevelILFunction'], None], - is_valid: Optional[Callable[[binaryview.BinaryView, lowlevelil.LowLevelILFunction], bool]] = None + is_valid: Optional[Callable[['binaryview.BinaryView', 'lowlevelil.LowLevelILFunction'], bool]] = None ): r""" ``register_for_low_level_il_function`` Register a plugin to be called with a low level IL function argument @@ -490,9 +490,9 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): @classmethod def register_for_low_level_il_instruction( - cls, name: str, description: str, action: Callable[[binaryview.BinaryView, lowlevelil.LowLevelILInstruction], + cls, name: str, description: str, action: Callable[['binaryview.BinaryView', 'lowlevelil.LowLevelILInstruction'], None], - is_valid: Optional[Callable[[binaryview.BinaryView, lowlevelil.LowLevelILInstruction], bool]] = None + is_valid: Optional[Callable[['binaryview.BinaryView', 'lowlevelil.LowLevelILInstruction'], bool]] = None ): r""" ``register_for_low_level_il_instruction`` Register a plugin to be called with a low level IL instruction argument @@ -519,9 +519,9 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): @classmethod def register_for_medium_level_il_function( - cls, name: str, description: str, action: Callable[[binaryview.BinaryView, mediumlevelil.MediumLevelILFunction], + cls, name: str, description: str, action: Callable[['binaryview.BinaryView', 'mediumlevelil.MediumLevelILFunction'], None], - is_valid: Optional[Callable[[binaryview.BinaryView, mediumlevelil.MediumLevelILFunction], bool]] = None + is_valid: Optional[Callable[['binaryview.BinaryView', 'mediumlevelil.MediumLevelILFunction'], bool]] = None ): r""" ``register_for_medium_level_il_function`` Register a plugin to be called with a medium level IL function argument @@ -548,8 +548,8 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): @classmethod def register_for_medium_level_il_instruction( cls, name: str, description: str, - action: Callable[[binaryview.BinaryView, mediumlevelil.MediumLevelILInstruction], None], - is_valid: Optional[Callable[[binaryview.BinaryView, mediumlevelil.MediumLevelILInstruction], bool]] = None + action: Callable[['binaryview.BinaryView', 'mediumlevelil.MediumLevelILInstruction'], None], + is_valid: Optional[Callable[['binaryview.BinaryView', 'mediumlevelil.MediumLevelILInstruction'], bool]] = None ): r""" ``register_for_medium_level_il_instruction`` Register a plugin to be called with a medium level IL instruction argument @@ -576,9 +576,9 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): @classmethod def register_for_high_level_il_function( - cls, name: str, description: str, action: Callable[[binaryview.BinaryView, highlevelil.HighLevelILFunction], + cls, name: str, description: str, action: Callable[['binaryview.BinaryView', 'highlevelil.HighLevelILFunction'], None], - is_valid: Optional[Callable[[binaryview.BinaryView, highlevelil.HighLevelILFunction], bool]] = None + is_valid: Optional[Callable[['binaryview.BinaryView', 'highlevelil.HighLevelILFunction'], bool]] = None ): r""" ``register_for_high_level_il_function`` Register a plugin to be called with a high level IL function argument @@ -604,9 +604,9 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): @classmethod def register_for_high_level_il_instruction( - cls, name: str, description: str, action: Callable[[binaryview.BinaryView, highlevelil.HighLevelILInstruction], + cls, name: str, description: str, action: Callable[['binaryview.BinaryView', 'highlevelil.HighLevelILInstruction'], None], - is_valid: Optional[Callable[[binaryview.BinaryView, highlevelil.HighLevelILInstruction], bool]] = None + is_valid: Optional[Callable[['binaryview.BinaryView', 'highlevelil.HighLevelILInstruction'], bool]] = None ): r""" ``register_for_high_level_il_instruction`` Register a plugin to be called with a high level IL instruction argument -- cgit v1.3.1