About ORCA callback functions

Several ORCA functions require you to code a callback function. A callback function provides a way for the called program (the ORCA DLL or the Library Manager) to execute code in the calling program (the ORCA program executable).

How ORCA uses callbacks

ORCA uses callback functions when an unknown number of items needs to be processed. The purpose of the callback function is to process each of the returned items, and in most cases return the information to the user.

Optional or required

Some callbacks handle errors that occur when the main work is being done -- for example, when compiling objects or building executables. For handling errors, the callback function is optional. Other callbacks handle the information you wanted when you called the function -- such as each item in a directory listing. Callbacks for information functions are required.

Language requirement

ORCA functions that require the use of callback functions can be used only by programs written in languages that use pointers, such as C and C++.

When you create a new ORCA callback function, use the CALLBACK macro to specify the calling convention of the function. On the Windows platform, CALLBACK is defined as __stdcall.

ORCA functions that use callbacks

These functions (which all have the prefix PBORCA_) use a callback function:

ORCA function call (prefix PBORCA_)

   Purpose of callback

BuildProjectEx

BuildProject

Called once for each deployment error

CompileEntryImport

CompileEntryImportList

CompileEntryRegenerate   

Called once for each compile error

ExecutableCreate

Called once for each link error

LibraryDirectory

Called once for each library entry name

ObjectQueryHierarchy

Called once for every ancestor name

ObjectQueryReference

Called once for every object referenced in the entry

SccSetTarget

Called once for each library in the library list


How a callback works

ORCA calls a callback function like this:

  1. The calling program allocates a buffer to hold data (the UserData buffer).

  2. The calling program calls an ORCA function, passing it pointers to the callback function and the UserData buffer.

  3. When the ORCA function needs to report information, it calls the callback function. It passes pointers to the structure holding the information and the UserData buffer.

  4. The callback function reads the information in the structure and formats it in the UserData buffer.

    Steps 3 and 4 repeat for each piece of information ORCA needs to report. An ORCA function might call the callback once, several times, or not at all, depending on whether errors occur or information needs to be reported.

  5. The ORCA function completes and returns control to the calling program, which reads the information in the UserData buffer.

Content of a callback function

The processing that occurs in the callback function is entirely up to you. This section illustrates a simple way of handling it.

UserData buffer

In this example, the UserData buffer is a structure with a field whose value points to the actual message buffer. Other fields keep track of the message buffer's contents as it is filled:

typedef struct ORCA_UserDataInfo {
   LPBYTE lpszBuffer;     // Buffer to store data
   DWORD dwCallCount;    // # of messages in buffer 
   DWORD dwBufferSize;   // size of buffer 
   DWORD dwBufferOffset; // current offset in buffer 
} ORCA_USERDATAINFO, FAR *PORCA_USERDATAINFO;

Calling program

In the calling program, the UserDataInfo structure is initialized.

The calling program does not know how much room will be required for messages, so it allocates 60000 bytes (an arbitrary size). If you are gathering link errors, it's probably enough. It might not be enough if you wanted directory information for a large library:

ORCA_USERDATAINFO UserDataBuffer;
PORCA_USERDATAINFO lpUserDataBuffer;
 
lpUserDataBuffer = &UserDataBuffer;
lpUserDataBuffer->dwCallCount = 0;
lpUserDataBuffer->dwBufferOffset = 0;
lpUserDataBuffer->dwBufferSize = 60000;
lpUserDataBuffer->lpszBuffer = 
  (LPTSTR)malloc((size_t)lpUserDataBuffer-> 
     dwBufferSize);
memset(lpUserDataBuffer->lpszBuffer, 
      0x00,(size_t)lpUserDataBuffer->dwBufferSize);

Define function pointer

The calling program defines a function pointer to the callback function that it passes to the ORCA function:

PBORCA_LINKPROC fpLinkProc; 
fpLinkProc = (PBORCA_LINKPROC)LinkErrors;

Call ORCA

The calling program calls the ORCA function, passing the callback function pointer and the UserData buffer pointer. This example calls PBORCA_ExecutableCreate, whose callback type is PBORCA_LNKPROC:

rtn = PBORCA_ExecutableCreate(..., (PBORCA_LNKPROC) 
fpLinkProc, lpUserDataBuffer);

Process results

Finally, the calling program can process or display information that the callback function stored in the UserData buffer.

Free allocated memory

If your UserData structure allocates memory, free the allocated memory:

free( lpUserDataBuffer->lpszBuffer )

Callback program

The callback program receives a structure with the current error or information and stores the information in the message buffer pointed to by lpszBuffer in the UserData buffer. It also manages the pointers stored in the UserData buffer.

Simple callback

A simple callback might do the following:

  • Keep count of the number of times it is called

  • Store messages and reallocate buffer if it overflows

This code implements a callback called LinkErrors for PBORCA_ExecutableCreate:

void CALLBACK LinkErrors(PPBORCA_LINKERR lpLinkError, 
      LPVOID lpUserData) 
{
   PORCA_USERDATAINFO lpData;   
   LPBYTE lpCurrByte;
   LPTSTR lpCurrentPtr;
   int iNeededSize;
   lpData = (PORCA_USERDATAINFO) lpUserData;
 
   // Keep track of number of link errors
   lpData->dwCallCount++;
 
   // Is buffer already full?
   if (lpData->dwBufferOffset==lpData->dwBufferSize)
   return;
 
   // How long is the new message? 
   // Message length plus carriage rtn and newline
   iNeededSize = 
      (_tcslen(lpLinkError->lpszMessageText) + 2)* 
          sizeof(TCHAR);
    // Reallocate buffer if necessary
   if ((lpData->dwBufferOffset + iNeededSize) > 
      lpData->dwBufferSize)
   {
      LPVOID lpNewBlock;
      DWORD  dwNewSize;
      dwNewSize = lpData->dwBufferSize * 2; 
      lpNewBlock = realloc(lpData->lpszBuffer,
         (size_t)dwNewSize);
      if (lpNewBlock)
      {
         lpData->lpszBuffer = (LPTSTR) lpNewBlock;
         lpData->dwBufferSize = dwNewSize;
      }
      else
         return;
   }
   
   // Set pointer for copying message to buffer
     lpCurrentPtr = lpData->lpszBuffer 
        + lpData->dwBufferOffset;   
      lpCurrString = (LPTSTR) lpCurrByte;
 
  // Copy link error message, CR, and LF to buffer. 
  _tcscpy(lpCurrentPtr, lpLinkError->lpszMessageText);
  _tcscat(lpCurrentPtr, _TEXT("\r\n"));
  lpData->dwBufferOffset += iNeededSize;
  return;
}