203 lines
6.8 KiB
Python
203 lines
6.8 KiB
Python
import sys, traceback, string
|
|
|
|
from win32com.axscript import axscript
|
|
from win32com.axdebug import codecontainer, axdebug, gateways, documents, contexts, adb, expressions
|
|
from win32com.axdebug.util import trace, _wrap, _wrap_remove
|
|
|
|
import pythoncom
|
|
import win32api, winerror
|
|
import os
|
|
|
|
currentDebugger = None
|
|
|
|
class ModuleTreeNode:
|
|
"""Helper class for building a module tree
|
|
"""
|
|
def __init__(self, module):
|
|
modName = module.__name__
|
|
self.moduleName = modName
|
|
self.module = module
|
|
self.realNode = None
|
|
self.cont = codecontainer.SourceModuleContainer(module)
|
|
def __repr__(self):
|
|
return "<ModuleTreeNode wrapping %s>" % (self.module)
|
|
def Attach(self, parentRealNode):
|
|
self.realNode.Attach(parentRealNode)
|
|
|
|
def Close(self):
|
|
self.module = None
|
|
self.cont = None
|
|
self.realNode = None
|
|
|
|
def BuildModule(module, built_nodes, rootNode, create_node_fn, create_node_args ):
|
|
if module:
|
|
keep = module.__name__
|
|
keep = keep and (built_nodes.get(module) is None)
|
|
if keep and hasattr(module, '__file__'):
|
|
keep = string.lower(os.path.splitext(module.__file__)[1]) not in [".pyd", ".dll"]
|
|
# keep = keep and module.__name__=='__main__'
|
|
if module and keep:
|
|
# print "keeping", module.__name__
|
|
node = ModuleTreeNode(module)
|
|
built_nodes[module] = node
|
|
realNode = create_node_fn(*(node,)+create_node_args)
|
|
node.realNode = realNode
|
|
|
|
# Split into parent nodes.
|
|
parts = string.split(module.__name__, '.')
|
|
if parts[-1][:8]=='__init__': parts = parts[:-1]
|
|
parent = string.join(parts[:-1], '.')
|
|
parentNode = rootNode
|
|
if parent:
|
|
parentModule = sys.modules[parent]
|
|
BuildModule(parentModule, built_nodes, rootNode, create_node_fn, create_node_args)
|
|
if parentModule in built_nodes:
|
|
parentNode = built_nodes[parentModule].realNode
|
|
node.Attach(parentNode)
|
|
|
|
def RefreshAllModules(builtItems, rootNode, create_node, create_node_args):
|
|
for module in list(sys.modules.values()):
|
|
BuildModule(module, builtItems, rootNode, create_node, create_node_args)
|
|
|
|
# realNode = pdm.CreateDebugDocumentHelper(None) # DebugDocumentHelper node?
|
|
# app.CreateApplicationNode() # doc provider node.
|
|
|
|
class CodeContainerProvider(documents.CodeContainerProvider):
|
|
def __init__(self, axdebugger):
|
|
self.axdebugger = axdebugger
|
|
documents.CodeContainerProvider.__init__(self)
|
|
self.currentNumModules = len(sys.modules)
|
|
self.nodes = {}
|
|
self.axdebugger.RefreshAllModules(self.nodes, self)
|
|
|
|
def FromFileName(self, fname):
|
|
### It appears we cant add modules during a debug session!
|
|
# if self.currentNumModules != len(sys.modules):
|
|
# self.axdebugger.RefreshAllModules(self.nodes, self)
|
|
# self.currentNumModules = len(sys.modules)
|
|
# for key in self.ccsAndNodes.keys():
|
|
# print "File:", key
|
|
return documents.CodeContainerProvider.FromFileName(self, fname)
|
|
|
|
def Close(self):
|
|
documents.CodeContainerProvider.Close(self)
|
|
self.axdebugger = None
|
|
print("Closing %d nodes" % (len(self.nodes)))
|
|
for node in self.nodes.values():
|
|
node.Close()
|
|
self.nodes = {}
|
|
|
|
class OriginalInterfaceMaker:
|
|
def MakeInterfaces(self, pdm):
|
|
app = self.pdm.CreateApplication()
|
|
self.cookie = pdm.AddApplication(app)
|
|
root = app.GetRootNode()
|
|
return app, root
|
|
|
|
def CloseInterfaces(self, pdm):
|
|
pdm.RemoveApplication(self.cookie)
|
|
|
|
class SimpleHostStyleInterfaceMaker:
|
|
def MakeInterfaces(self, pdm):
|
|
app = pdm.GetDefaultApplication()
|
|
root = app.GetRootNode()
|
|
return app, root
|
|
|
|
def CloseInterfaces(self, pdm):
|
|
pass
|
|
|
|
|
|
class AXDebugger:
|
|
def __init__(self, interfaceMaker = None, processName = None):
|
|
if processName is None: processName = "Python Process"
|
|
if interfaceMaker is None: interfaceMaker = SimpleHostStyleInterfaceMaker()
|
|
|
|
self.pydebugger = adb.Debugger()
|
|
|
|
self.pdm=pythoncom.CoCreateInstance(axdebug.CLSID_ProcessDebugManager,None,pythoncom.CLSCTX_ALL, axdebug.IID_IProcessDebugManager)
|
|
|
|
self.app, self.root = interfaceMaker.MakeInterfaces(self.pdm)
|
|
self.app.SetName(processName)
|
|
self.interfaceMaker = interfaceMaker
|
|
|
|
expressionProvider = _wrap(expressions.ProvideExpressionContexts(), axdebug.IID_IProvideExpressionContexts)
|
|
self.expressionCookie = self.app.AddGlobalExpressionContextProvider(expressionProvider)
|
|
|
|
contProvider = CodeContainerProvider(self)
|
|
self.pydebugger.AttachApp(self.app, contProvider)
|
|
|
|
def Break(self):
|
|
# Get the frame we start debugging from - this is the frame 1 level up
|
|
try:
|
|
1 + ''
|
|
except:
|
|
frame = sys.exc_info()[2].tb_frame.f_back
|
|
|
|
# Get/create the debugger, and tell it to break.
|
|
self.app.StartDebugSession()
|
|
# self.app.CauseBreak()
|
|
|
|
self.pydebugger.SetupAXDebugging(None, frame)
|
|
self.pydebugger.set_trace()
|
|
|
|
def Close(self):
|
|
self.pydebugger.ResetAXDebugging()
|
|
self.interfaceMaker.CloseInterfaces(self.pdm)
|
|
self.pydebugger.CloseApp()
|
|
self.app.RemoveGlobalExpressionContextProvider(self.expressionCookie)
|
|
self.expressionCookie = None
|
|
|
|
self.pdm = None
|
|
self.app = None
|
|
self.pydebugger = None
|
|
self.root = None
|
|
|
|
def RefreshAllModules(self, nodes, containerProvider):
|
|
RefreshAllModules(nodes, self.root, self.CreateApplicationNode, (containerProvider,))
|
|
|
|
def CreateApplicationNode(self, node, containerProvider):
|
|
realNode = self.app.CreateApplicationNode()
|
|
|
|
document = documents.DebugDocumentText(node.cont)
|
|
document = _wrap(document, axdebug.IID_IDebugDocument)
|
|
|
|
node.cont.debugDocument = document
|
|
|
|
provider = documents.DebugDocumentProvider(document)
|
|
provider = _wrap(provider, axdebug.IID_IDebugDocumentProvider)
|
|
realNode.SetDocumentProvider(provider)
|
|
|
|
containerProvider.AddCodeContainer(node.cont, realNode)
|
|
return realNode
|
|
|
|
def _GetCurrentDebugger():
|
|
global currentDebugger
|
|
if currentDebugger is None:
|
|
currentDebugger = AXDebugger()
|
|
return currentDebugger
|
|
|
|
def Break():
|
|
_GetCurrentDebugger().Break()
|
|
|
|
brk = Break
|
|
set_trace = Break
|
|
|
|
def dosomethingelse():
|
|
a=2
|
|
b = "Hi there"
|
|
|
|
def dosomething():
|
|
a=1
|
|
b=2
|
|
dosomethingelse()
|
|
|
|
def test():
|
|
Break()
|
|
input("Waiting...")
|
|
dosomething()
|
|
print("Done")
|
|
|
|
if __name__=='__main__':
|
|
print("About to test the debugging interfaces!")
|
|
test()
|
|
print(" %d/%d com objects still alive" % (pythoncom._GetInterfaceCount(), pythoncom._GetGatewayCount()))
|