251 lines
7.2 KiB
Python
251 lines
7.2 KiB
Python
import os
|
|
import string
|
|
import sys
|
|
|
|
import pythoncom
|
|
import win32api
|
|
from win32com.axdebug import (
|
|
adb,
|
|
axdebug,
|
|
codecontainer,
|
|
contexts,
|
|
documents,
|
|
expressions,
|
|
gateways,
|
|
)
|
|
from win32com.axdebug.util import _wrap, _wrap_remove, trace
|
|
from win32com.axscript import axscript
|
|
|
|
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())
|
|
)
|