Here I described how VFP generates executable code and runs it for early and late binding COM clients and implementing COM interfaces. However, there is an important issue with generating and running executable code in the same process.
A computer has a processor (or a few) that fetches instructions from memory and executes them. Some of the instructions refer to other areas of memory. The Operating System loads the memory initially. Various areas of memory are used for different purposes:
- Code (the program)
- Stack (for local variables and return addresses)
- Initialized Data (like constants)
- Uninitialized Data (like an uninitialized static array)
It gets a little more complicated with multiple DLLs each of which may have its own code/data sections, and threads, each of which will have its own stack, but we’ll ignore this for now.
Certain areas of memory will remain constant for the process lifetime, such as the code. Other areas will change dynamically, such as the stack and the heap. The CPU will fetch instructions only through the Instruction Pointer.
The NX feature prevents code from running from non-code marked pages of memory, so if the IP points to an NX area of memory, an exception is thrown. Certain viruses and worms cause execution of code from the stack or heap, so this prevents certain kinds of malicious attacks.
The generated machine code for VFP COM servers and implemented interfaces lives in heap memory, so the IP will be fetching instructions from the heap.
(This feature is called NX (No Execute) on AMD machines, XD (Execute Disable) by Intel, and DEP (Data Execution Prevention) on Windows Server 2003 and I believe, WinXP SP2. Does anyone see this feature on WinXPSP2?)
To test this, I installed VFP8 and VFP9 (RTM) on an AMD64 machine running Windows Server 2003 SP1. Right click on My Computer, select Properties->Advanced->Performance Settings->Data Execution Prevention->Turn on DEP for all programs and services except those I select:
Then I built my universal COM server below (which is used to run my website, by the way) and tested it. On a WinXP machine, the code runs fine, with both early and late bound calls succeeding. If I use CREATEOBJECTEX to create an object which will be accessed via early binding, I can use GETINTERFACE to get the IDispatch interface to call it via late binding. Conversely, an object that is created via CREATEOBJECT will be accessed by late binding, but I can use GETINTERFACE to get its early bound interface
On the AMD machine with DEP enabled, VFP8 caused an Access Violation (see What is a C0000005 crash?) for the early bound calls, but VFP9 worked just fine. Why? Because generated executable code in VFP9 now gets put into a memory page marked with the PAGE_EXECUTE_READWRITE flag.
If your application invites any other 3rd party code into the process space (such as ActiveX controls, COM servers, DECLARE DLL, Load/Call or a SET LIBRARY TO) and you are getting unexpected errors, it may be due to this NX feature.
These articles go into a lot more detail: http://en.wikipedia.org/wiki/NX_bit
* A universal COM server
TEXT TO tempvar NOSHOW
DEFINE CLASS c1 as session olepublic
proc MyDoCmd(cCmd as string,p2 as Variant,p3 as Variant,p4 as Variant,p5 as Variant) helpstring 'Execute a command'
proc MyEval(cExpr as string,p2 as Variant,p3 as Variant,p4 as Variant,p5 as Variant) helpstring 'Evaluate an expression'
IF FILE("t1.dll") && unregister prior one
DECLARE integer DllUnregisterServer IN t1.dll
IF !FILE("t1.pjx") && the GUIDS are stored in the project, so to keep the same GUID, use the same PJX
BUILD PROJECT t1 FROM c1
BUILD MTDLL t1 FROM t1
ox=CREATEOBJECT("t1.c1") && create late bound object (IDispatch interface)
?"Late bound object with late bound call ",ox.MyEval("'EarlyLate='+sys(2334)")
oEarly = GETINTERFACE(ox,"IC1")
?"Late bound object with early bound call ",oEarly.MyEval("'EarlyLate='+sys(2334)")
oEarly=CREATEOBJECTEX("t1.c1","","") && create early bound object (IC1 interface)
?"Early bound object with early bound call",oEarly.MyEval("'EarlyLate='+sys(2334)")
?"Early bound object with late bound call",oDisp.MyEval("'EarlyLate='+sys(2334)")