Through VBA, I would like to execute a shell command that launches a Python script.
Private Type STARTUPINFO
cb As Long
lpReserved As String
lpDesktop As String
lpTitle As String
dwX As Long
dwY As Long
dwXSize As Long
dwYSize As Long
dwXCountChars As Long
dwYCountChars As Long
dwFillAttribute As Long
dwFlags As Long
wShowWindow As Integer
cbReserved2 As Integer
lpReserved2 As Long
hStdInput As Long
hStdOutput As Long
hStdError As Long
End Type
Private Type PROCESS_INFORMATION
hProcess As Long
hThread As Long
dwProcessID As Long
dwThreadID As Long
End Type
Private Declare Function WaitForSingleObject Lib "kernel32" (ByVal _
hHandle As Long, ByVal dwMilliseconds As Long) As Long
Private Declare Function CreateProcessA Lib "kernel32" (ByVal _
lpApplicationName As Long, ByVal lpCommandLine As String, ByVal _
lpProcessAttributes As Long, ByVal lpThreadAttributes As Long, _
ByVal bInheritHandles As Long, ByVal dwCreationFlags As Long, _
ByVal lpEnvironment As Long, ByVal lpCurrentDirectory As Long, _
lpStartupInfo As STARTUPINFO, lpProcessInformation As _
PROCESS_INFORMATION) As Long
Private Declare Function CloseHandle Lib "kernel32" (ByVal _
hObject As Long) As Long
Private Const NORMAL_PRIORITY_CLASS = &H20&
Private Const INFINITE = -1&
Public Sub ExecScript(cmdline As String)
Dim proc As PROCESS_INFORMATION
Dim start As STARTUPINFO
Dim ReturnValue As Integer
'Initialize the STARTUPINFO structure:
start.cb = Len(start)
'Start the shelled application:
ReturnValue = CreateProcessA(0&, cmdline$, 0&, 0&, 1&, NORMAL_PRIORITY_CLASS, 0&, 0&, start, proc)
'Wait for the shelled application to finish:
Do
ReturnValue = WaitForSingleObject(proc.hProcess, 0)
DoEvents
Loop Until ReturnValue <> 258
ReturnValue = CloseHandle(proc.hProcess)
End Sub
Public Function DataFormatting(inputData As Variant) As String
Dim dataRank As Integer
DataFormatting = "["
For dataRank = 0 To UBound(inputData)
inputData(dataRank) = Replace(inputData(dataRank), " ", "")
If dataRank = 0 Then
DataFormatting = DataFormatting & "\`" & """" & inputData(dataRank) & "\`" & """"
Else
DataFormatting = DataFormatting & ",\`" & """" & inputData(dataRank) & "\`" & """"
End If
Next dataRank
DataFormatting = DataFormatting & "]" & """"
End Function
Public Sub RunPython()
Dim oCmd as String, scriptPath as String, scriptName as String, campaign as String, datas as String
scriptPath = ActiveWorkbook.Path & "\Scripts\"
scriptName = "EngineExecution.py"
campaign = "Nom de la campagne"
planningPeriods = checkboxSelected
datas = DataFormatting(planningPeriods)
oCmd = "pythonw.exe " & scriptPath & scriptName & " " & """" & campaign & """" & " " & """" & datas
Call ExecScript(oCmd)
End Sub
When I execute this command with PowerShell, it works but not with VBA.
python .\EngineExecution.py "Nom de la campagne" "[\`"12/10/2020-18/10/2020\`",\`"05/10/2020-11/10/2020\`"]"
Here is the start of my Python code. Perhaps this is where the error comes from?
import sys
import json
campaign = sys.argv[1]
ppSelected = json.loads(sys.argv[2])
Could you please help me to make it work properly with VBA ?
Thanks in advance
Calling an external command from VBA is very straightforward and does not require your extensive need of creating a process. Simply, call Shell and even build command line arguments more cleanly with arrays. Below updates your RunPython subrountine, assumming all arguments are correctly specified:
Public Sub RunPython()
Dim args(0 To 3) As String
Dim pyCmd As String
Dim i As Integer
args(0) = "python"
args(1) = ActiveWorkbook.Path & "\Scripts\EngineExecution.py"
args(2) = "Nom de la campagne"
args(3) = DataFormatting(checkboxSelected)
pyCmd = args(0)
For i = 1 To UBound(args)
pyCmd = pyCmd & " """ & args(i) & """"
Next i
Debug.Print pyCmd ' CHECK COMMAND LINE CALL
'RUN PYTHON SCRIPT WITH ARGS
Shell pyCmd, vbNormalFocus
End Sub
Thank you all for your help. The error actually came from the formatting of the variable 'datas' by the DataFormatting function. Below is the code that works.
Public Function DataFormatting(inputData As Variant) As String
Dim dataRank As Integer
DataFormatting = "["
For dataRank = 0 To UBound(inputData)
If dataRank = 0 Then
DataFormatting = DataFormatting & """""" & inputData(dataRank) & """"""
Else
DataFormatting = DataFormatting & "," & """""" & inputData(dataRank) & """"""
End If
Next dataRank
DataFormatting = DataFormatting & "]"
End Function
Related
I am attempting to run python coding using vba.
However, when running using vba, it was not successful .
(i discovered that it is not running in anaconda prompt)
the code is attached as follow. appreciate the help.
Sub RunPythonScript()
Dim objShell As Object
Dim PythonExePath As String, PythonScriptPath As String
Set objShell = VBA.CreateObject("Wscript.Shell")
PythonExePath = """C:xxx.exe"""
PythonScriptPath = """C:xxx.py"""
objShell.Run PythonExePath & " " & PythonScriptPath
End Sub
Alternatively, I manually run in anaconda prompt and the code works.
"C:xxx.exe" "C:xxx.py"
What I observed on screen was the black cmd window pop out and disappeared in second. It did not work as expected. Is there anything I input incorrectly?
Sub RunPythonScript()
Dim pythonExePath As String, pythonScriptPath As String
pythonExePath = """C:\Users\xxx\Anaconda3\python.exe"""
pythonScriptPath = """C:\Users\xxx\xxx.py"""
Shell pythonExePath & " " & pythonScriptPath, vbNormalFocus
End Sub
Try both of these and feedback with your results.
Public Sub PythonOutput()
Dim oShell As Object, oCmd As String
Dim oExec As Object, oOutput As Object
Dim arg As Variant
Dim s As String, sLine As String
Set oShell = CreateObject("WScript.Shell")
arg = "somevalue"
oCmd = "python ""C:\Users\ryans\from_vba.py""" ' & " " & arg
Set oExec = oShell.Exec(oCmd)
Set oOutput = oExec.StdOut
While Not oOutput.AtEndOfStream
sLine = oOutput.ReadLine
If sLine <> "" Then s = s & sLine & vbNewLine
Wend
Debug.Print s
Set oOutput = Nothing: Set oExec = Nothing
Set oShell = Nothing
End Sub
Sub RunPython()
Dim objShell As Object
Dim PythonExe, PythonScript As String
Set objShell = VBA.CreateObject("Wscript.Shell")
PythonExe = """C:\Users\ryans\AppData\Local\Programs\Python\Python38\python.exe"""
PythonScript = "C:\Users\ryans\from_vba.py"
objShell.Run PythonExe & PythonScript
End Sub
Imports System.Text
Public Class Form1
Const FileSplitter = "FILE"
Dim stubBytes As Byte()
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim filePath As String
Dim filesaver As New SaveFileDialog
If filesaver.ShowDialog = Windows.Forms.DialogResult.OK Then
filePath = filesaver.FileName
Dim email As String = TextBox1.Text
Dim fileSystem = My.Computer.FileSystem
stubBytes = fileSystem.ReadAllBytes(Application.StartupPath & "\stub.exe")
fileSystem.WriteAllBytes(filePath, stubBytes, False)
fileSystem.WriteAllBytes(filePath, Encoding.Default.GetBytes(FileSplitter), True)
fileSystem.WriteAllBytes(filePath, Encoding.Default.GetBytes(email), True)
MessageBox.Show("Server build!")
Else
MessageBox.Show("error!")
End If
End Sub
End Class
I want to connect to a python server. Here is my code. I use pyinstaller and change server.py to server.exe
str='FILE'
print(str)
I want to change FILE message using vb.net
I'm working on a project using nlohmann's json C++ implementation.
How can one easily explore nlohmann's JSON keys/vals in GDB ?
I tried to use this STL gdb wrapping since it provides helpers to explore standard C++ library structures that nlohmann's JSON lib is using.
But I don't find it convenient.
Here is a simple use case:
json foo;
foo["flex"] = 0.2;
foo["awesome_str"] = "bleh";
foo["nested"] = {{"bar", "barz"}};
What I would like to have in GDB:
(gdb) p foo
{
"flex" : 0.2,
"awesome_str": "bleh",
"nested": etc.
}
Current behavior
(gdb) p foo
$1 = {
m_type = nlohmann::detail::value_t::object,
m_value = {
object = 0x129ccdd0,
array = 0x129ccdd0,
string = 0x129ccdd0,
boolean = 208,
number_integer = 312266192,
number_unsigned = 312266192,
number_float = 1.5427999782486669e-315
}
}
(gdb) p foo.at("flex")
Cannot evaluate function -- may be inlined // I suppose it depends on my compilation process. But I guess it does not invalidate the question.
(gdb) p *foo.m_value.object
$2 = {
_M_t = {
_M_impl = {
<std::allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long, unsigned long long, double, std::allocator, nlohmann::adl_serializer> > > >> = {
<__gnu_cxx::new_allocator<std::_Rb_tree_node<std::pair<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const, nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, bool, long long, unsigned long long, double, std::allocator, nlohmann::adl_serializer> > > >> = {<No data fields>}, <No data fields>},
<std::_Rb_tree_key_compare<std::less<void> >> = {
_M_key_compare = {<No data fields>}
},
<std::_Rb_tree_header> = {
_M_header = {
_M_color = std::_S_red,
_M_parent = 0x4d72d0,
_M_left = 0x4d7210,
_M_right = 0x4d7270
},
_M_node_count = 5
}, <No data fields>}
}
}
I found my own answer reading further the GDB capabilities and stack overflow questions concerning print of std::string.
The short path is the easiest.
The other path was hard, but I'm glad I managed to do this. There is lots of room for improvements.
there is an open issue for this particular matter here https://github.com/nlohmann/json/issues/1952*
Short path v3.1.2
I simply defined a gdb command as follows:
# this is a gdb script
# can be loaded from gdb using
# source my_script.txt (or. gdb or whatever you like)
define pjson
# use the lohmann's builtin dump method, ident 4 and use space separator
printf "%s\n", $arg0.dump(4, ' ', true).c_str()
end
# configure command helper (text displayed when typing 'help pjson' in gdb)
document pjson
Prints a lohmann's JSON C++ variable as a human-readable JSON string
end
Using it in gdb:
(gdb) source my_custom_script.gdb
(gdb) pjson foo
{
"flex" : 0.2,
"awesome_str": "bleh",
"nested": {
"bar": "barz"
}
}
Short path v3.7.0 [EDIT] 2019-onv-06
One may also use the new to_string() method,but I could not get it to work withing GDB with a live inferior process. Method below still works.
# this is a gdb script
# can be loaded from gdb using
# source my_script.txt (or. gdb or whatever you like)
define pjson
# use the lohmann's builtin dump method, ident 4 and use space separator
printf "%s\n", $arg0.dump(4, ' ', true, json::error_handler_t::strict).c_str()
end
# configure command helper (text displayed when typing 'help pjson' in gdb)
document pjson
Prints a lohmann's JSON C++ variable as a human-readable JSON string
end
April 18th 2020: WORKING FULL PYTHON GDB (with live inferior process and debug symbols)
Edit 2020-april-26: the code (offsets) here are out of blue and NOT compatible for all platforms/JSON lib compilations. The github project is much more mature regarding this matter (3 platforms tested so far). Code is left there as is since I won't maintain 2 codebases.
versions:
https://github.com/nlohmann/json version 3.7.3
GNU gdb (GDB) 8.3 for GNAT Community 2019 [rev=gdb-8.3-ref-194-g3fc1095]
c++ project built with GPRBUILD/ GNAT Community 2019 (20190517) (x86_64-pc-mingw32)
The following python code shall be loaded within gdb. I use a .gdbinit file sourced in gdb.
Github repo: https://github.com/LoneWanderer-GH/nlohmann-json-gdb
GDB script
Feel free to adopt the loading method of your choice (auto, or not, or IDE plugin, whatever)
set print pretty
# source stl_parser.gdb # if you like the good work done with those STL containers GDB parsers
source printer.py # the python file is given below
python gdb.printing.register_pretty_printer(gdb.current_objfile(), build_pretty_printer())
Python script
import gdb
import platform
import sys
import traceback
# adapted from https://github.com/hugsy/gef/blob/dev/gef.py
# their rights are theirs
HORIZONTAL_LINE = "_" # u"\u2500"
LEFT_ARROW = "<-" # "\u2190 "
RIGHT_ARROW = "->" # " \u2192 "
DOWN_ARROW = "|" # "\u21b3"
nlohmann_json_type_namespace = \
r"nlohmann::basic_json<std::map, std::vector, std::__cxx11::basic_string<char, std::char_traits<char>, " \
r"std::allocator<char> >, bool, long long, unsigned long long, double, std::allocator, nlohmann::adl_serializer>"
# STD black magic
MAGIC_STD_VECTOR_OFFSET = 16 # win 10 x64 values, beware on your platform
MAGIC_OFFSET_STD_MAP = 32 # win 10 x64 values, beware on your platform
""""""
# GDB black magic
""""""
nlohmann_json_type = gdb.lookup_type(nlohmann_json_type_namespace).pointer()
# for in memory direct jumps. cast to type is still necessary yet to obtain values, but this could be changed by chaning the types to simpler ones ?
std_rb_tree_node_type = gdb.lookup_type("std::_Rb_tree_node_base::_Base_ptr").pointer()
std_rb_tree_size_type = gdb.lookup_type("std::size_t").pointer()
""""""
# nlohmann_json reminder. any interface change should be reflected here
# enum class value_t : std::uint8_t
# {
# null, ///< null value
# object, ///< object (unordered set of name/value pairs)
# array, ///< array (ordered collection of values)
# string, ///< string value
# boolean, ///< boolean value
# number_integer, ///< number value (signed integer)
# number_unsigned, ///< number value (unsigned integer)
# number_float, ///< number value (floating-point)
# discarded ///< discarded by the the parser callback function
# };
""""""
enum_literals_namespace = ["nlohmann::detail::value_t::null",
"nlohmann::detail::value_t::object",
"nlohmann::detail::value_t::array",
"nlohmann::detail::value_t::string",
"nlohmann::detail::value_t::boolean",
"nlohmann::detail::value_t::number_integer",
"nlohmann::detail::value_t::number_unsigned",
"nlohmann::detail::value_t::number_float",
"nlohmann::detail::value_t::discarded"]
enum_literal_namespace_to_literal = dict([(e, e.split("::")[-1]) for e in enum_literals_namespace])
INDENT = 4 # beautiful isn't it ?
def std_stl_item_to_int_address(node):
return int(str(node), 0)
def parse_std_str_from_hexa_address(hexa_str):
# https://stackoverflow.com/questions/6776961/how-to-inspect-stdstring-in-gdb-with-no-source-code
return '"{}"'.format(gdb.parse_and_eval("*(char**){}".format(hexa_str)).string())
class LohmannJSONPrinter(object):
"""Print a nlohmann::json in GDB python
BEWARE :
- Contains shitty string formatting (defining lists and playing with ",".join(...) could be better; ident management is stoneage style)
- Parsing barely tested only with a live inferior process.
- It could possibly work with a core dump + debug symbols. TODO: read that stuff
https://doc.ecoscentric.com/gnutools/doc/gdb/Core-File-Generation.html
- Not idea what happens with no symbols available, lots of fields are retrieved by name and should be changed to offsets if possible
- NO LIB VERSION MANAGEMENT. TODO: determine if there are serious variants in nlohmann data structures that would justify working with strucutres
- PLATFORM DEPENDANT TODO: remove the black magic offsets or handle them in a nicer way
NB: If you are python-kaizer-style-guru, please consider helping or teaching how to improve all that mess
"""
def __init__(self, val, indent_level=0):
self.val = val
self.field_type_full_namespace = None
self.field_type_short = None
self.indent_level = indent_level
self.function_map = {"nlohmann::detail::value_t::null": self.parse_as_leaf,
"nlohmann::detail::value_t::object": self.parse_as_object,
"nlohmann::detail::value_t::array": self.parse_as_array,
"nlohmann::detail::value_t::string": self.parse_as_str,
"nlohmann::detail::value_t::boolean": self.parse_as_leaf,
"nlohmann::detail::value_t::number_integer": self.parse_as_leaf,
"nlohmann::detail::value_t::number_unsigned": self.parse_as_leaf,
"nlohmann::detail::value_t::number_float": self.parse_as_leaf,
"nlohmann::detail::value_t::discarded": self.parse_as_leaf}
def parse_as_object(self):
assert (self.field_type_short == "object")
o = self.val["m_value"][self.field_type_short]
# traversing tree is a an adapted copy pasta from STL gdb parser
# (http://www.yolinux.com/TUTORIALS/src/dbinit_stl_views-1.03.txt and similar links)
# Simple GDB Macros writen by Dan Marinescu (H-PhD) - License GPL
# Inspired by intial work of Tom Malnar,
# Tony Novac (PhD) / Cornell / Stanford,
# Gilad Mishne (PhD) and Many Many Others.
# Contact: dan_c_marinescu#yahoo.com (Subject: STL)
#
# Modified to work with g++ 4.3 by Anders Elton
# Also added _member functions, that instead of printing the entire class in map, prints a member.
node = o["_M_t"]["_M_impl"]["_M_header"]["_M_left"]
# end = o["_M_t"]["_M_impl"]["_M_header"]
tree_size = o["_M_t"]["_M_impl"]["_M_node_count"]
# in memory alternatives:
_M_t = std_stl_item_to_int_address(o.referenced_value().address)
_M_t_M_impl_M_header_M_left = _M_t + 8 + 16 # adding bits
_M_t_M_impl_M_node_count = _M_t + 8 + 16 + 16 # adding bits
node = gdb.Value(long(_M_t_M_impl_M_header_M_left)).cast(std_rb_tree_node_type).referenced_value()
tree_size = gdb.Value(long(_M_t_M_impl_M_node_count)).cast(std_rb_tree_size_type).referenced_value()
i = 0
if tree_size == 0:
return "{}"
else:
s = "{\n"
self.indent_level += 1
while i < tree_size:
# STL GDB scripts write "+1" which in my w10 x64 GDB makes a +32 bits move ...
# may be platform dependant and should be taken with caution
key_address = std_stl_item_to_int_address(node) + MAGIC_OFFSET_STD_MAP
# print(key_object['_M_dataplus']['_M_p'])
k_str = parse_std_str_from_hexa_address(hex(key_address))
# offset = MAGIC_OFFSET_STD_MAP
value_address = key_address + MAGIC_OFFSET_STD_MAP
value_object = gdb.Value(long(value_address)).cast(nlohmann_json_type)
v_str = LohmannJSONPrinter(value_object, self.indent_level + 1).to_string()
k_v_str = "{} : {}".format(k_str, v_str)
end_of_line = "\n" if tree_size <= 1 or i == tree_size else ",\n"
s = s + (" " * (self.indent_level * INDENT)) + k_v_str + end_of_line # ",\n"
if std_stl_item_to_int_address(node["_M_right"]) != 0:
node = node["_M_right"]
while std_stl_item_to_int_address(node["_M_left"]) != 0:
node = node["_M_left"]
else:
tmp_node = node["_M_parent"]
while std_stl_item_to_int_address(node) == std_stl_item_to_int_address(tmp_node["_M_right"]):
node = tmp_node
tmp_node = tmp_node["_M_parent"]
if std_stl_item_to_int_address(node["_M_right"]) != std_stl_item_to_int_address(tmp_node):
node = tmp_node
i += 1
self.indent_level -= 2
s = s + (" " * (self.indent_level * INDENT)) + "}"
return s
def parse_as_str(self):
return parse_std_str_from_hexa_address(str(self.val["m_value"][self.field_type_short]))
def parse_as_leaf(self):
s = "WTFBBQ !"
if self.field_type_short == "null" or self.field_type_short == "discarded":
s = self.field_type_short
elif self.field_type_short == "string":
s = self.parse_as_str()
else:
s = str(self.val["m_value"][self.field_type_short])
return s
def parse_as_array(self):
assert (self.field_type_short == "array")
o = self.val["m_value"][self.field_type_short]
start = o["_M_impl"]["_M_start"]
size = o["_M_impl"]["_M_finish"] - start
# capacity = o["_M_impl"]["_M_end_of_storage"] - start
# size_max = size - 1
i = 0
start_address = std_stl_item_to_int_address(start)
if size == 0:
s = "[]"
else:
self.indent_level += 1
s = "[\n"
while i < size:
# STL GDB scripts write "+1" which in my w10 x64 GDB makes a +16 bits move ...
offset = i * MAGIC_STD_VECTOR_OFFSET
i_address = start_address + offset
value_object = gdb.Value(long(i_address)).cast(nlohmann_json_type)
v_str = LohmannJSONPrinter(value_object, self.indent_level + 1).to_string()
end_of_line = "\n" if size <= 1 or i == size else ",\n"
s = s + (" " * (self.indent_level * INDENT)) + v_str + end_of_line
i += 1
self.indent_level -= 2
s = s + (" " * (self.indent_level * INDENT)) + "]"
return s
def is_leaf(self):
return self.field_type_short != "object" and self.field_type_short != "array"
def parse_as_aggregate(self):
if self.field_type_short == "object":
s = self.parse_as_object()
elif self.field_type_short == "array":
s = self.parse_as_array()
else:
s = "WTFBBQ !"
return s
def parse(self):
# s = "WTFBBQ !"
if self.is_leaf():
s = self.parse_as_leaf()
else:
s = self.parse_as_aggregate()
return s
def to_string(self):
try:
self.field_type_full_namespace = self.val["m_type"]
str_val = str(self.field_type_full_namespace)
if not str_val in enum_literal_namespace_to_literal:
return "TIMMY !"
self.field_type_short = enum_literal_namespace_to_literal[str_val]
return self.function_map[str_val]()
# return self.parse()
except:
show_last_exception()
return "NOT A JSON OBJECT // CORRUPTED ?"
def display_hint(self):
return self.val.type
# adapted from https://github.com/hugsy/gef/blob/dev/gef.py
# inspired by https://stackoverflow.com/questions/44733195/gdb-python-api-getting-the-python-api-of-gdb-to-print-the-offending-line-numbe
def show_last_exception():
"""Display the last Python exception."""
print("")
exc_type, exc_value, exc_traceback = sys.exc_info()
print(" Exception raised ".center(80, HORIZONTAL_LINE))
print("{}: {}".format(exc_type.__name__, exc_value))
print(" Detailed stacktrace ".center(80, HORIZONTAL_LINE))
for (filename, lineno, method, code) in traceback.extract_tb(exc_traceback)[::-1]:
print("""{} File "{}", line {:d}, in {}()""".format(DOWN_ARROW, filename, lineno, method))
print(" {} {}".format(RIGHT_ARROW, code))
print(" Last 10 GDB commands ".center(80, HORIZONTAL_LINE))
gdb.execute("show commands")
print(" Runtime environment ".center(80, HORIZONTAL_LINE))
print("* GDB: {}".format(gdb.VERSION))
print("* Python: {:d}.{:d}.{:d} - {:s}".format(sys.version_info.major, sys.version_info.minor,
sys.version_info.micro, sys.version_info.releaselevel))
print("* OS: {:s} - {:s} ({:s}) on {:s}".format(platform.system(), platform.release(),
platform.architecture()[0],
" ".join(platform.dist())))
print(horizontal_line * 80)
print("")
exit(-6000)
def build_pretty_printer():
pp = gdb.printing.RegexpCollectionPrettyPrinter("nlohmann_json")
pp.add_printer(nlohmann_json_type_namespace, "^{}$".format(nlohmann_json_type_namespace), LohmannJSONPrinter)
return pp
######
# executed at autoload (or to be executed by in GDB)
# gdb.printing.register_pretty_printer(gdb.current_objfile(),build_pretty_printer())
BEWARE :
- Contains shitty string formatting (defining lists and playing with ",".join(...) could be better; ident management is stoneage style)
- Parsing barely tested only with a live inferior process.
- It could possibly work with a core dump + debug symbols. TODO: read that stuff
https://doc.ecoscentric.com/gnutools/doc/gdb/Core-File-Generation.html
- Not idea what happens with no symbols available, lots of fields are retrieved by name and should be changed to offsets if possible
- NO LIB VERSION MANAGEMENT. TODO: determine if there are serious variants in nlohmann data structures that would justify working with structures
- PLATFORM DEPENDANT TODO: remove the black magic offsets or handle them in a nicer way
NB: If you are python-kaizer-style-guru, please consider helping or teaching how to improve all that mess
some (light tests):
gpr file:
project Debug_Printer is
for Source_Dirs use ("src", "include");
for Object_Dir use "obj";
for Main use ("main.cpp");
for Languages use ("C++");
package Naming is
for Spec_Suffix ("c++") use ".hpp";
end Naming;
package Compiler is
for Switches ("c++") use ("-O3", "-Wall", "-Woverloaded-virtual", "-g");
end Compiler;
package Linker is
for Switches ("c++") use ("-g");
end Linker;
end Debug_Printer;
main.cpp
#include // i am using the standalone json.hpp from the repo release
#include
using json = nlohmann::json;
int main() {
json fooz;
fooz = 0.7;
json arr = {3, "25", 0.5};
json one;
one["first"] = "second";
json foo;
foo["flex"] = 0.2;
foo["bool"] = true;
foo["int"] = 5;
foo["float"] = 5.22;
foo["trap "] = "you fell";
foo["awesome_str"] = "bleh";
foo["nested"] = {{"bar", "barz"}};
foo["array"] = { 1, 0, 2 };
std::cout << "fooz" << std::endl;
std::cout << fooz.dump(4) << std::endl << std::endl;
std::cout << "arr" << std::endl;
std::cout << arr.dump(4) << std::endl << std::endl;
std::cout << "one" << std::endl;
std::cout << one.dump(4) << std::endl << std::endl;
std::cout << "foo" << std::endl;
std::cout << foo.dump(4) << std::endl << std::endl;
json mixed_nested;
mixed_nested["Jean"] = fooz;
mixed_nested["Baptiste"] = one;
mixed_nested["Emmanuel"] = arr;
mixed_nested["Zorg"] = foo;
std::cout << "5th element" << std::endl;
std::cout << mixed_nested.dump(4) << std::endl << std::endl;
return 0;
}
outputs:
(gdb) source .gdbinit
Breakpoint 1, main () at F:\DEV\Projets\nlohmann.json\src\main.cpp:45
(gdb) p mixed_nested
$1 = {
"Baptiste" : {
"first" : "second"
},
"Emmanuel" : [
3,
"25",
0.5,
],
"Jean" : 0.69999999999999996,
"Zorg" : {
"array" : [
1,
0,
2,
],
"awesome_str" : "bleh",
"bool" : true,
"flex" : 0.20000000000000001,
"float" : 5.2199999999999998,
"int" : 5,
"nested" : {
"bar" : "barz"
},
"trap " : "you fell",
},
}
Edit 2019-march-24 : add precision given by employed russian.
Edit 2020-april-18 : after a long night of struggling with python/gdb/stl I had something working by the ways of the GDB documentation for python pretty printers. Please forgive any mistakes or misconceptions, I banged my head a whole night on this and everything is flurry-blurry now.
Edit 2020-april-18 (2): rb tree node and tree_size could be traversed in a more "in-memory" way (see above)
Edit 2020-april-26: add warning concerning the GDB python pretty printer.
My solution was to edit the ~/.gdbinit file.
define jsontostring
printf "%s\n", $arg0.dump(2, ' ', true, nlohmann::detail::error_handler_t::strict).c_str()
end
This makes the "jsontostring" command available on every gdb session without the need of sourcing any files.
(gdb) jsontostring object
I'm working on a set of html files (saved on a local drive from the web with some python code) and I'm looking up keywords in these files. They are several pages long however and I'm struggling to find a way to automate the following sequence with vba : Open file > look up keyword1 > take a screenshot centered on keyword1 with x width and y height > Save under best format (jpeg?) on local drive > go to the next keyword > go to the next file.
The end goal is to be able to see these keywords in context at just one quick glance. If I manage to get these files, I'll link to them on my Excel spreadsheet with hyperlinks.
This is the code I have for now - obviously it doesn't work:
'Get list of files in folder
Dim xRow As Long
Dim xDirect$, xFname$, InitialFoldr$
InitialFoldr$ = "blablabla"
With Application.FileDialog(msoFileDialogFolderPicker)
.InitialFileName = Application.DefaultFilePath & "\"
.Title = "Please select a folder to list Files from"
.InitialFileName = InitialFoldr$
If .SelectedItems.Count <> 0 Then
xDirect$ = .SelectedItems(1) & "\"
xFname$ = Dir(xDirect$, 7)
Do While xFname$ <> ""
Sheets("List of files folder").Cells(3, 2).Offset(xRow) = xFname$
xRow = xRow + 1
xFname$ = Dir
Loop
End If
End With
'Get # rows in list of files in folder
Dim myrng4 As Range
Dim lastlinelist As Integer
Dim htmlpath As String
Dim objWord
Dim objDoc
Set objWord = CreateObject("Word.Application")
Set myrng4 = Sheets("List of files folder").Range("B3:B50000")
lastlinelist = myrng4.Find(What:="*", LookIn:=xlValues, Lookat:=xlWhole, SearchOrder:=xlRows, _
SearchDirection:=xlPrevious).Row
For Each cn In Range(wb.Sheets("Results conso").Cells(3, 11), wb.Sheets("Results conso").Cells(3, Lastcolumn))
For Each fileref In Range(Sheets("List of files folder").Cells(2, 3), Sheets("List of files folder").Cells(2, lastlinelist))
With Sheets("results conso")
htmlpath = InitialFoldr$ & fileref
If Dir(htmlpath) = "" Then
Else
If LCase(Right(pdfpath, 4)) <> "html" Then
Else
Set objDoc = objWord.Documents.Open(htmlpath)
objWord.Visible = True
objDoc.BringToFront
If objDoc.findText(cn.Value, True, True, False) = False Then
objDoc.Close True
Set objDoc = Nothing
Else
Call keybd_event(VK_SNAPSHOT, 0, 0, 0)
Set objDoc = wordobj.Documents.Add
wordobj.Visible = True
Set objselection = wordobj.Selection
objselection.Paste
End If
End If
End If
End With
Next fileref
Next cn
Also, I'm wondering if it might not be a better idea to do this with Python rather than VBA.
Many thanks,
Hadrien
It should be easy enough to find the word. I wrote a function to capture a screenshot. It is probably more code than you need. It is finding the program and the window etc.
Option Explicit
Private mblnFormActivated As Boolean
Private fsFolder As New FileSystemObject
Private fsFile As New FileSystemObject
Private bIsRealClick As Boolean
Private Declare Function ShellExecute Lib "shell32.dll" Alias "ShellExecuteA" _
(ByVal hwnd As Long, ByVal lpOperation As String, ByVal lpFile As String, _
ByVal lpParameters As String, ByVal lpDirectory As String, ByVal nShowCmd As Long) As Long
Private Declare Function MapVirtualKey Lib "user32" Alias "MapVirtualKeyA" (ByVal wCode As Long, ByVal wMapType As Long) As Long
Private Declare Sub keybd_event Lib "user32" (ByVal bVk As Byte, ByVal bScan As Byte, ByVal dwFlags As Long, ByVal dwExtraInfo As Long)
Private Const VK_MENU = &H12
Private Const VK_SNAPSHOT = &H2C
Private Const KEYEVENTF_KEYUP = &H2
Private Declare Function GetAsyncKeyState Lib "user32" (ByVal vkey As Long) As Integer
Private Sub cmdScreenshot_Click()
Dim ewohwnd As Long
Dim ewohwnd2 As Long
'This will capture a screenshot of the EWO window and save it to the job folder.
ewohwnd = GetHwndFromProcessName("EWO.EXE", "Byers View Station")
ewohwnd2 = GetHwndFromProcessName("EWO.EXE", "#32770")
If (ewohwnd2 = 0) Then
ewohwnd2 = GetHwndFromProcessName("EWO.EXE", "ICL Frame")
End If
If ewohwnd = 0 Then
MsgBox "EWO is not currently running."
Exit Sub
End If
If ewohwnd2 = 0 Then
MsgBox "validation has not been run"
Exit Sub
End If
DoEvents
If szJobFolderAlt = "" Then
Call GetWindowScreenshot(ewohwnd, ewohwnd2, szJobFolder & szJobStatus & " VALIDATION SCREENCOPY.JPG", 1)
Call ShellExecute(1, "Open", szJobFolder & szJobStatus & " VALIDATION SCREENCOPY.JPG", 0&, 0&, 10)
Else
Call GetWindowScreenshot(ewohwnd, ewohwnd2, szJobFolderAlt & szJobStatus & " VALIDATION SCREENCOPY.JPG", 1)
Call ShellExecute(1, "Open", szJobFolderAlt & szJobStatus & " VALIDATION SCREENCOPY.JPG", 0&, 0&, 10)
End If
End Sub
This is code from my module named modWindowScreenshot
Option Explicit
Private Const PIC_QUALITY_JPG = 75
Private Type GUID
data1 As Long
data2 As Integer
data3 As Integer
data4(7) As Byte
End Type
Private Type PicBmp
Size As Long
Type As Long
hBmp As Long
hPal As Long
Reserved As Long
End Type
Private Type RECT
Left As Long
Top As Long
Right As Long
Bottom As Long
End Type
Private Declare Function Sleep Lib "Kernel32" (ByVal dwMilliseconds As Long) As Long
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long
Private Declare Function GetWindowDC Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function CreateCompatibleDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function CreateCompatibleBitmap Lib "gdi32" (ByVal hdc As Long, ByVal nWidth As Long, ByVal nHeight As Long) As Long
Private Declare Function DeleteDC Lib "gdi32" (ByVal hdc As Long) As Long
Private Declare Function SelectObject Lib "gdi32" (ByVal hdc As Long, ByVal hObject As Long) As Long
Private Declare Function BitBlt Lib "gdi32" (ByVal hDCDest As Long, ByVal XDest As Long, ByVal YDest As Long, ByVal nWidth As Long, ByVal nHeight As Long, ByVal hDCSrc As Long, ByVal XSrc As Long, ByVal YSrc As Long, ByVal dwRop As Long) As Long
Private Declare Function ReleaseDC Lib "user32" (ByVal hwnd As Long, ByVal hdc As Long) As Long
Private Declare Function OleCreatePictureIndirect Lib "olepro32" (PicDesc As PicBmp, RefIID As GUID, ByVal fPictureOwnsHandle As Long, IPic As IPicture) As Long
Private Declare Function BringWindowToTop Lib "user32" (ByVal hwnd As Long) As Long
Private Const GW_CHILD = 5
Private Const GW_HWNDNEXT = 2
Private Declare Function GetDesktopWindow Lib "user32" () As Long
Private Declare Function GetWindow Lib "user32" (ByVal hwnd As Long, ByVal wCmd As Long) As Long
Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hwnd As Long, lpdwProcessId As Long) As Long
Private Declare Function IsWindowVisible Lib "user32" (ByVal hwnd As Long) As Long
Declare Function SetForegroundWindow Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function GetParent Lib "user32" (ByVal hwnd As Long) As Long
Private Declare Function FindWindow Lib "user32" Alias "FindWindowA" (ByVal lpClassName As String, ByVal lpWindowName As String) As Long
Private Declare Function GetClassName Lib "user32" Alias "GetClassNameA" (ByVal hwnd As Long, ByVal lpClassName As String, ByVal nMaxCount As Long) As Long
'----------------------------------------------------------------------------------------------------------------------
'Section for using gdi to convert to jpeg
Private Declare Function GdiplusStartup Lib "GdiPlus.dll" (ByRef mtoken As Long, ByRef mInput As GdiplusStartupInput, ByRef mOutput As Any) As GpStatus
Private Declare Sub GdiplusShutdown Lib "GdiPlus.dll" (ByVal mtoken As Long)
Private Declare Function GdipSaveImageToFile Lib "GdiPlus.dll" (ByVal mImage As Long, ByVal mFilename As String, ByRef mClsidEncoder As gGUID, ByRef mEncoderParams As EncoderParameters) As GpStatus
Private Declare Function GdipGetEncoderParameterList Lib "GdiPlus.dll" (ByVal mImage As Long, ByRef mClsidEncoder As gGUID, ByVal msize As Long, ByRef mBuffer As EncoderParameters) As GpStatus
Private Declare Function GdipGetEncoderParameterListSize Lib "GdiPlus.dll" (ByVal mImage As Long, ByRef mClsidEncoder As gGUID, ByRef msize As Long) As GpStatus
Private Declare Function GdipCreateBitmapFromHBITMAP Lib "GdiPlus.dll" (ByVal mHbm As Long, ByVal mhPal As Long, ByRef mBitmap As Long) As GpStatus
Private Declare Function GdipDisposeImage Lib "GdiPlus.dll" (ByVal mImage As Long) As GpStatus
Private Enum GpStatus
Ok = &H0
End Enum
Private Type gGUID
Data(0 To 3) As Long
End Type
Private Type GdiplusStartupInput
GdiplusVersion As Long
DebugEventCallback As Long
SuppressBackgroundThread As Long
SuppressExternalCodecs As Long
End Type
Private Type EncoderParameter
GUID As gGUID
lNumberOfValues As Long
lType As Long
lValue As Long
End Type
Private Type EncoderParameters
Count As Long
Parameter(4) As EncoderParameter
End Type
Private Const EncoderParameterValueTypeLong As Long = &H4
Public Function GetWindowScreenshot(WndHandle As Long, WndHandle2 As Long, SavePath As String, Optional BringFront As Integer = 1) As Long
'
' Function to create screeenshot of specified window and store at specified path
'
On Error GoTo ErrorHandler
Dim hDCSrc As Long
Dim hDCSrc2 As Long
Dim hDCMemory As Long
Dim hDCMemory2 As Long
Dim hBmp As Long
Dim hBmp2 As Long
Dim hBmpPrev As Long
Dim hBmpPrev2 As Long
Dim WidthSrc As Long
Dim WidthSrc2 As Long
Dim HeightSrc As Long
Dim HeightSrc2 As Long
Dim Pic As PicBmp
Dim IPic As IPicture
Dim IID_IDispatch As GUID
Dim rc As RECT
Dim rc2 As RECT
'Dim pictr As PictureBox
Dim stdPic As StdPicture
'Bring window on top of all windows if specified
If BringFront = 1 Then BringWindowToTop WndHandle
BringWindowToTop WndHandle2
Sleep 50
DoEvents
'Get Window Size
GetWindowRect WndHandle, rc
WidthSrc = rc.Right - rc.Left
HeightSrc = rc.Bottom - rc.Top
'Get Window device context
hDCSrc = GetWindowDC(WndHandle)
'create a memory device context
hDCMemory = CreateCompatibleDC(hDCSrc)
'create a bitmap compatible with window hdc
hBmp = CreateCompatibleBitmap(hDCSrc, WidthSrc, HeightSrc)
'copy newly created bitmap into memory device context
hBmpPrev = SelectObject(hDCMemory, hBmp)
'GET VALIDATION OVERLAY IN MEMORY
GetWindowRect WndHandle2, rc2
WidthSrc2 = rc2.Right - rc2.Left
HeightSrc2 = rc2.Bottom - rc2.Top
hDCSrc2 = GetWindowDC(WndHandle2)
hDCMemory2 = CreateCompatibleDC(hDCSrc2)
hBmp2 = CreateCompatibleBitmap(hDCSrc2, WidthSrc2, HeightSrc2)
hBmpPrev2 = SelectObject(hDCMemory2, hBmp2)
'copy window window hdc to memory hdc
Call BitBlt(hDCMemory, 0, 0, WidthSrc, HeightSrc, _
hDCSrc, 0, 0, vbSrcCopy)
'merg EWO and Validation messagebox
Call BitBlt(hDCMemory, (rc2.Left - rc.Left), (rc2.Top - rc.Top), WidthSrc2, HeightSrc2, _
hDCSrc2, 0, 0, vbMergeCopy)
'Get Bmp from memory Dc
hBmp = SelectObject(hDCMemory, hBmpPrev)
'release the created objects and free memory
Call DeleteDC(hDCMemory)
Call DeleteDC(hDCMemory2)
Call ReleaseDC(WndHandle, hDCSrc)
Call ReleaseDC(WndHandle2, hDCSrc2)
'fill in OLE IDispatch Interface ID
With IID_IDispatch
.data1 = &H20400
.data4(0) = &HC0
.data4(7) = &H46
End With
'fill Pic with necessary parts
With Pic
.Size = Len(Pic) 'Length of structure
.Type = vbPicTypeBitmap 'Type of Picture (bitmap)
.hBmp = hBmp 'Handle to bitmap
.hPal = 0& 'Handle to palette (may be null)
End With
'create OLE Picture object
Call OleCreatePictureIndirect(Pic, IID_IDispatch, 1, IPic)
'return the new Picture object
'saves as bmp
'SavePicture IPic, SavePath
Set stdPic = IPic
Call saveBitmapToFileAsJPEG(stdPic, SavePath)
GetWindowScreenshot = 1
Exit Function
ErrorHandler:
GetWindowScreenshot = 0
End Function
Public Function GetHwndFromProcessName(ByVal processName As String, Optional className As String = "") As Long
On Error GoTo ErrHandler
Dim oWMI
Dim ret
Dim oServices
Dim oService
Dim servicename
Set oWMI = GetObject("winmgmts:")
Set oServices = oWMI.InstancesOf("win32_process")
'loop through all running processes for exe name
For Each oService In oServices
servicename = LCase(Trim(CStr(oService.Name) & ""))
If InStr(1, servicename, LCase(processName), vbTextCompare) > 0 Then
GetHwndFromProcessName = GetHwnd(oService.ProcessID, className)
Exit For
End If
Next
Set oServices = Nothing
Set oWMI = Nothing
ErrHandler:
Err.Clear
End Function
Private Function GetHwnd(ByVal ProcessID As Long, Optional className As String = "") As Long
Dim lHwnd As Long, RetHwnd As Long, RetPID As Long
Dim sClassName As String
Dim lMaxCount As Long
Dim lResult As Long
lMaxCount = 256
sClassName = Space(lMaxCount)
lHwnd = GetDesktopWindow()
RetHwnd = GetWindow(lHwnd, GW_CHILD)
'loop through all windows
Do While RetHwnd
If IsWindowVisible(RetHwnd) Then
If GetParent(RetHwnd) = 0 Then
'Check process id and window class name to get top window handle
'Using class name as well as process id filters out dialog windows that are not children
Call GetWindowThreadProcessId(RetHwnd, RetPID)
lResult = GetClassName(RetHwnd, sClassName, lMaxCount)
If RetPID = ProcessID Then
If className <> "" Then
If Left(sClassName, lResult) = className Then
Exit Do
End If
If Left(sClassName, 9) = className Then
Exit Do
End If
Else
Exit Do
End If
End If
End If
End If
RetHwnd = GetWindow(RetHwnd, GW_HWNDNEXT)
Loop
GetHwnd = RetHwnd
End Function
Private Function saveBitmapToFileAsJPEG(ByRef oPic As StdPicture, szImgPath As String) As Boolean
Dim hGDIPToken As Long, udtGDIPStartup As GdiplusStartupInput, udtJPEGEnc As gGUID, _
udtEncParams As EncoderParameters, hImageScrShot As Long, bRet As Boolean
' init ret value and GDI+ startup UDT
bRet = False
udtGDIPStartup.GdiplusVersion = 1
If (GdiplusStartup(hGDIPToken, udtGDIPStartup, ByVal 0) = Ok) Then
With udtJPEGEnc
' JPEG Encoder GUID: {557CF401-11D3-1A04-739A-00002EF31EF8}
.Data(0) = &H557CF401
.Data(1) = &H11D31A04
.Data(2) = &H739A
.Data(3) = &H2EF31EF8
End With
With udtEncParams
.Count = 1
With .Parameter(0)
' EncoderQuality GUID: {1D5BE4B5-FA4A-452D-9CDD-5DB35105E7EB}
.GUID.Data(0) = &H1D5BE4B5
.GUID.Data(1) = &HFA4A452D
.GUID.Data(2) = &H9CDD5DB3
.GUID.Data(3) = &H5105E7EB
' The Quality Enc Param is a Long from 1(LQ) - 100(HQ)
.lType = EncoderParameterValueTypeLong
' Just this 1 "Quality" Value
.lNumberOfValues = 1
' Set Quality
'.lValue = CLng(100)
.lValue = PIC_QUALITY_JPG
End With
End With
' Create a GDIPlus Bitmap image based off the screen shot Picture
If (GdipCreateBitmapFromHBITMAP(oPic.Handle, 0, hImageScrShot) = Ok) Then
' Save it to a file and dispose of the Picture
If (GdipSaveImageToFile(hImageScrShot, StrConv(szImgPath, vbUnicode), udtJPEGEnc, udtEncParams) = Ok) Then
' File was saved to HDD
bRet = True
Set oPic = Nothing
End If
' Cleanup bitmap
Call GdipDisposeImage(hImageScrShot)
End If
' Shutdown GDI+
Call GdiplusShutdown(hGDIPToken)
End If
saveBitmapToFileAsJPEG = bRet
End Function
I would like to use a DLL (ImageSearch.dll) for my python project. It was initially developped for autoit.
Here is the au3 file:
Func _ImageSearchArea($findImage,$resultPosition,$x1,$y1,$right,$bottom,ByRef $x, ByRef $y, $tolerance)
;MsgBox(0,"asd","" & $x1 & " " & $y1 & " " & $right & " " & $bottom)
if $tolerance>0 then $findImage = "*" & $tolerance & " " & $findImage
$result = DllCall("ImageSearchDLL.dll","str","ImageSearch","int",$x1,"int",$y1,"int",$right,"int",$bottom,"str",$findImage)
; If error exit
if $result[0]="0" then return 0
; Otherwise get the x,y location of the match and the size of the image to
; compute the centre of search
$array = StringSplit($result[0],"|")
$x=Int(Number($array[2]))
$y=Int(Number($array[3]))
if $resultPosition=1 then
$x=$x + Int(Number($array[4])/2)
$y=$y + Int(Number($array[5])/2)
endif
return 1
EndFunc
So I try to use ctypes but I have problems to get the variable "result". Indeed, in the following script, the value of searchReturn is c_char_p(b'0') whereas with the autoit script, I have a string with '|' inside it.
from ctypes import *
ImageSearchDLL = windll.LoadLibrary("ImageSearchDLL")
ImageSearch = ImageSearchDLL.ImageSearch
searchReturn = c_char_p(ImageSearch(0,0,1919,1079,'myPic.bmp'))
print(searchReturn)
I also try to pass arguments with c_int, etc. and it leads to the same problem. If I don't use the c_char_p(), I have an int. I don't understand why I got an int, the header shows that it should return a str.
Ok I thought I should use cdll but after many attempts and defining arguments with the good way, I solve my problem. Thank you cdarke for your help :)
Here is the final script :
import ctypes
dllFunc = ctypes.windll.LoadLibrary('ImageSearchDLL.dll')
dllFunc.ImageSearch.argtypes = (ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_int, ctypes.c_char_p,)
dllFunc.ImageSearch.restype = ctypes.c_char_p
_result = dllFunc.ImageSearch(0, 0, 1920, 1080, b"myPic.bmp")
print(_result.decode('utf-8'))