This commit is contained in:
Igor Skochinsky
2019-12-20 15:58:02 +01:00
15 changed files with 311 additions and 99 deletions
+155 -16
View File
@@ -13,6 +13,8 @@
import ida_idaapi
import ida_hexrays
import ida_lines
import ida_typeinf
# --------------------------------------------------------------------------
class func_stroff_ah_t(ida_kernwin.action_handler_t):
@@ -20,11 +22,12 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
ida_kernwin.action_handler_t.__init__(self)
def activate(self, ctx):
# get current item
# get the current item
vu = ida_hexrays.get_widget_vdui(ctx.widget)
vu.get_current_item(idaapi.USE_KEYBOARD)
vu.get_current_item(ida_hexrays.USE_KEYBOARD)
# check the current item is union field
# REGION1, will be referenced latter
# check that the current item is a union field
if not vu.item.is_citem():
return 0
e = vu.item.e
@@ -41,8 +44,10 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
break
if not e.type.is_udt():
return 0
# END REGION1
# calculate member's offset
# REGION2
# calculate the member offset
off = 0
e = vu.item.e
while True:
@@ -55,7 +60,9 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
break
if not e2.type.is_udt():
break
# END REGION2
# REGION3
# go up and collect more member references (in order to calculate the final offset)
p = vu.item.e
while True:
@@ -63,8 +70,8 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
if p2.op == ida_hexrays.cot_memptr:
break
if p2.op == ida_hexrays.cot_memref:
e2 = p2
tif = remove_pointer(e2.x.type)
e2 = p2.cexpr
tif = ida_typeinf.remove_pointer(e2.x.type)
if not tif.is_union():
off += e2.m
p = p2
@@ -81,45 +88,59 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
objsize = add.type.get_ptrarr_objsize()
nbytes = delta * objsize
off += nbytes
# we can use the calling helpers like WORD/BYTE/...
# to calculate the more precise offset
# we could use helpers like WORD/BYTE/... to calculate a more precise offset
# if ( p2->op == cot_call && (e2->exflags & EXFL_LVALUE) != 0 )
break
# END REGION3
# REGION4
ea = vu.item.e.ea
# the item itself may be unaddressable.
# TODO: find its addressable parent
if ea == idaapi.BADADDR:
if ea == ida_idaapi.BADADDR:
return 0
# END REGION4
# REGION5
# prepare the text representation for the item,
# use the neighborhoods of cursor
line = idaapi.tag_remove(ida_kernwin.get_custom_viewer_curline(vu.ct, False))
line = ida_lines.tag_remove(ida_kernwin.get_custom_viewer_curline(vu.ct, False))
line_len = len(line)
x = max(0, vu.cpos.x - 10)
l = min(10, line_len - vu.cpos.x) + 10
line = line[x:x+l]
# END REGION5
# REGION6
ops = ida_hexrays.ui_stroff_ops_t()
op = ops.push_back()
op.offset = off
op.text = line
# END REGION6
# REGION7
class set_union_sel_t(ida_hexrays.ui_stroff_applicator_t):
def __init__(self, eas):
def __init__(self, ea):
ida_hexrays.ui_stroff_applicator_t.__init__(self)
self.eas = eas
self.ea = ea
def apply(self, opnum, path):
vu.cfunc.set_user_union_selection(self.eas[opnum], path)
def apply(self, opnum, path, top_tif, spath):
typename = ida_typeinf.print_tinfo('', 0, 0, ida_typeinf.PRTYPE_1LINE, top_tif, '', '')
idaapi.msg("User selected %s of type %s\n" % (spath, typename))
if path.empty():
return False
vu.cfunc.set_user_union_selection(self.ea, path)
vu.cfunc.save_user_unions()
return True
# END REGION7
su = set_union_sel_t([ea])
# REGION8
su = set_union_sel_t(ea)
res = ida_hexrays.select_udt_by_offset(None, ops, su)
if res != 0:
# regenerate ctree
vu.refresh_view(True)
# END REGION8
return 1
@@ -131,7 +152,7 @@ class func_stroff_ah_t(ida_kernwin.action_handler_t):
# --------------------------------------------------------------------------
if ida_hexrays.init_hexrays_plugin():
print("Hex-rays version %s has been detected, Structure offsets ready to use" % idaapi.get_hexrays_version())
print("Hex-rays version %s has been detected, Structure offsets ready to use" % ida_hexrays.get_hexrays_version())
ida_kernwin.register_action(
ida_kernwin.action_desc_t(
"vds17:strchoose",
@@ -140,3 +161,121 @@ if ida_hexrays.init_hexrays_plugin():
"Shift+T"))
else:
print('vds17: Hex-rays is not available.')
"""
# A few notes about the VDS17 sample
You can find two VDS17 samples in the IDA Pro install directory:
python/examples/hexrays/vds17.py
plugins/hexrays_sdk/plugins/vds17
The former is an IDAPython plugin and the latter is a C++ IDA Pro plugin.
Actually they have the same functionality.
Just to be more concrete the vds17.py plugin will be used below.
## How the user interface works
Let us suppose that we have the following local types:
1 C struct {int c0;int c1;}
2 U union {int u0;__int16 u1;}
3 D struct {int d0;C d1;U d2;}
4 E struct {int e0;D e1;}
5 res_t struct {int r0;int r1;__int16 r2;}
and the decompiler generates the following pseudocode:
void __cdecl f(res_t *r)
{
r->r0 = e->e1.d1.c0;
r->r1 = e->e1.d2.u0;
r->r2 = e->e1.d2.u1;
}
As we see, it looks good, the decompiler did a really good job.
But let us imagine that we need reference to the ```e1.d2.u0``` union member on the last line.
At first we need to load the VDS17 plugin. For that,
place the cursor at the ```u1``` union member on the last line
and use ```Shift-T```.
The "Structure offsets" dialog appears on the screen.
The left pane of the dialog contains the available local types
and the right pane has the following view:
[checked] e->e1.d2.u1; | 10h | |
Use the left pane to select ```E```, expand it, and select (Double-click) on the ```u0``` member.
The right pane will change to:
[checked] e->e1.d2.u1; | 10h | [checke] E.e1.d2.u0 |
Since this is what we want, press the ```OK``` button and the pseudocode gets changed:
void __cdecl f(res_t *r)
{
r->r0 = e->e1.d1.c0;
r->r1 = e->e1.d2.u0;
r->r2 = e->e1.d2.u0;
}
### What do we need this plugin for?
All union members have the same offset in the parent structure.
The decompiler selects the first suitable union member.
Sometimes we need to change this selection and use another union member.
## API details
Let us look into ```python/examples/hexrays/vds17.py```.
* REGION1: check that the cursor points to the union member
* REGION2: calculate the member offset
* REGION3: sometimes there is a need to adjust the offset
* REGION4: the pointed item must be addressable
Then the magic begins:
* REGION5: we need something to show in the ```Operand``` column of the right pane.
The text around the cursor looks like a good compromise.
* REGION6: we are ready to fill the right pane: we have calculated the offset and
have prepared the descriptive text for the operand.
* REGION7: it is a callback that informs us about the user selection (will be described later)
* REGION8: activate the "Structure offsets" dialog, let the user select,
the callback specified above will update the union member, refresh pseudocode
### The devil is in the details
The main part of callback is the ```apply``` method (REGION7).
It receives two arguments:
1. ```opnum``` is the number of the selected operand (line) in the right pane of the dialog.
We need something to map the line number to our operand.
In the case of VDS17 ```ea``` performs this role.
2. ```path``` the path that describes the union selection.
3. ```top_tif``` typeinfo of the top-level UDT which user selected
4. ```spath``` the field names path to the selected member
The union selection path is denotes a concrete member inside a UDT.
For structure types there is no need in the union selection path because the member
offset uniquely denotes the desired member.
For unions, on the other hand, the member offset is not enough because all union
members start at the same offset zero. For them, we remember the ordinal number of the
union member in the path. E.g., for the local types given above, the following holds:
* e1.d1.c0 is denoted by an empty path because it does not have any unions
* e1.d2.u0 is denoted by path consisting of 0: we use the first member of U
* e1.d2.u1 is denoted by path consisting of 1: we use the second member of U
You can retrieve the union selection path using API call ```get_user_union_selection```
or apply it using ```set_user_union_selection```.
"""
+18 -9
View File
@@ -151,10 +151,10 @@ static bool find_libpython_dt_needed_info(
//-------------------------------------------------------------------------
static bool patch_dt_needed(
const char *path,
const qstring &replacement,
const qstring &_replacement,
qstring *errbuf)
{
out_verb("Setting relevant DT_NEEDED of \"%s\" to \"%s\"\n", path, replacement.c_str());
out_verb("Setting relevant DT_NEEDED of \"%s\" to \"%s\"\n", path, _replacement.c_str());
out_ident_inc_t iinc;
linput_t *linput = open_linput(path, /*remote=*/ false);
if ( linput == nullptr )
@@ -214,13 +214,24 @@ static bool patch_dt_needed(
room = qltell(reader.get_linput()) - dt_needed_off - 1;
}
const size_t nbytes = replacement.length();
bytevec_t replacement;
replacement.append(_replacement.c_str(), _replacement.length() + 1);
size_t nbytes = replacement.size();
out_verb("We have room for %" FMT_Z " bytes, and need to write %" FMT_Z "\n",
room, nbytes);
// and patch
if ( room >= nbytes )
{
if ( room > nbytes )
{
out_verb("Expanding replacement with %" FMT_Z " '\\0' bytes, to "
"override possible previous soname that could derail "
"later computation of available room.\n", room - nbytes);
replacement.resize(room, 0);
nbytes = replacement.size();
}
FILE *fp = openM(path);
if ( fp != nullptr )
{
@@ -230,7 +241,7 @@ static bool patch_dt_needed(
if ( !args.dry_run )
{
// we want to write the zero as well!
if ( qfwrite(fp, replacement.c_str(), nbytes+1) == nbytes+1 )
if ( qfwrite(fp, replacement.begin(), nbytes) == nbytes )
{
out_verb("File \"%s\" successfully patched\n", path);
}
@@ -243,7 +254,7 @@ static bool patch_dt_needed(
else
{
out("Would write %" FMT_Z " bytes (\"%s\") to file\n",
nbytes, replacement.c_str());
nbytes, replacement.begin());
}
}
else
@@ -263,7 +274,7 @@ static bool patch_dt_needed(
errbuf->sprnt("Replacement \"%s\" has a length of %" FMT_Z
" bytes, but there is only room for %" FMT_Z ""
" bytes in the file. Cannot proceed.\n",
replacement.c_str(), nbytes, room);
replacement.begin(), nbytes, room);
return false;
}
return true;
@@ -460,9 +471,7 @@ static bool split_debug_expand_libpython3_dtneeded_room(
out_verb("\"%s\" command successful. Restoring the "
"original DT_NEEDED of \"%s\"\n",
cmdline.c_str(), dt_needed);
qstring padded_dt_needed(dt_needed);
padded_dt_needed.resize(SLOT_SIZE, '\0');
if ( !patch_dt_needed(path, padded_dt_needed, errbuf) )
if ( !patch_dt_needed(path, dt_needed, errbuf) )
return false;
}
else
+8 -3
View File
@@ -167,14 +167,19 @@ static bool has_appx_path(qstrvec_t paths)
if ( appx_path.empty() )
{
HKEY hkey;
bool ok = false;
if ( RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Appx", 0, KEY_READ, &hkey) == ERROR_SUCCESS )
{
if ( !read_string(&appx_path, hkey, L"PackageRoot") )
appx_path = "<none>";
ok = read_string(&appx_path, hkey, L"PackageRoot");
RegCloseKey(hkey);
}
if ( !ok )
{
// no Appx support, set to dummy value which doesn't occur in paths
appx_path = "<none>";
}
}
for ( auto path : paths )
for ( const qstring &path : paths )
if ( path.find(appx_path) != qstring::npos )
return true;
+32 -7
View File
@@ -254,6 +254,9 @@ void ida_export idapython_hide_wait_box()
#define LEXEC(...)
#endif
#define AVERAGE_STEPS_COUNT 10
#define MAX_STEPS_COUNT ((AVERAGE_STEPS_COUNT * 2) + 1)
//-------------------------------------------------------------------------
void execution_t::reset_steps()
{
@@ -268,7 +271,7 @@ void execution_t::reset_steps()
// If we never hit the 'trace' callback while in the 'while True' loop
// but always when performing the call to the processor module's 'out/outop'
// then the loop will never stop. That was happening on windows (optimized.)
steps_before_action = 1 + rand() % 20;
steps_before_action = 1 + rand() % (AVERAGE_STEPS_COUNT*2);
}
//-------------------------------------------------------------------------
@@ -300,6 +303,7 @@ void execution_t::stop_tracking()
void execution_t::sync_to_present_time()
{
time_t now = time(NULL);
LEXEC("execution_t (%p)::sync_to_present_time() now=%d\n", this, int(now));
for ( size_t i = 0, n = entries.size(); i < n; ++i )
entries[i].etime = now;
maybe_hide_wait_box();
@@ -342,16 +346,38 @@ void execution_t::reset_current_start_time()
//------------------------------------------------------------------------
int execution_t::on_trace(PyObject *obj, PyFrameObject *frame, int what, PyObject *arg)
{
LEXEC("on_trace() (steps=%d, nentries=%d)\n",
#ifdef TESTABLE_BUILD
// ensure there was no decrement overflow
QASSERT(0, execution.steps_before_action <= MAX_STEPS_COUNT);
#endif
LEXEC("on_trace() (steps_before_action=%u, entries.size()=%d, timeout=%d)\n",
int(execution.steps_before_action),
int(execution.entries.size()));
// we don't want to query for time at every trace event
if ( execution.steps_before_action-- > 0 )
int(execution.entries.size()),
execution.timeout);
if ( execution.timeout < 0 )
{
LEXEC("on_trace()::no timeout currently set (%d).\n", execution.timeout);
return 0;
}
// we don't want to query for time at every trace event
if ( execution.steps_before_action > 0 )
{
--execution.steps_before_action;
return 0;
}
if ( get_active_modal_widget() != NULL )
{
LEXEC("on_trace()::a modal widget is active. Not showing our 'interrupt dialog'.\n");
// in addition, we want to sync the "start time" to now, so that
// the timeout will be relative to that (otherwise, calling
// ask_file() might end up showing the waitdialog for a fraction
// of a second after `_ida_kernwin.ask_file()` returns, but before
// the `ida_kernwin.ask_file()` one does.)
execution.sync_to_present_time();
return 0;
}
@@ -377,8 +403,7 @@ int execution_t::on_trace(PyObject *obj, PyFrameObject *frame, int what, PyObjec
{
if ( PyErr_Occurred() == NULL )
{
LEXEC("on_trace()::INTERRUPTING (setting 'User interrupted' exception) at line %d\n",
PyFrame_GetLineNumber(frame));
LEXEC("on_trace()::INTERRUPTING (setting 'User interrupted' exception)\n");
PyErr_SetString(PyExc_KeyboardInterrupt, "User interrupted");
}
return -1;
Binary file not shown.
+6 -18
View File
@@ -444,8 +444,7 @@ def save_database(idbname, flags=0):
if len(idbname) == 0:
idbname = get_idb_path()
mask = ida_loader.DBFL_KILL | ida_loader.DBFL_COMP | ida_loader.DBFL_BAK
res = ida_loader.save_database_ex(idbname, flags & mask)
return res
return ida_loader.save_database(idbname, flags & mask)
DBFL_BAK = ida_loader.DBFL_BAK # for compatiblity with older versions, eventually delete this
@@ -509,14 +508,14 @@ def delete_all_segments():
ida_name.del_global_name(ea)
func = ida_funcs.get_func(ea)
if func:
ida_funcs.del_func_cmt(func, False)
ida_funcs.del_func_cmt(func, True)
ida_funcs.set_func_cmt(func, "", False)
ida_funcs.set_func_cmt(func, "", True)
ida_funcs.del_func(ea)
ida_bytes.del_hidden_range(ea)
seg = ida_segment.getseg(ea)
if seg:
ida_segment.del_segment_cmt(seg, False)
ida_segment.del_segment_cmt(seg, True)
ida_segment.set_segment_cmt(seg, "", False)
ida_segment.set_segment_cmt(seg, "", True)
ida_segment.del_segm(ea, ida_segment.SEGMOD_KEEP | ida_segment.SEGMOD_SILENT)
ea = ida_bytes.next_head(ea, ida_ida.cvar.inf.max_ea)
@@ -1007,18 +1006,7 @@ def op_offset_high16(ea, n, target):
def MakeVar(ea):
"""
Mark the location as "variable"
@param ea: address to mark
@return: None
@note: All that IDA does is to mark the location as "variable".
Nothing else, no additional analysis is performed.
This function may disappear in the future.
"""
ida_bytes.doVar(ea, 1)
pass
# Every anterior/posterior line has its number.
# Anterior lines have numbers from E_PREV
+18 -12
View File
@@ -358,6 +358,12 @@ static int get_pyidc_cvt_type(PyObject *py_var)
return int(IDAPyIntOrLong_AsLong(attr.o));
}
//-------------------------------------------------------------------------
static inline bool is_pyidc_cvt_type_int64(PyObject *py_var)
{
return get_pyidc_cvt_type(py_var) == PY_ICID_INT64;
}
//-------------------------------------------------------------------------
// Utility function to convert a python object to an IDC object
// and sets a python exception on failure.
@@ -736,15 +742,9 @@ int ida_export pyvar_to_idcvar(
}
//-------------------------------------------------------------------------
inline PyObject *cvt_to_pylong(int32 v)
{
return PyLong_FromLong(v);
}
inline PyObject *cvt_to_pylong(int64 v)
{
return PyLong_FromLongLong(v);
}
// helpers to use with idc_value_t::num (which can be 32-, or 64-bit.)
inline PyObject *cvt_to_pylong(int32 v) { return PyLong_FromLong(v); }
inline PyObject *cvt_to_pylong(int64 v) { return PyLong_FromLongLong(v); }
//-------------------------------------------------------------------------
// Converts an IDC variable to a Python variable
@@ -786,8 +786,7 @@ int ida_export idcvar_to_pyvar(
if ( *py_var != NULL )
{
// Recycling an int64 object?
int t = get_pyidc_cvt_type(py_var->o);
if ( t != PY_ICID_INT64 )
if ( !is_pyidc_cvt_type_int64(py_var->o) )
return CIP_IMMUTABLE; // Cannot recycle immutable object
// Update the attribute
PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, PyLong_FromLongLong(idc_var.i64));
@@ -818,7 +817,14 @@ int ida_export idcvar_to_pyvar(
case VT_LONG:
// Cannot recycle immutable objects
if ( *py_var != NULL )
return CIP_IMMUTABLE;
{
// Recycling an int64 object?
if ( !is_pyidc_cvt_type_int64(py_var->o) )
return CIP_IMMUTABLE;
// Update the attribute
PyObject_SetAttrString(py_var->o, S_PY_IDCCVT_VALUE_ATTR, cvt_to_pylong(idc_var.num));
return CIP_OK;
}
*py_var = newref_t(cvt_to_pylong(idc_var.num));
break;
case VT_FLOAT:
+3 -1
View File
@@ -842,7 +842,9 @@ protected:
{
// identifier
{
ref_t py_id = newref_t(PyObject_GetAttrString(self, "id"));
ref_t py_id;
if ( PyObject_HasAttrString(self, "id") )
py_id = newref_t(PyObject_GetAttrString(self, "id"));
if ( py_id == NULL || !IDAPyStr_Check(py_id.o) )
py_id = newref_t(PyObject_Repr(self));
if ( py_id != NULL && IDAPyStr_Check(py_id.o) )
+9
View File
@@ -126,6 +126,15 @@ class quick_widget_commands_t:
cmd.icon)
attach_dynamic_action_to_popup(widget, popup, desc)
class disabled_script_timeout_t(object):
def __enter__(self):
import _ida_idaapi
self.was_timeout = _ida_idaapi.set_script_timeout(0)
def __exit__(self, type, value, tb):
import _ida_idaapi
_ida_idaapi.set_script_timeout(self.was_timeout)
# ----------------------------------------------------------------------
# bw-compat/deprecated. You shouldn't rely on this in new code
from ida_pro import str2user
+1 -4
View File
@@ -1384,15 +1384,12 @@ except:
def __call_form_callable(call, *args):
assert(len(args))
old = _ida_idaapi.set_script_timeout(0)
try:
with disabled_script_timeout_t():
if sys.version_info.major >= 3 and isinstance(args[0], str):
largs = list(args)
largs[0] = largs[0].encode("UTF-8")
args = tuple(largs)
r = call(*args)
finally:
_ida_idaapi.set_script_timeout(old)
return r
def ask_form(*args):
+2 -3
View File
@@ -205,9 +205,8 @@ class Choose(object):
self.flags |= Choose.CH_MODAL
# Disable the timeout
old = _ida_idaapi.set_script_timeout(0)
n = _ida_kernwin.choose_choose(self)
_ida_idaapi.set_script_timeout(old)
with disabled_script_timeout_t():
n = _ida_kernwin.choose_choose(self)
# Delete the modal chooser instance
self.Close()
+19 -10
View File
@@ -31124,7 +31124,7 @@ class ui_stroff_applicator_t(__builtin__.object)
| __repr__ = _swig_repr(self)
|
| apply(self, *args)
| apply(self, opnum, path) -> bool
| apply(self, opnum, path, top_tif, spath) -> bool
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
@@ -46114,6 +46114,24 @@ detach_action_from_toolbar(*args)
@param name: the action name (C++: const char *)
@return: success
Help on class disabled_script_timeout_t in module ida_kernwin:
class disabled_script_timeout_t(__builtin__.object)
| Methods defined here:
|
| __enter__(self)
|
| __exit__(self, type, value, tb)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
Help on class disasm_line_t in module ida_kernwin:
class disasm_line_t(__builtin__.object)
@@ -73313,15 +73331,6 @@ LoadFile(filepath, pos, ea, size)
Help on function MakeVar in module idc:
MakeVar(ea)
Mark the location as "variable"
@param ea: address to mark
@return: None
@note: All that IDA does is to mark the location as "variable".
Nothing else, no additional analysis is performed.
This function may disappear in the future.
Help on function SaveFile in module idc:
+19 -10
View File
@@ -31074,7 +31074,7 @@ class ui_stroff_applicator_t(builtins.object)
| delete_ui_stroff_applicator_t(self)
|
| apply(self, *args) -> 'bool'
| apply(self, opnum, path) -> bool
| apply(self, opnum, path, top_tif, spath) -> bool
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
@@ -46249,6 +46249,24 @@ detach_action_from_toolbar(*args) -> 'bool'
@param name: the action name (C++: const char *)
@return: success
Help on class disabled_script_timeout_t in module ida_kernwin:
class disabled_script_timeout_t(builtins.object)
| Methods defined here:
|
| __enter__(self)
|
| __exit__(self, type, value, tb)
|
| ----------------------------------------------------------------------
| Data descriptors defined here:
|
| __dict__
| dictionary for instance variables (if defined)
|
| __weakref__
| list of weak references to the object (if defined)
Help on class disasm_line_t in module ida_kernwin:
class disasm_line_t(builtins.object)
@@ -73351,15 +73369,6 @@ LoadFile(filepath, pos, ea, size)
Help on function MakeVar in module idc:
MakeVar(ea)
Mark the location as "variable"
@param ea: address to mark
@return: None
@note: All that IDA does is to mark the location as "variable".
Nothing else, no additional analysis is performed.
This function may disappear in the future.
Help on function SaveFile in module idc:
+6 -6
View File
@@ -42,12 +42,12 @@ extern plugin_t PLUGIN;
%ignore ask_text;
%ignore vwarning;
// Note: don't do that for ask_form(), since that calls back into Python.
%thread ask_addr;
%thread ask_seg;
%thread ask_long;
%thread ask_yn;
%thread ask_buttons;
%thread ask_file;
%modal_dialog_triggering_function(ask_addr);
%modal_dialog_triggering_function(ask_seg);
%modal_dialog_triggering_function(ask_long);
%modal_dialog_triggering_function(ask_yn);
%modal_dialog_triggering_function(ask_buttons);
%modal_dialog_triggering_function(ask_file);
%ignore simpleline_t::simpleline_t(const qstring &);
+15
View File
@@ -1681,6 +1681,21 @@ SWIGINTERN bool __chkreqidb()
%}
%define %modal_dialog_triggering_function(NAME)
%thread NAME;
%pythonprepend NAME
%{
import ida_kernwin
# kludge: we can't use %feature("shadow") for top-level
# functions (see https://github.com/swig/swig/issues/980)
# Thus we'll %pythonprepend some code, and return from it,
# making the original code unreachable. Not pretty, but I
# don't have anything better at the moment.
with ida_kernwin.disabled_script_timeout_t():
return _ida_kernwin.NAME(*args)
%}
%enddef
%include <constraints.i>
// If the module is 'pro', don't import pro.h, or the %include