Application logging and Error methods
I have some Fox classes that run in several modes.
- The normal fox interactive design environment
- In a runtime Multithreaded DLL from the fox interactive environment
- In a runtime Multithreaded DLL from within a client application (like windows explorer)
The error methods of those classes do different things depending on _vfp.StartMode(“Returns the Fox Start Mode. 0=None, 1=LocalServer, 2=Runtime EXE Server, 3=Runtime DLL Server, 4=VFP Runtime App, 5=Runtime MTDLL Server”)
VFP MT DLLs do not normally do UI (User Interface) because they’re targeted to be used in server applications, such as web servers which have no UI. But you can still call MessageBox by using DECLARE DLL as shown.
Many windows APIs that take strings as parameters have two versions: ANSI and Wide char. The ANSI ones use a single byte per character, and the Wide versions use Unicode. If you single step through the assembly code you can see that one version just converts the parameter for the other and then calls it.
From winuser.h:
#ifdef UNICODE
#define MessageBox MessageBoxW
#else
#define MessageBox MessageBoxA
#endif // !UNICODE
You can see that both MessageBoxA and MessageBoxW are exported from user32.dll:
link /dump /exports c:\windows\system32\user32.dll
The OutputDebugString call outputs to any attached debugger. I typically run with Visual Studio as the debugger, so its output window shows a log of what’s happening.
The VFP program has a single PUBLIC integer variable called g_debugFlags which has up to 32 bits, each of which can indicate which things to log to the DebugOutputWindow and/or a logfile. Filtering the events to only those of interest reduces the noise.
I have both a RETRY and a RETURN statement: while at a breakpoint in the VFP debugger, I can change variable values or switch to the command window to open tables, etc. A RETRY will try again to execute the erroneous statement, and RETURN will skip that statement and proceed with the following one.
DEFINE CLASS ShellNode AS session
PROCEDURE error(nError, cMethod, nLine)
cStr=PROGRAM()+" "+MESSAGE()+" "+MESSAGE(1)+" "+TRANSFORM(nError)+" "+TRANSFORM(cMethod)+" "+TRANSFORM(nLine)
?cStr
IF _vfp.StartMode=0
SET STEP ON && open debugger
RETRY && to retry statement that triggered err
RETURN && SET NEXT Stmt (Shift+Ctrl-F7 here will skip statement that triggered err
ENDIF
DECLARE integer MessageBoxA IN WIN32API integer,string,string,integer
this.logit(cStr,.t.)
nRet=MessageBoxA(0,cStr,PROGRAM(),2+512+32)
DO CASE
CASE nRet=3 &&Abort
COMRETURNERROR(PROGRAM(),cStr)
CASE nRet=4 &&Retry
RETRY
CASE nRet=5 &&Ignore
RETURN
ENDCASE
PROCEDURE logit(cstr as String,fForce as Boolean)
IF BITAND(DF_NSELOGFILE + DF_NSEVFP,g_debugflags)>0 OR fForce
DECLARE integer OutputDebugString IN WIN32API string
cstr=TRANSFORM(DATETIME())+" "+cstr+CHR(13)
OutputDebugString(cstr)
IF BITAND(DF_NSELOGFILE,g_debugflags)>0 OR fForce
STRTOFILE(cstr,LOGFILE,.t.)
ENDIF
ENDIF
ENDDEFINE
63067
Comments
- Anonymous
May 27, 2005
Another great article, thanks!
On a probably less-important side aspect - is there a "hidden insight" in not declaring variables like cStr, nRet as Local at that point?
> PROCEDURE error(nError, cMethod, nLine)
> cStr=PROGRAM()+" "+MESSAGE()+"...
-Stefan - Anonymous
May 27, 2005
The comment has been removed - Anonymous
May 21, 2007
The comment has been removed