cancel
Showing results for 
Search instead for 
Did you mean: 
cancel
Showing results for 
Search instead for 
Did you mean: 

Community Tip - Need help navigating or using the PTC Community? Contact the community team. X

C++ custom function

DM_10631844
11-Garnet

C++ custom function

 

11223344

 

 

ACCEPTED SOLUTION

Accepted Solutions

Hi,

Function to create a vector of given length made up of square of the number in DLL file "fc.dll".   DLL file is in the zip file enclosed. Put the DLL file in CustomFunctions folder of Prime installation directory.  Changing "fc.c" to create a square matrix with values on the diagonal is trivial so not done.

 

Capture.JPG

 

 

// fc.c : Defines the entry point for the DLL application.


#include "MCADINCL.H"
#include <math.h>

#define  INTERRUPTED            1
#define  INSUFFICIENT_MEMORY    2
#define  MUST_BE_REAL           3
#define  NUMBER_OF_ERRORS       3   

    // 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"
};


// this code executes the multiplication
// see the information of MathcadUserFunction
// to find out more
LRESULT  ExpandRealScalar(COMPLEXARRAY* const Product,
    LPCCOMPLEXSCALAR Scalar)
{
    unsigned int row, col;

    // check that the scalar argument is real
    if ((Scalar->imag != 0.0) || (Scalar->real < 1.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


    // allocate memory for the product
    if (!MathcadArrayAllocate(Product, floor(Scalar->real), 1,
        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 * Scalar->real;
    }
        // normal return
        return 0;
   }

    // fill out a FUNCTIONINFO structure with
    // the information needed for registering the
    // function with Mathcad
    FUNCTIONINFO fc =
    {
        // Name by which mathcad will recognize the function
        "fc",

        // description of "multiply" parameters to be used
        // by the Insert Function dialog box
        "a",

        // description of the function for the Insert Function dialog box       
        "returns the expansion of real scalar a",

        // pointer to the executible code
        // i.e. code that should be executed
        // when a user types in "fc(a)="
        (LPCFUNCTION)ExpandRealScalar,

        // multiply(a,M) returns a complex array
        COMPLEX_ARRAY,

        //fc(a) takes on one arguments
        1,

        // the first is a complex scalar
        { COMPLEX_SCALAR}
    };



    BOOL APIENTRY DllMain(HMODULE hModule,
        DWORD  ul_reason_for_call,
        LPVOID lpReserved
    )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
            //
                   // DLL is attaching to the address space of 
                   // the current process.
                   //

            // register the error message table
            // Note, that if your function never returns
            // an error -- you do not need to 
            // register an error message table
            if (CreateUserErrorMessageTable(
                hModule, NUMBER_OF_ERRORS, myErrorMessageTable))
                // and if the errors register OK
                // go ahead and
                // register user function
                CreateUserFunction(hModule, &fc);
            break;



        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }

#undef INTERRUPTED    
#undef INSUFFICIENT_MEMORY
#undef MUST_BE_REAL   
#undef NUMBER_OF_ERRORS

 

Cheers

Terry 

View solution in original post

15 REPLIES 15

Hi,

Function to create a vector of given length made up of square of the number in DLL file "fc.dll".   DLL file is in the zip file enclosed. Put the DLL file in CustomFunctions folder of Prime installation directory.  Changing "fc.c" to create a square matrix with values on the diagonal is trivial so not done.

 

Capture.JPG

 

 

// fc.c : Defines the entry point for the DLL application.


#include "MCADINCL.H"
#include <math.h>

#define  INTERRUPTED            1
#define  INSUFFICIENT_MEMORY    2
#define  MUST_BE_REAL           3
#define  NUMBER_OF_ERRORS       3   

    // 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"
};


// this code executes the multiplication
// see the information of MathcadUserFunction
// to find out more
LRESULT  ExpandRealScalar(COMPLEXARRAY* const Product,
    LPCCOMPLEXSCALAR Scalar)
{
    unsigned int row, col;

    // check that the scalar argument is real
    if ((Scalar->imag != 0.0) || (Scalar->real < 1.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


    // allocate memory for the product
    if (!MathcadArrayAllocate(Product, floor(Scalar->real), 1,
        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 * Scalar->real;
    }
        // normal return
        return 0;
   }

    // fill out a FUNCTIONINFO structure with
    // the information needed for registering the
    // function with Mathcad
    FUNCTIONINFO fc =
    {
        // Name by which mathcad will recognize the function
        "fc",

        // description of "multiply" parameters to be used
        // by the Insert Function dialog box
        "a",

        // description of the function for the Insert Function dialog box       
        "returns the expansion of real scalar a",

        // pointer to the executible code
        // i.e. code that should be executed
        // when a user types in "fc(a)="
        (LPCFUNCTION)ExpandRealScalar,

        // multiply(a,M) returns a complex array
        COMPLEX_ARRAY,

        //fc(a) takes on one arguments
        1,

        // the first is a complex scalar
        { COMPLEX_SCALAR}
    };



    BOOL APIENTRY DllMain(HMODULE hModule,
        DWORD  ul_reason_for_call,
        LPVOID lpReserved
    )
    {
        switch (ul_reason_for_call)
        {
        case DLL_PROCESS_ATTACH:
            //
                   // DLL is attaching to the address space of 
                   // the current process.
                   //

            // register the error message table
            // Note, that if your function never returns
            // an error -- you do not need to 
            // register an error message table
            if (CreateUserErrorMessageTable(
                hModule, NUMBER_OF_ERRORS, myErrorMessageTable))
                // and if the errors register OK
                // go ahead and
                // register user function
                CreateUserFunction(hModule, &fc);
            break;



        case DLL_THREAD_ATTACH:
        case DLL_THREAD_DETACH:
        case DLL_PROCESS_DETACH:
            break;
        }
        return TRUE;
    }

#undef INTERRUPTED    
#undef INSUFFICIENT_MEMORY
#undef MUST_BE_REAL   
#undef NUMBER_OF_ERRORS

 

Cheers

Terry 

Hello @DM_10631844,

 

It looks like you have a response from a community member. If it helped you solve your question please mark the reply as the Accepted Solution. 

Of course, if you have more to share on your issue, please let the Community know so other community members can continue to help you.

Thanks,
Vivek N.
Community Moderation Team.

Perfect thank you I have a question @terryhendicott you did it by command promp or by the visual studio program, I comment that I still do not understand how it would be to try to compile it by the visual studio program, by command it works well but I think that by program I do not add well the references etc if or I start from 0 can you indicate how it would be without the command promp thank you greetings

I compile by Borland C++ Builder 12 - paid for or Visual Studio Community 2022 - free.  Both use an IDE to make compilation easier.  I have used these so much have forgotten how to use the command line.  Using  a "*c" file forces c compilation not c++.

 

Four things to set are:

1)    Directory for include of  C:\Program Files\PTC\Mathcad Prime 10.0.0.0\Custom Functions

2)    #include "MCADINCL.H" in the source file *.c for the dll

3)    Directory for library C:\Program Files\PTC\Mathcad Prime 10.0.0.0\Custom Functions

4)    Inclusiion of the library mcaduser.lib

