diff options
| -rw-r--r-- | binaryninjaapi.h | 77 | ||||
| -rw-r--r-- | binaryninjacore.h | 8 | ||||
| -rw-r--r-- | plugin.cpp | 38 | ||||
| -rw-r--r-- | plugins/bntl_utils/src/command/create.rs | 8 | ||||
| -rw-r--r-- | plugins/bntl_utils/src/command/diff.rs | 9 | ||||
| -rw-r--r-- | plugins/bntl_utils/src/command/dump.rs | 10 | ||||
| -rw-r--r-- | plugins/bntl_utils/src/command/validate.rs | 9 | ||||
| -rw-r--r-- | plugins/bntl_utils/src/lib.rs | 8 | ||||
| -rw-r--r-- | plugins/warp/src/plugin.rs | 5 | ||||
| -rw-r--r-- | plugins/warp/src/plugin/commit.rs | 9 | ||||
| -rw-r--r-- | plugins/warp/src/plugin/create.rs | 15 | ||||
| -rw-r--r-- | python/plugin.py | 74 | ||||
| -rw-r--r-- | rust/src/command.rs | 42 |
13 files changed, 259 insertions, 53 deletions
diff --git a/binaryninjaapi.h b/binaryninjaapi.h index a7a64f4c..08c03c30 100644 --- a/binaryninjaapi.h +++ b/binaryninjaapi.h @@ -16502,6 +16502,12 @@ namespace BinaryNinja { { BNPluginCommand m_command; + struct RegisteredGlobalCommand + { + std::function<void()> action; + std::function<bool()> isValid; + }; + struct RegisteredDefaultCommand { std::function<void(BinaryView*)> action; @@ -16568,6 +16574,7 @@ namespace BinaryNinja { std::function<bool(Project*)> isValid; }; + static void GlobalPluginCommandActionCallback(void* ctxt); static void DefaultPluginCommandActionCallback(void* ctxt, BNBinaryView* view); static void AddressPluginCommandActionCallback(void* ctxt, BNBinaryView* view, uint64_t addr); static void RangePluginCommandActionCallback(void* ctxt, BNBinaryView* view, uint64_t addr, uint64_t len); @@ -16586,6 +16593,7 @@ namespace BinaryNinja { void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func, size_t instr); static void ProjectPluginCommandActionCallback(void* ctxt, BNProject* project); + static bool GlobalPluginCommandIsValidCallback(void* ctxt); static bool DefaultPluginCommandIsValidCallback(void* ctxt, BNBinaryView* view); static bool AddressPluginCommandIsValidCallback(void* ctxt, BNBinaryView* view, uint64_t addr); static bool RangePluginCommandIsValidCallback(void* ctxt, BNBinaryView* view, uint64_t addr, uint64_t len); @@ -16611,9 +16619,74 @@ namespace BinaryNinja { PluginCommand& operator=(const PluginCommand& cmd); + /*! Register a command. + + This will appear in the top menu. + + \code{.cpp} + + // Registering a command using a lambda expression + PluginCommand::RegisterGlobal("MyPlugin\\MyAction", "Perform an action", []() { }); + + // Registering a command using a standard static function + // This also works with functions in the global namespace, e.g. "void myCommand()" + void MyPlugin::MyCommand() + { + // Perform an action + } + + PluginCommand::Register("MyPlugin\\MySecondAction", "Perform an action", MyPlugin::MyCommand); + \endcode + + \param name + \parblock + Name of the command to register. This will appear in the top menu. + + You can register submenus to an item by separating names with a \c "\\". The base (farthest right) name will + be the item which upon being clicked will perform the action. + \endparblock + \param description Description of the command + \param action Action to perform + */ + static void RegisterGlobal(const std::string& name, const std::string& description, const std::function<void()>& action); + + /*! Register a command globally, with a validity check. + + This will appear in the top menu. + + \code{.cpp} + + // Registering a command using lambda expressions + PluginCommand::Register("MyPlugin\\MyAction", "Perform an action", [](){ }, [](){ }); + + // Registering a command using a standard static function, and a lambda for the isValid check + // This also works with functions in the global namespace, e.g. "void myCommand()" + void MyPlugin::MyCommand(BinaryView* view) + { + // Perform an action + } + + PluginCommand::Register("MyPlugin\\MySecondAction", "Perform an action", MyPlugin::MyCommand, + [](){ return true; }); + \endcode + + \param name + \parblock + Name of the command to register. This will appear in the top menu and the context menu. + + You can register submenus to an item by separating names with a \c "\\". The base (farthest right) name will + be the item which upon being clicked will perform the action. + \endparblock + \param description Description of the command + \param action Action to perform + \param isValid Function that returns whether the command is allowed to be performed. + */ + static void RegisterGlobal(const std::string& name, const std::string& description, + const std::function<void()>& action, const std::function<bool()>& isValid); + /*! Register a command for a given BinaryView. - This will appear in the top menu and the right-click context menu. + This will appear in the top menu. \code{.cpp} @@ -16649,7 +16722,7 @@ namespace BinaryNinja { /*! Register a command for a given BinaryView, with a validity check. - This will appear in the top menu and the right-click context menu. + This will appear in the top menu. \code{.cpp} diff --git a/binaryninjacore.h b/binaryninjacore.h index 94456c2f..439f87c0 100644 --- a/binaryninjacore.h +++ b/binaryninjacore.h @@ -2784,7 +2784,8 @@ extern "C" MediumLevelILInstructionPluginCommand, HighLevelILFunctionPluginCommand, HighLevelILInstructionPluginCommand, - ProjectPluginCommand + ProjectPluginCommand, + GlobalPluginCommand }; typedef struct BNPluginCommand @@ -2794,6 +2795,7 @@ extern "C" BNPluginCommandType type; void* context; + void (*globalCommand)(void* ctxt); void (*defaultCommand)(void* ctxt, BNBinaryView* view); void (*addressCommand)(void* ctxt, BNBinaryView* view, uint64_t addr); void (*rangeCommand)(void* ctxt, BNBinaryView* view, uint64_t addr, uint64_t len); @@ -2808,6 +2810,7 @@ extern "C" void* ctxt, BNBinaryView* view, BNHighLevelILFunction* func, size_t instr); void (*projectCommand)(void* ctxt, BNProject* view); + bool (*globalIsValid)(void* ctxt); bool (*defaultIsValid)(void* ctxt, BNBinaryView* view); bool (*addressIsValid)(void* ctxt, BNBinaryView* view, uint64_t addr); bool (*rangeIsValid)(void* ctxt, BNBinaryView* view, uint64_t addr, uint64_t len); @@ -7426,6 +7429,8 @@ extern "C" BINARYNINJACOREAPI void BNInstallPendingUpdate(char** errors); // Plugin commands + BINARYNINJACOREAPI void BNRegisterPluginCommandGlobal(const char* name, const char* description, + void (*action)(void* ctxt), bool (*isValid)(void* ctxt), void* context); BINARYNINJACOREAPI void BNRegisterPluginCommand(const char* name, const char* description, void (*action)(void* ctxt, BNBinaryView* view), bool (*isValid)(void* ctxt, BNBinaryView* view), void* context); BINARYNINJACOREAPI void BNRegisterPluginCommandForAddress(const char* name, const char* description, @@ -7460,6 +7465,7 @@ extern "C" void (*action)(void* ctxt, BNProject* project), bool (*isValid)(void* ctxt, BNProject* project), void* context); BINARYNINJACOREAPI BNPluginCommand* BNGetAllPluginCommands(size_t* count); + BINARYNINJACOREAPI BNPluginCommand* BNGetValidPluginCommandsGlobal(size_t* count); BINARYNINJACOREAPI BNPluginCommand* BNGetValidPluginCommands(BNBinaryView* view, size_t* count); BINARYNINJACOREAPI BNPluginCommand* BNGetValidPluginCommandsForAddress( BNBinaryView* view, uint64_t addr, size_t* count); @@ -68,6 +68,13 @@ PluginCommand& PluginCommand::operator=(const PluginCommand& cmd) } +void PluginCommand::GlobalPluginCommandActionCallback(void* ctxt) +{ + RegisteredGlobalCommand* cmd = (RegisteredGlobalCommand*)ctxt; + cmd->action(); +} + + void PluginCommand::DefaultPluginCommandActionCallback(void* ctxt, BNBinaryView* view) { RegisteredDefaultCommand* cmd = (RegisteredDefaultCommand*)ctxt; @@ -172,6 +179,13 @@ void PluginCommand::ProjectPluginCommandActionCallback(void *ctxt, BNProject* pr } +bool PluginCommand::GlobalPluginCommandIsValidCallback(void* ctxt) +{ + RegisteredGlobalCommand* cmd = (RegisteredGlobalCommand*)ctxt; + return cmd->isValid(); +} + + bool PluginCommand::DefaultPluginCommandIsValidCallback(void* ctxt, BNBinaryView* view) { RegisteredDefaultCommand* cmd = (RegisteredDefaultCommand*)ctxt; @@ -276,6 +290,23 @@ bool PluginCommand::ProjectPluginCommandIsValidCallback(void* ctxt, BNProject* p } +void PluginCommand::RegisterGlobal(const string& name, const string& description, const function<void()>& action) +{ + RegisterGlobal(name, description, action, []() { return true; }); +} + + +void PluginCommand::RegisterGlobal(const string& name, const string& description, + const function<void()>& action, const function<bool()>& isValid) +{ + RegisteredGlobalCommand* cmd = new RegisteredGlobalCommand; + cmd->action = action; + cmd->isValid = isValid; + BNRegisterPluginCommandGlobal(name.c_str(), description.c_str(), GlobalPluginCommandActionCallback, + GlobalPluginCommandIsValidCallback, cmd); +} + + void PluginCommand::Register( const string& name, const string& description, const function<void(BinaryView* view)>& action) { @@ -515,6 +546,10 @@ bool PluginCommand::IsValid(const PluginCommandContext& ctxt) const { switch (m_command.type) { + case GlobalPluginCommand: + if (!m_command.globalIsValid) + return true; + return m_command.globalIsValid(m_command.context); case DefaultPluginCommand: if (!ctxt.binaryView) return false; @@ -606,6 +641,9 @@ void PluginCommand::Execute(const PluginCommandContext& ctxt) const switch (m_command.type) { + case GlobalPluginCommand: + m_command.globalCommand(m_command.context); + break; case DefaultPluginCommand: m_command.defaultCommand(m_command.context, ctxt.binaryView->GetObject()); break; diff --git a/plugins/bntl_utils/src/command/create.rs b/plugins/bntl_utils/src/command/create.rs index 5fa8c2be..96d0eb60 100644 --- a/plugins/bntl_utils/src/command/create.rs +++ b/plugins/bntl_utils/src/command/create.rs @@ -3,7 +3,7 @@ use crate::process::{new_processing_state_background_thread, TypeLibProcessor}; use crate::validate::TypeLibValidater; use binaryninja::background_task::BackgroundTask; use binaryninja::binary_view::{BinaryView, BinaryViewExt}; -use binaryninja::command::{Command, ProjectCommand}; +use binaryninja::command::{Command, GlobalCommand, ProjectCommand}; use binaryninja::interaction::{Form, FormInputField, MessageBoxButtonSet, MessageBoxIcon}; use binaryninja::platform::Platform; use binaryninja::project::Project; @@ -188,14 +188,14 @@ impl CreateFromDirectory { } } -impl Command for CreateFromDirectory { - fn action(&self, _view: &BinaryView) { +impl GlobalCommand for CreateFromDirectory { + fn action(&self) { thread::spawn(move || { CreateFromDirectory::execute(); }); } - fn valid(&self, _view: &BinaryView) -> bool { + fn valid(&self) -> bool { true } } diff --git a/plugins/bntl_utils/src/command/diff.rs b/plugins/bntl_utils/src/command/diff.rs index 48501433..6cc0fe0a 100644 --- a/plugins/bntl_utils/src/command/diff.rs +++ b/plugins/bntl_utils/src/command/diff.rs @@ -2,8 +2,7 @@ use crate::command::OutputDirectoryField; use crate::diff::TILDiff; use crate::helper::path_to_type_libraries; use binaryninja::background_task::BackgroundTask; -use binaryninja::binary_view::BinaryView; -use binaryninja::command::Command; +use binaryninja::command::GlobalCommand; use binaryninja::interaction::{Form, FormInputField}; use std::path::PathBuf; use std::thread; @@ -95,14 +94,14 @@ impl Diff { } } -impl Command for Diff { - fn action(&self, _view: &BinaryView) { +impl GlobalCommand for Diff { + fn action(&self) { thread::spawn(move || { Diff::execute(); }); } - fn valid(&self, _view: &BinaryView) -> bool { + fn valid(&self) -> bool { true } } diff --git a/plugins/bntl_utils/src/command/dump.rs b/plugins/bntl_utils/src/command/dump.rs index 96c3b264..7b48900d 100644 --- a/plugins/bntl_utils/src/command/dump.rs +++ b/plugins/bntl_utils/src/command/dump.rs @@ -2,8 +2,7 @@ use crate::command::{InputDirectoryField, OutputDirectoryField}; use crate::dump::TILDump; use crate::helper::path_to_type_libraries; use binaryninja::background_task::BackgroundTask; -use binaryninja::binary_view::BinaryView; -use binaryninja::command::Command; +use binaryninja::command::GlobalCommand; use binaryninja::interaction::Form; pub struct Dump; @@ -48,15 +47,14 @@ impl Dump { } } -impl Command for Dump { - // TODO: We need a command type that does not require a binary view. - fn action(&self, _view: &BinaryView) { +impl GlobalCommand for Dump { + fn action(&self) { std::thread::spawn(move || { Dump::execute(); }); } - fn valid(&self, _view: &BinaryView) -> bool { + fn valid(&self) -> bool { true } } diff --git a/plugins/bntl_utils/src/command/validate.rs b/plugins/bntl_utils/src/command/validate.rs index b95699e0..921309fd 100644 --- a/plugins/bntl_utils/src/command/validate.rs +++ b/plugins/bntl_utils/src/command/validate.rs @@ -1,8 +1,7 @@ use crate::command::{InputDirectoryField, OutputDirectoryField}; use crate::helper::path_to_type_libraries; use crate::validate::TypeLibValidater; -use binaryninja::binary_view::BinaryView; -use binaryninja::command::Command; +use binaryninja::command::GlobalCommand; use binaryninja::interaction::Form; use binaryninja::platform::Platform; @@ -68,14 +67,14 @@ impl Validate { } } -impl Command for Validate { - fn action(&self, _view: &BinaryView) { +impl GlobalCommand for Validate { + fn action(&self) { std::thread::spawn(move || { Validate::execute(); }); } - fn valid(&self, _view: &BinaryView) -> bool { + fn valid(&self) -> bool { true } } diff --git a/plugins/bntl_utils/src/lib.rs b/plugins/bntl_utils/src/lib.rs index aad6cdcf..fb96746a 100644 --- a/plugins/bntl_utils/src/lib.rs +++ b/plugins/bntl_utils/src/lib.rs @@ -35,25 +35,25 @@ fn plugin_init() -> Result<(), ()> { command::create::CreateFromProject {}, ); - binaryninja::command::register_command( + binaryninja::command::register_global_command( "BNTL\\Create\\From Directory", "Create .bntl files from the given directory", command::create::CreateFromDirectory {}, ); - binaryninja::command::register_command( + binaryninja::command::register_global_command( "BNTL\\Diff", "Diff two .bntl files and output the difference to a file", command::diff::Diff {}, ); - binaryninja::command::register_command( + binaryninja::command::register_global_command( "BNTL\\Dump To Header", "Dump a .bntl file to a header file", command::dump::Dump {}, ); - binaryninja::command::register_command( + binaryninja::command::register_global_command( "BNTL\\Validate", "Validate a .bntl file and report the issues", command::validate::Validate {}, diff --git a/plugins/warp/src/plugin.rs b/plugins/warp/src/plugin.rs index cb527fff..d163205b 100644 --- a/plugins/warp/src/plugin.rs +++ b/plugins/warp/src/plugin.rs @@ -11,6 +11,7 @@ use crate::{core_signature_dir, user_signature_dir}; use binaryninja::background_task::BackgroundTask; use binaryninja::command::{ register_command, register_command_for_function, register_command_for_project, + register_global_command, }; use binaryninja::is_ui_enabled; use binaryninja::settings::{QueryOptions, Settings}; @@ -192,7 +193,7 @@ fn plugin_init() -> bool { load::LoadSignatureFile {}, ); - register_command( + register_global_command( "WARP\\Commit File", "Commit file to a source", commit::CommitFile {}, @@ -234,7 +235,7 @@ fn plugin_init() -> bool { create::CreateFromCurrentView {}, ); - register_command( + register_global_command( "WARP\\Create\\From File(s)", "Creates a signature file containing all selected functions", create::CreateFromFiles {}, diff --git a/plugins/warp/src/plugin/commit.rs b/plugins/warp/src/plugin/commit.rs index d98a356a..4e8a9fee 100644 --- a/plugins/warp/src/plugin/commit.rs +++ b/plugins/warp/src/plugin/commit.rs @@ -3,8 +3,7 @@ use crate::cache::container::cached_containers; use crate::container::{SourceId, SourcePath}; use crate::plugin::create::OpenFileField; -use binaryninja::binary_view::BinaryView; -use binaryninja::command::Command; +use binaryninja::command::GlobalCommand; use binaryninja::interaction::{Form, FormInputField}; use warp::chunk::ChunkKind; use warp::WarpFile; @@ -137,14 +136,14 @@ impl CommitFile { } } -impl Command for CommitFile { - fn action(&self, _view: &BinaryView) { +impl GlobalCommand for CommitFile { + fn action(&self) { std::thread::spawn(move || { Self::execute(); }); } - fn valid(&self, _view: &BinaryView) -> bool { + fn valid(&self) -> bool { true } } diff --git a/plugins/warp/src/plugin/create.rs b/plugins/warp/src/plugin/create.rs index 2a748819..5f5527a7 100644 --- a/plugins/warp/src/plugin/create.rs +++ b/plugins/warp/src/plugin/create.rs @@ -6,7 +6,8 @@ use crate::report::{ReportGenerator, ReportKindField}; use crate::{user_signature_dir, INCLUDE_TAG_NAME}; use binaryninja::background_task::BackgroundTask; use binaryninja::binary_view::{BinaryView, BinaryViewExt}; -use binaryninja::command::Command; +use binaryninja::command::{Command, GlobalCommand}; +use binaryninja::file_metadata::FileMetadata; use binaryninja::interaction::form::{Form, FormInputField}; use binaryninja::interaction::{MessageBoxButtonResult, MessageBoxButtonSet, MessageBoxIcon}; use binaryninja::rc::Ref; @@ -248,15 +249,17 @@ impl Command for CreateFromCurrentView { pub struct CreateFromFiles; -impl Command for CreateFromFiles { - fn action(&self, view: &BinaryView) { - let view = view.to_owned(); +impl GlobalCommand for CreateFromFiles { + fn action(&self) { + let empty_file_metadata = FileMetadata::new(); + let empty_bv = BinaryView::from_data(&empty_file_metadata, &[]); thread::spawn(move || { - CreateFromCurrentView::execute(view, true); + CreateFromCurrentView::execute(empty_bv.to_owned(), true); + empty_bv.file().close(); }); } - fn valid(&self, _view: &BinaryView) -> bool { + fn valid(&self) -> bool { true } } diff --git a/python/plugin.py b/python/plugin.py index 0284d882..988f7c41 100644 --- a/python/plugin.py +++ b/python/plugin.py @@ -55,7 +55,8 @@ class PluginCommandContext: self._length = 0 self._function = None self._instruction = None - self._project = view.project + if view is not None: + self._project = view.project def __len__(self): return self._length @@ -140,6 +141,14 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): self._type = PluginCommandType(cmd.type) @staticmethod + def _global_action(action): + try: + action() + except: + log_error_for_exception("Unhandled Python exception in PluginCommand._global_action") + + + @staticmethod def _default_action(view, action): try: file_metadata = filemetadata.FileMetadata(handle=core.BNGetFileForView(view)) @@ -255,6 +264,16 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): log_error_for_exception("Unhandled Python exception in PluginCommand._project_action") @staticmethod + def _global_is_valid(is_valid): + try: + if is_valid is None: + return True + return is_valid() + except: + log_error_for_exception("Unhandled Python exception in PluginCommand._global_is_valid") + return False + + @staticmethod def _default_is_valid(view, is_valid): try: if is_valid is None: @@ -409,6 +428,38 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): return False @classmethod + def register_global( + cls, name: str, description: str, action: Callable[[], None], + is_valid: Optional[Callable[[], bool]] = None + ): + r""" + ``register_global`` Register a command globally + + :param str name: name of the command (use 'Folder\\Name' to have the menu item nested in a folder) + :param str description: description of the command + :param callback action: function to call + :param callback is_valid: optional argument of a function to determine whether the command should be enabled + :rtype: None + :Example: + + >>> def my_command(): + >>> log_info(f"My command was called on bv") + >>> PluginCommand.register_global("My Command", "My command description (not used)", my_command) + True + >>> def is_valid() -> bool: + >>> return False + >>> PluginCommand.register_global("My Command (With Valid Function)", "My command description (not used)", my_plugin, is_valid) + True + + .. warning:: Calling ``register_global`` with the same function name will replace the existing function but will leak the memory of the original plugin. + """ + binaryninja._init_plugins() + action_obj = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(lambda ctxt: cls._global_action(action)) + is_valid_obj = ctypes.CFUNCTYPE(ctypes.c_bool, ctypes.c_void_p)(lambda ctxt: cls._global_is_valid(is_valid)) + cls._registered_commands.append((action_obj, is_valid_obj)) + core.BNRegisterPluginCommandGlobal(name, description, action_obj, is_valid_obj, None) + + @classmethod def register( cls, name: str, description: str, action: Callable[['binaryview.BinaryView'], None], is_valid: Optional[Callable[['binaryview.BinaryView'], bool]] = None @@ -831,6 +882,17 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): return result def is_valid(self, context: PluginCommandContext): + if self._command.type == PluginCommandType.ProjectPluginCommand: + if context.project is None: + return False + if not self._command.projectIsValid: + return True + return self._command.projectIsValid(self._command.context, context.project.handle) + elif self._command.type == PluginCommandType.GlobalPluginCommand: + if not self._command.globalIsValid: + return True + return self._command.globalIsValid(self._command.context) + if context.view is None: return False if self._command.type == PluginCommandType.DefaultPluginCommand: @@ -912,12 +974,6 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): self._command.context, context.view.handle, context.instruction.function.handle, context.instruction.instr_index ) - elif self._command.type == PluginCommandType.ProjectPluginCommand: - if context.project is None: - return False - if not self._command.projectIsValid: - return True - return self._command.projectIsValid(self._command.context, context.project.handle) return False def execute(self, context: PluginCommandContext): @@ -933,7 +989,9 @@ class PluginCommand(metaclass=_PluginCommandMetaClass): """ if not self.is_valid(context): return - if self._command.type == PluginCommandType.DefaultPluginCommand: + if self._command.type == PluginCommandType.GlobalPluginCommand: + self._command.globalCommand(self._command.context) + elif self._command.type == PluginCommandType.DefaultPluginCommand: self._command.defaultCommand(self._command.context, context.view.handle) elif self._command.type == PluginCommandType.AddressPluginCommand: self._command.addressCommand(self._command.context, context.view.handle, context.address) diff --git a/rust/src/command.rs b/rust/src/command.rs index 9f585633..ac9870fa 100644 --- a/rust/src/command.rs +++ b/rust/src/command.rs @@ -32,11 +32,7 @@ //! //! The return value of these functions should indicate whether they successfully initialized themselves. -use binaryninjacore_sys::{ - BNBinaryView, BNFunction, BNProject, BNRegisterPluginCommand, - BNRegisterPluginCommandForAddress, BNRegisterPluginCommandForFunction, - BNRegisterPluginCommandForProject, BNRegisterPluginCommandForRange, -}; +use binaryninjacore_sys::*; use crate::binary_view::BinaryView; use crate::function::Function; @@ -46,6 +42,42 @@ use std::ops::Range; use std::os::raw::c_void; use std::ptr::NonNull; +pub trait GlobalCommand: 'static + Sync { + fn action(&self); + fn valid(&self) -> bool; +} + +pub fn register_global_command<C: GlobalCommand>(name: &str, desc: &str, command: C) { + extern "C" fn cb_action<C>(ctxt: *mut c_void) + where + C: GlobalCommand, + { + let cmd = unsafe { &*(ctxt as *const C) }; + cmd.action(); + } + + extern "C" fn cb_valid<C>(ctxt: *mut c_void) -> bool + where + C: GlobalCommand, + { + let cmd = unsafe { &*(ctxt as *const C) }; + cmd.valid() + } + + let name = name.to_cstr(); + let desc = desc.to_cstr(); + let ctxt = Box::into_raw(Box::new(command)); + unsafe { + BNRegisterPluginCommandGlobal( + name.as_ptr(), + desc.as_ptr(), + Some(cb_action::<C>), + Some(cb_valid::<C>), + ctxt as *mut _, + ); + } +} + /// The trait required for generic commands. See [register_command] for example usage. pub trait Command: 'static + Sync { fn action(&self, view: &BinaryView); |
