VB6 Python embed w/debugger
Date: 09.04.23 - 2:35am
So in the last post we had an example of python embedding within C.
C is not my main language, so it was only natural that the next step was to take it into the vb realm which is exactly what happened.
We have talked about integrating with python before. So in todays episode we do it embedded style.
The example linked above is my initial experiments with it. Current features in this sample:
The debugger example is the most fun. Python makes this ridiculously easy compared to what I have had to go through in the past.
- set script command line arguments
- manually import modules
- manually set global script values
- hook the python output to stdout (print) and stdErr
- run a python script in memory from its text string
- call a python function with an argument
- receive the function return value
- read a global variable/global obj property from the script
- hook exit, sys.exit, and quit so they dont close the process.
- interact with VB COM objects (pywin32lib)
- threading example
- python debugger example
- single step, breakpoints, call stack, step out, set next line, inspect globals/locals/args, modify variable
- Note: current UI does not yet have intellisense and I am just letting it use the java hilighter for now
I guess this is the 6th interactive debugger I have written. (x86, scdbg, Scriptbasic, duktape, pcode)
Python is kinda like eating candy, and also probably not good for your teeth. Interoperability and library wise, apparently we just need it. To many code monkeys churning out stuff, just cant deny it.
So anyway, being able to call fragments of code and utilize libraries from it is a force multiplier. To embed it in vb6 we will need the 32 bit version. I linked against Python 3.11
The python debug/trace api actually runs from within the script itself which is interesting.
Python embedding seems to be slightly a second class citizen. They generally think of everything (its mature at this point).
Overall though, implementing the basic debugger UI with most features was done in a half days labor which is crazy. (I did rip code from duk4vb though and was already familiar with scintilla from previous war campaigns)
- pywin32 extension doesnt really support embedding. Cant call finalize if you want it to work a second time.
- I havent found an end script api yet. exit and quit want to close the process without hooking.
- raise keyboard interrupt to end can be masked with while 1 try except. Not sure how to end the script in every context yet.
Anyway, there is still more to come. I started a thread on it over at vbforums. The example linked above will be updated more regularly than the forum copy.
This will probably end up as an ocx soon where I can call small python scripts in memory directly from vb6, reuse existing libraries I have to be compatiable with (or just no code myself), and the IDE / Debugger portion will be perfect for extending my own apps and using it for user automation.
Will see how it all shakes out.
- find proper end script now call with no loopholes.
- rip pywin32 IDispatch code and integrate into py4vb.dll so I can finalize.
- use python lexer and highlighter
- intellisense & obj browser
- review for memory leaks (Py_DecRef)
Comments: (3)On 09.12.23 - 7:20am Dave wrote:
|Stripping down pywin32 to include just the IDispatch stuff in my own dll. Pywin32 is pretty huge and covers a zillion things. Much over my head. Have a minimal build compiling now.|
Stepping through the python side is complicated too. It’s robust code, performance optimized. Interesting design.
Dumb dispatch for no type info. It brute forces methods to see what call type works, then dynamically builds a function definition, compiles and evals it into scope for python access. It’s then cached as a performance optimization.
The initial dynamic lookup triggers from the __getattr__ dunder voodoo to dynamically catch arbitrary access on demand.
It’s impressive, I may be able to do the same thing with duktape and a js proxyclass. I was pre building class wrappers from source parsing. In the full version he does that too but from typelibs.
It’s frying my noodle a bit. Complicated but not unapproachable . So far so good!
As for why, removing the dependency on pywin32 will allow me to call Py_Finalize again and start each script fresh. Plus since I am working all within process, integrating more tightly with it, I should be able to use my in memory live COM objects directly rather than having to work through the ROT.
Just tightening things up for my use case.
On 09.17.23 - 7:19pm Dave wrote:
|Took me maybe 3 days to get the COM bits of pywin32 extracted and implemented into my helper dll. Seems to be working. pycom.GetObject will now first check for a vb host shared object before trying the ROT.
see com_internal_example. Note: you will have to copy pycom folder to your python /lib/site-packages directory manually.
On 10.22.23 - 5:30pm Dave wrote:
|There is now an installer that busked everything together in an easy to use ocx packsge. Comes with a full python 311 install, ocx conttols fir python and debugger, and the custom dlls and python package already installed. You will still need the mscomctl but since your likely a vb6 developer already that’s not a problem.|