Managing Memory

The Visual FoxPro API provides direct access to the Visual FoxPro dynamic memory manager. For API routines that request memory allocations, a memory identifier – or handle, is returned. The Visual FoxPro segment-loading architecture uses handles instead of pointers so it can manage memory more efficiently.

Note   The techniques described in this section for managing memory using the Visual FoxPro API apply to both ActiveX controls and FLL libraries.

Using Handles

A handle refers to a memory handle, which is essentially an index into an array of pointers. The pointers point to blocks of memory that Visual FoxPro knows about. Nearly all references to memory in the API are made through handles instead of the more traditional C pointers.

To allocate and use memory in your library

  1. Allocate a handle with _AllocHand( ).

  2. Lock the handle with _HLock( ).

  3. Convert the handle into a pointer with _HandToPtr( ).

  4. Reference the memory by using the pointer.

  5. Unlock the handle with _HUnLock( ).

    Note   To avoid memo file corruption, don't write to a memo file before calling _AllocMemo( ).

In order to address the allocated memory, your API routines must convert the handle to a pointer by calling the _HandToPtr( ) routine. Even if the Visual FoxPro memory manager needs to reorganize memory to obtain more contiguous memory for subsequent memory requests, the handle remains the same. Routines that grow, shrink, free, and lock memory allocations are also provided.

When you're creating external routines, try to minimize memory use. If you create an external routine that dynamically allocates memory, try to use the least amount of memory possible. Be especially careful about locking large memory allocations for long periods of time. Remember to unlock memory handles with _HUnLock( ) when they no longer need to be locked, because the performance of Visual FoxPro can be adversely affected by locked memory handles.

Caution   Excessive use of dynamic memory deprives Visual FoxPro of memory for buffers, windows, menus, and so on, and degrades performance, because the memory given to fill API requests is managed by the Visual FoxPro memory manager. Allocating large handles and retaining them could cause Visual FoxPro to run out of memory and terminate abnormally. The Visual FoxPro environment has no memory protection. The external API routine cannot provide all the validation that's inherent in a standard Visual FoxPro program. If you corrupt memory, you receive messages such as "Transgressed handle," "Internal consistency error," and "Transgressed node during compaction."

The following function from an FLL library illustrates memory allocation. The example uses _RetDateStr( ) to return a Visual FoxPro Date type (assuming that the Character parameter is a proper date):

#include <Pro_ext.h>

void dates(ParamBlk  *parm)
{
   MHANDLE mh;
   char *instring;

   if ((mh = _AllocHand(parm->p[0].val.ev_length + 1)) == 0) {
      _Error(182); // "Insufficient memory"
   }
   _HLock(parm->p[0].val.ev_handle);
   instring = _HandToPtr(parm->p[0].val.ev_handle);
   instring[parm->p[0].val.ev_length] = '\0';
   _RetDateStr(instring);
   _HUnLock(parm->p[0].val.ev_handle);
}
FoxInfo myFoxInfo[] = {
   {"DATES", (FPFI) dates, 1, "C"}
};
FoxTable _FoxTable = {
   (FoxTable *) 0, sizeof(myFoxInfo)/sizeof(FoxInfo), myFoxInfo
};

Understanding Stacks

The control or library you create doesn't have a stack of its own. Instead, it uses the stack of its calling program, in this case the Visual FoxPro stack. You cannot control the size of the Visual FoxPro stack or affect the amount of stack space available to an ActiveX control or .fll file.

Under normal circumstances, this distinction isn't important. The Visual FoxPro stack is generally large enough to hold the automatic variables you might need to allocate in a control or library. If you run out of stack space, you can always allocate additional memory on the heap dynamically.

Following Handle Rules

The following rules apply to ownership of handles and the responsibility for freeing them:

  • Users must free all handles they allocate, including handles allocated by functions such as _Load().

  • _Load() only creates a handle when the variable you're loading is a character string (that is, ev_type = 'C'). All the other data types store their values in the Value structure itself, while loading a character string puts an MHANDLE in the ev_handle of the Value structure.

  • In an FLL library, Visual FoxPro assumes responsibility for freeing all handles returned with _RetVal( ). Users must not free these handles, even if they allocate them.

  • Users must not free handles passed to them in their ParamBlk.

    Caution   When you write an external routine that calls functions, be careful to follow all rules and check the return results. A stray pointer or handle reference could damage the Visual FoxPro internal data structures, causing an immediate abnormal termination or delayed problems, which could result in data loss.

See Also

Access to Visual FoxPro Variables and Fields | Building and Debugging Libraries and ActiveX Controls | Accessing the Visual FoxPro API | Extending Visual FoxPro with External Libraries | _AllocHand( ) | API Library Construction