Hi Robert,
robert wrote: We may be able to emulate this behavior, but to properly do so we need an example that shows what you describe.
Are you saying that you want to have one method as "handler" for all events, with different parameters depending on the event that was called ?
We can probably do that by creating a method "on the fly" that then does a Clipper Calling convention call to your code.
Can you provide us with an example ?
Whipped up a VERY basic sample of the approach to "retrofit" a timing log to specific object.method() calls, as this is more "against Dotnet thinking" compared to having a special method linking specific delegates in the init of interesting classes.
This approach might be used on the cursoradapter class of the largest tables, as larger tables are more suspect of causing delays. The second approach - hooking in Init of base class (perhaps with property switch) would be used to log_time all cursoradapter.method calls suspect of causing delays for all tables, for instance across different machines or on different customer data sets.
Compared to coverage profiler this will give more realistic times, but I have specific C-logger .Fll functionality to check in more detail / with even less overhead / Heisenbug changes introduced by measuring, as well as specific "tableaction-wrappers" allowing me to see problematic/slow code parts.
Code tries to reduce to the max, but it DOES have cleanup (not needed in most situations), but the objects wrapped normally are have their own lists of what and where to clear - unwrap is more a hint than sober implementation and logging would be piped either into txt or dbf....
Fire up vfp, run and grin

* BindEvent Logger
clea
*--- tiny stuff injected in app code
private poLog
poLog = createobject("Logger_Base")
***************************************************************
*-- this would usually be table based, to switch several dozen timers on/off with single SQL on table
= Check_Instance("Worker_Fast", "Do_Job")
= Check_Instance("Worker_Base", "Do_Wrk, Do_Val, Do_Sav")
= Check_Instance("Worker_Fast", "Do_Wrk, Do_Job")
= Check_Instance("Worker_Slow", "Do_Wrk, Do_Val, Do_Sav, Do_Job")
= Check_Instance("Worker_Sloppy", "Do_Wrk, Do_Val, Do_Sav, Do_Job")
function Check_Instance(tcObj, tcWatchList)
local loWork
loWork = createobject(m.tcObj)
= poLog.Wrap_List(m.loWork, m.tcWatchList)
loWork.Do_Job()
***************************************************************
* logger classes have more capabilities only example of "static external code"
define class EvWrapper as custom
function Wrap_List(toSource, tcMethodCSV, toHandler, tcDelegate))
local laMethods[1], lnRun
for lnRun = 1 to alines(laMethods, m.tcMethodCSV, 5, ",")
= this.Wrap_Both(m.toSource, laMethods[m.lnRun], m.toHandler, m.tcDelegate)
next
function Wrap_Both(toSource, tcEvent, toHandler, tcDelegate)
local loHandler, lcDelegate
*-- make last 2 parameters optional via defaults
loHandler = iif(vartype(m.toHandler)=="O", m.toHandler, this)
lcDelegate = iif(vartype(m.toHandler)=="C", m.tcDelegate, "Log")
= bindevent(m.toSource, m.tcEvent, m.loHandler, m.lcDelegate+"_Setup",0)
= bindevent(m.toSource, m.tcEvent, m.loHandler, m.lcDelegate+"_TearDown",1)
return
function UnWrap(toSource)
*-- ok, here it gets really ugly
*-- in demo code trying to hint at better cleanup
local laEv[1], lnRun
for lnRun = 1 to aevents(laEv, m.toSource)
= unbindevents(m.toSource, laEv[m.lnRun, 3], laEv[m.lnRun, 2], laEv[m.lnRun, 4])
next
return
enddefine
define class Logger_Base as EvWrapper
*--- timing log does not need special info, dummy parameter,
*--- don't overdo, as vfp parameters are slooooow
function Log_Setup(dum1, dum2, dum3)
= this.Log_Do("Setup")
function Log_TearDown(dum1, dum2, dum3)
= this.Log_Do("TearDown")
function Log_Do(tcFrom)
local laEv[1]
= aevents(laEv, 0)
? str(seconds(), 12, 4) + " at " + m.tcFrom ;
+ ">>>" + laEv[1].Class + "::" + laEv[2]
return
enddefine
*****************************************************
* here is the huge customer app where I am tasked to identify the slow parts
* I don't want to mess *inside* their code too much, as subclass methods will also have code
* and not will depend on properties to be slow
define class Worker_Base as custom
nVal = 1
nSav = 1
nWrk = 1
function Simulate(tnVal)
wait window program(program(-1)-1) timeout m.tnVal
wait clea
return
function destroy()
*-- ahemmm... good habits done ugly...
= poLog.UnWrap(this)
return dodefault()
function Do_Job()
= this.Do_wrk()
= this.Do_Val(1)
= this.Do_Sav("", 0)
function Do_wrk()
= this.Simulate(this.nWrk)
function Do_Val(toObj)
= this.Simulate(this.nVal)
function Do_Sav(toObj, tcTable)
= this.Simulate(this.nSav)
enddefine
define class Worker_Fast as Worker_Base
nVal = 0.2
nSav = 0.1
nWrk = 0.4
enddefine
define class Worker_Slow as Worker_Base
nVal = 2
nSav = 2
nWrk = 3
enddefine
define class Worker_Sloppy as Worker_Slow
nVal = 0.2
nSav = 3
nWrk = 1
enddefine