diff --git a/idapyswitch.cpp b/idapyswitch.cpp index e428390..d1de4a7 100644 --- a/idapyswitch.cpp +++ b/idapyswitch.cpp @@ -16,6 +16,7 @@ //lint -esym(1788, iinc) is referenced only by its constructor or destructor #include +#include #include #include #include diff --git a/idapyswitch_win.cpp b/idapyswitch_win.cpp index 22fe902..ebb5804 100644 --- a/idapyswitch_win.cpp +++ b/idapyswitch_win.cpp @@ -97,6 +97,94 @@ static bool extract_version_from_path( return true; } + +#pragma comment(lib, "version.lib") +//------------------------------------------------------------------------- +DWORD GetFileVersionNumber(const char *filename, DWORD *pdwMSVer, DWORD *pdwLSVer) +{ + DWORD dwResult = NOERROR; + unsigned uiSize; + DWORD dwVerInfoSize; + DWORD dwHandle; + PBYTE prgbVersionInfo = NULL; + VS_FIXEDFILEINFO *lpVSFixedFileInfo = NULL; + + DWORD dwMSVer = 0xffffffff; + DWORD dwLSVer = 0xffffffff; + + qwstring wfilename; + if ( !utf8_utf16(&wfilename, filename) ) + { + dwResult = ERROR_INVALID_PARAMETER; + goto Finish; + } + + dwVerInfoSize = GetFileVersionInfoSizeW(wfilename.c_str(), &dwHandle); + if ( dwVerInfoSize != 0 ) + { + prgbVersionInfo = (PBYTE)qalloc(dwVerInfoSize); + if ( prgbVersionInfo == NULL ) + { + dwResult = ERROR_NOT_ENOUGH_MEMORY; + goto Finish; + } + + // Read version stamping info + if ( GetFileVersionInfoW(wfilename.c_str(), dwHandle, dwVerInfoSize, prgbVersionInfo) ) + { + // get the value for VS_FIXEDFILEINFO + if ( VerQueryValueW(prgbVersionInfo, L"\\", (LPVOID*)&lpVSFixedFileInfo, &uiSize) && (uiSize != 0) ) + { + dwMSVer = lpVSFixedFileInfo->dwFileVersionMS; + dwLSVer = lpVSFixedFileInfo->dwFileVersionLS; + } + } + else + { + dwResult = GetLastError(); + goto Finish; + } + } + else + { + dwResult = GetLastError(); + } + + out_verb("%s is version %d.%d.%d.%d\n", filename, HIWORD(dwMSVer), LOWORD(dwMSVer), HIWORD(dwLSVer), LOWORD(dwLSVer)); + +Finish: + if ( prgbVersionInfo != NULL ) + qfree(prgbVersionInfo); + if ( pdwMSVer != NULL ) + *pdwMSVer = dwMSVer; + if ( pdwLSVer != NULL ) + *pdwLSVer = dwLSVer; + + return dwResult; +} +//------------------------------------------------------------------------- +static bool extract_version_from_dll( + pylib_version_t *out, + const char *fname) +{ + DWORD dwMSVer, dwLSVer; + DWORD res = GetFileVersionNumber(fname, &dwMSVer, &dwLSVer); + if ( res == NOERROR ) + { + out->raw.sprnt("%d.%d.%d.%d", HIWORD(dwMSVer), LOWORD(dwMSVer), HIWORD(dwLSVer), LOWORD(dwLSVer)); + // Python DLL versions look like 3.7.4150.1013 -> 3.7.4 + out->major = HIWORD(dwMSVer); + out->minor = LOWORD(dwMSVer); + out->revision = HIWORD(dwLSVer) / 1000; + return true; + } + else + { + out_verb("error getting version of \"%s\": %s\n", fname, winerr(res)); + return false; + } +} + //------------------------------------------------------------------------- static bool is_python3Y_dll_file_name(const char *fname) { @@ -138,6 +226,9 @@ static bool probe_python_install_dir_from_dll_path( { char dll_path[QMAXPATH]; qmakepath(dll_path, sizeof(dll_path), dir, fb.ff_name, nullptr); + pylib_version_t tmp; + if ( extract_version_from_dll(&tmp, path) ) + *out_version = tmp; out_verb("Found: \"%s\" (version: %s)\n", dll_path, out_version->str(&verbuf)); out_paths->push_back(dll_path); @@ -392,10 +483,24 @@ void pyver_tool_t::do_find_python_libs(pylib_entries_t *result) const out("IDA previously used: \"%s\" (guessed version: %s). " "Making this the preferred version.\n", existing.c_str(), version.str(&verbuf)); - pylib_entry_t e(version); - e.paths.swap(paths); - e.preferred = true; - result->entries.push_back(e); + // do we have it in the list? + bool found = false; + for ( pylib_entry_t &e : result->entries ) + { + if ( e.paths.has(existing) ) + { + found = true; + e.preferred = true; + } + } + if ( !found ) + { + // add a new one + pylib_entry_t e(version); + e.paths.swap(paths); + e.preferred = true; + result->entries.push_back(e); + } } else { diff --git a/idapython.cpp b/idapython.cpp index 11f5cc5..f1f3507 100644 --- a/idapython.cpp +++ b/idapython.cpp @@ -840,7 +840,7 @@ static bool return_python_result( // We use the Python function to execute the script because it knows how to deal with // module reloading. static bool IDAPython_ExecFile( - const char *FileName, + const char *path, PyObject *globals, qstring *errbuf, const char *idaapi_script = S_IDAAPI_EXECSCRIPT, @@ -856,12 +856,22 @@ static bool IDAPython_ExecFile( } char script[MAXSTR]; - qstrncpy(script, FileName, sizeof(script)); + qstrncpy(script, path, sizeof(script)); strrpl(script, '\\', '/'); + newref_t py_script(IDAPyStr_FromUTF8(script)); if ( globals == NULL ) globals = get_module_globals(); - newref_t py_script(IDAPyStr_FromUTF8(script)); + if ( globals != get_module_globals() ) + { + // Executions that take place in the scope of their own module, + // should have the '__file__' attribute properly set (so that + // it doesn't just get temporarily set and then removed by + // `ida_idaapi.IDAPython_ExecScript`. + newref_t py_file_key(IDAPyStr_FromUTF8(S_FILE)); + if ( !PyDict_Contains(globals, py_file_key.o) ) + PyDict_SetItem(globals, py_file_key.o, py_script.o); + } borref_t py_false(Py_False); newref_t py_ret(PyObject_CallFunctionObjArgs( py_execscript.o, diff --git a/out_of_tree/parsed_notifications.zip b/out_of_tree/parsed_notifications.zip index 0779c5a..71480dc 100644 Binary files a/out_of_tree/parsed_notifications.zip and b/out_of_tree/parsed_notifications.zip differ diff --git a/python/idc.py b/python/idc.py index a9971bd..9dc0989 100644 --- a/python/idc.py +++ b/python/idc.py @@ -5106,7 +5106,7 @@ def SetType(ea, newtype): @return: 1-ok, 0-failed. """ - if newtype is not '': + if newtype != '': pt = parse_decl(newtype, PT_SIL) if pt is None: # parsing failed diff --git a/pywraps.hpp b/pywraps.hpp index 074f485..2fa7498 100644 --- a/pywraps.hpp +++ b/pywraps.hpp @@ -130,6 +130,7 @@ static const char S_M_TITLE[] = "_title"; static const char S_CLINK_NAME[] = "__clink__"; static const char S_ON_VIEW_MOUSE_MOVED[] = "OnViewMouseMoved"; static const char S_MAIN[] = "__main__"; +static const char S_FILE[] = "__file__"; #define VALID_CAPSULE_NAME "$valid$" #define INVALID_CAPSULE_NAME "$INvalid$"