To get you started writing custom functions for PTC Mathcad, a number of code samples are included. Refer to the file MULTIPLY.C located in the Custom Functions\multiply subdirectory of your PTC Mathcad installation directory. MULTIPLY.C contains a two-argument function that multiplies a scalar by an array. When you compile and correctly link it, the new function multiply(a, M) becomes available when you restart PTC Mathcad.
A detailed description of the code in MULTIPLY.C follows. To see the code in its entirety, open the file in Visual Studio or any text editor.
Headers
#include "mcadincl.h"
Your C program must include the mcadincl.h header file, located in the Custom Functions subdirectory of the PTC Mathcad installation directory. This file contains the data structures that allow you to perform the following actions:
• Define functions that PTC Mathcad can read.
• Allocate and free scalars and arrays in a PTC Mathcad compatible way.
• Create error messages that can be returned to PTC Mathcad using the graphical user interface.
Error Messages
Typically, the next part of the program defines error codes for the types of errors you might expect to encounter. Be sure to trap errors for inappropriate data types, as these are among the most common misuses of PTC Mathcad functions. These are defined together, so it is easy to count the total number of errors. This total is required by the CreateUserErrorMessageTable function.
// table of error messages // if your function never returns an error -- you do not need to create this table char * myErrorMessageTable[NUMBER_OF_ERRORS] = { "interrupted",. "insufficient memory", "must be real" };
PTC Mathcad traps the following floating point exceptions: overflow, divide by zero, and invalid operation. In the case of these exceptions, PTC Mathcad displays a floating point error message under the function. PTC Mathcad also frees all the memory allocated when one of these errors occurs.
Algorithm
Next comes the code that executes your algorithm. If you are converting code from an existing library, you must recast it with the COMPLEXARRAY, COMPLEXSCALAR, and MCSTRING types passed and expected by PTC Mathcad.
The first argument to the algorithm is a pointer to the return value, in this case, Product. The remaining arguments are pointers to the input values coming from PTC Mathcad.
// this code executes the multiplication LRESULT MultiplyRealArrayByRealScalar( COMPLEXARRAY * const Product, LPCCOMPLEXSCALAR Scalar, LPCCOMPLEXARRAY Array ) { unsigned int row, col; // check that the scalar argument is real if ( Scalar->imag != 0.0) // if it is not, display "must be real" error message // under the scalar argument ( the 1st argument ) return MAKELRESULT( MUST_BE_REAL, 1)
// check that the array argument is real if ( Array->hImag != NULL ) // if it is not, display "must be real" error message // under the array argument ( the 2nd argument ) return MAKELRESULT( MUST_BE_REAL, 2);
// allocate memory for the product if( !MathcadArrayAllocate( Product, Array-rows,Array-cols, TRUE, // allocate memory for the real part FALSE )) // do not allocate memory for the imaginary part
// if allocation is not successful, return with the appropriate error code return INSUFFICIENT_MEMORY;
// if all is well so far -- go ahead and perform the multiplication for ( col = 0; col < Product-> cols; col++ ) { // check that a user has not tried to interrupt the calculation if (isUserInterrupted()) { // if user has interrupted -- free the allocated memory MathcadArrayFree( Product ); // and return with an appropriate error code return INTERRUPTED; } for ( row = 0; row < Product-> rows; row++ ) Product->hReal[col][row] = Scalar-> real*Array-> hReal[col][row]; }
// normal return return 0; }
Register the Function with PTC Mathcad
Fill out a FUNCTIONINFO structure with the information needed for registering the function with PTC Mathcad. This structure defines the name of the function within PTC Mathcad and not the name of the algorithm MultiplyRealArrayByRealScalar. It also defines the parameters, and a description, as well as a pointer to the algorithm used.
FUNCTIONINFO multiply = { // name by which Mathcad will recognize the function "multiply",
// description of "multiply" parameters to be used "a,M",
// description of the function "returns the product of real scalar a and real array M",
// pointer to the executable code // i.e. code that should be executed when a user types "multiply(a,M)=" (LPCFUNCTION)MultiplyRealArrayByRealScalar;
// multiply(a, M) returns a complex array COMPLEX_ARRAY,
// multiply takes on two arguments 2,
// the first is a complex scalar, the second a complex array { COMPLEX_SCALAR, COMPLEX_ARRAY} };
Dynamically Link the Library
DLL stands for Dynamic Link Library. The following code makes this library available to other Windows functions, in particular, PTC Mathcad, through Windows' DLL interface. You must also specify a reference to the entry point through your programming environment. The DLL entry point is called by the operating system when the DLL is loaded. PTC Mathcad requires that you register your custom functions and your error message table while the DLL is being loaded.
// DLL entry point code // the _CRT_INIT function is needed if you are using Microsoft's 32-bit compiler
BOOL WINAPI _CRT_INIT(HINSTANCE hinstDLL, DWORD dwReason, LPVOID lpReserved); BOOL WINAPI DllEntryPoint (HINSTANCE hDLL, DWORD dwReason, LPVOID lpReserved) { switch (dwReason) { case DLL_PROCESS_ATTACH: // DLL is attaching to the address space of the current process if (!_CRT_INIT(hDLL, dwReason, lpReserved)) return FALSE;
Register the Error Message Table and the Function
If your function never returns an error, an error message table is not required. You can register only one error message table per DLL, but you can register more than one function per DLL. You must also clean up any remaining processes or definitions.
if ( CreateUserErrorMessageTable( hDLL, NUMBER_OF_ERRORS, myErrorMessageTable ) ) // and if the errors register properly, register the user function CreateUserFunction( hDLL, &multiply ); break; case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: if (!_CRT_INIT(hDLL, dwReason, lpReserved)) return FALSE; break; } return TRUE; } #undef INTERRUPTED #undef INSUFFICIENT_MEMORY #undef MUST_BE_REAL #undef NUMBER_OF_ERRORS
Additional Information
• All values passed into and out of PTC Mathcad are complex, having both a real and an imaginary part to their structure (see mcadincl.h). You must split off the real and imaginary parts if you intend to manipulate them independently.
• Arrays are indexed by column, then row, as opposed to the order of indices inside PTC Mathcad, which is by row, then column.
• All arrays are assumed to have two dimensions. If you wish to reference a vector, set the first array index (column) to 0.