5)    I turn off wide character strings (optional)

 

1,3,4,5 are set by options in Project Properties in the IDE (Integrated Development Environment)

2 is in the source file.

 

Cheers

Terry

 

 

This is a project that I have but I would like to compile it in visual studio because with commands if I understand there are some of the steps you mention that have not worked for me
@terryhendicott

DM_10631844_0-1721703819495.pngDM_10631844_1-1721703827454.png

 

Hi,

Prime is a 64 bit program so needs 64 bit DLL's

Capture.JPG

Need to compile as c code so highlight file in solution explorer and right click and select rename file option

Capture4.jpg

 

Get rid of precompiled headers

Capture3.JPG

 

You need to explicitly call up the library to use it

Capture2.JPG

I also set the include and library directories to the original location of the header and library files.  It is the same directory.  I see you copied them into the solution directory. so no real need to set the directories

 

Cheers

Terry

Hi,

You change the properties like precompiled headers from the properties dialog launched like this:

Capture5.JPG

Has the function worked? What are the input variables of geometriavolcar as you compiled it please show in mathcad the example as it is solved

DM_10631844_0-1726097839382.png
Al entrar me aparece esto en el archivo log Exception data:
System.AccessViolationException: Intento de leer o escribir en la memoria protegida. A menudo, esto indica que hay otra memoria dañada.
en Efi.User.Function.Invoke4(Object arg1, Object arg2, Object arg3, Object arg4)
en $$b_706914c0(Object[] , Object )
en Mpl.Run.UnaryDelegateFunction.app[R,A](R& r, A aa)
en Mpl.Run.Function.Call_1[R,A](A aa)
en McdTranslator.values.op_eval(Object )
en $$a_70691364(Object[] , Object )
en Mpl.Run.UnaryDelegateFunction.app[R,A](R& r, A aa)
en Mpl.Run.Function.Call_1[R,A](A aa)
en McdTranslator.values.op_eval(Object )
en Update(Object[] , Object[] , Object[] )
en depman.SafeRun.Run()
en System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
en System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
en System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
en System.Threading.ThreadHelper.ThreadStart()

 

Hi

Function fc a c language function in this thread works OK.  It was  written by me so I can debug it easily.

Function geometria_volcar was written by someone else so it would take a long time to debug as it does not work with inputs as per your example.

Start a new thread as this one is months old.

Cheers

Terry

Greetings thanks

Hi

Function now works.

Capture.JPG

Hi

Back to your original post.  Swap the indices into the matrix.  Mathcad stores arrays in column major format.

No array[ row ][ col ]

Yes array[ col ][ row ]

Capture.JPG

DM_10631844_0-1730485991066.png

How would this example be for a custom function with c++? 

Great, thank you very much. Now I have another question. In the case of doing operations with sqrt, I had to use Newton Rapson's method. Is there another way? Could you tell me what the error is here for which it doesn't work for me? What happens is that this error always appears. I have not managed to make a good code, and also when I make a mistake, something I am not doing right.

 

DM_10631844_0-1730482735886.png

 

The idea was to pass the code from matlab to mathcad through the custom function c

DM_10631844_0-1730483007904.png

In the file I put the following functions, is it possible to do the same with units or only that way?

 

Announcements

Top Tags