COM Memory Handling in Visual Prolog

From wiki.visual-prolog.com

Revision as of 10:17, 3 October 2007 by Kari Rastas (talk | contribs)

The Component Object Model (COM) implies usage of COM memory. When we use an external COM component or create our own COM component, we should handle parameters correctly.

The IDE generates necessary memory conversions, allocations and releasing on creating of COM package using component's type library. So normally, you do not have to consider the memory handling. However, sometimes some code is written manually, and then the rules must be known.

The possible situations are:

  • Call a COM predicate, which returns a parameter in COM memory;
  • Call a COM predicate with input parameter;
  • Call a COM predicate with input mutable parameter;
  • Implement a COM predicate, which gives us a parameter in COM memory;
  • Implement a COM predicate, which returns a parameter in COM memory;
  • Implement a COM Predicate. In/Out Parameter;

Please notice that integral parameters do not need special memory handling, but strings and structure domains require such memory handling.

Call a COM Predicate. Handle Returned Memory

When we use an external COM component, we can call a predicate, which returns COM memory to us. Usually we want to use the returned information for some time; hence we should copy the returned memory and free the COM memory then. Once the data is copied to Prolog memory it is controlled by the garbage collector.

In the example below, the returned parameter has string type.

clauses
    p() = Parameter:-
        HResult = someComPredicate(ComParameter),
        HResult = s_ok,
        !,
        Parameter= string::createCopy(ComParameter),
        comMemory::taskMemFree(ComParameter).
    p() = "".

Call a COM Predicate. Fill Input Parameter

When we use an external COM component, we can call a predicate, which requires our information. We can use any memory for such input parameters. If we use COM memory, then we should free it after the predicate invocation.

In the example below the parameter has myDom type.

domains
    myDom = myDom(integer X, string S).
clauses
    q(myDom(X, S)) :-
        HResult = someComPredicate(myDom(X, S)),
        check(HResult).

The IDE uses COM memory for input parameters when generating COM code. The IDE also generates the code to release the COM memory.

Call a COM Predicate. Fill Mutable Parameter

When we use an external COM component, we can call a predicate, which requires our information and which changes it. We should use COM memory for such parameters. After the call we should free the result COM memory.

In the example below, the parameter has myDom type.

domains
    myDom = myDom(integer X, string S).
clauses
    q(myDom(X, S)) = myDom(NewX, NewS):-
        ComParameterPtr= comMemory::taskMemAlloc(sizeofdomain(myDom)),
        ComS= comMemory::allocString(S),
        memory::copy(ComParameterPtr,
            uncheckedConvert(pointer,myDom(X, ComS)),
            sizeofdomain(myDom)),
        ComParameter= uncheckedConvert(myDom, ComParameterPtr)
        HResult = someComPredicate(ComParameter),
        ComParameter= myDom(NewX, NewComS),
        NewS= string::createCopy(NewComS),
        comMemory::taskMemFree(NewComS),
        comMemory::taskMemFree(ComParameter),
        check(HResult).

Please notice that inner fields can be changed, so it is necessary to find new values and release them.

Implement a COM Predicate. Handle Input Memory

When we create our own COM component or when we implement a callback predicate, we can receive a parameter in COM memory.

It is important to notice that this memory should not be released.

But if we are going to use the information for some time, then we should copy the memory.

In the example below, the parameter has bstr type.

clauses
    someComPredicate(ComParameter) = s_ok :-
        trap(somePredicate(basicString::toString(ComParameter)),
            TraceId, processTraceId(TraceId)),
        !.
    someComPredicate(_) = e_fail.
 
clauses
    somePredicate(Parameter) :-
        %some implementation here

Please also notice that Prolog predicates should be trapped, as the COM system cannot process Prolog fail or Prolog exception.

Implement a COM Predicate. Return Memory

When we create our own COM component or when we implement a callback predicate, we can be asked about a parameter. Such a parameter must be allocated in COM memory.

So, we should allocate COM memory and fill it with proper values.

In the example below the parameter has myDom type.

domains
    myDom = myDom(integer X, string S).
clauses
    someComPredicate(ComParameter) = s_ok :-
        trap(
            (
                myDom(X, S) = somePredicate(),
                ComParameterPtr = comMemory::taskMemAlloc(sizeofdomain(myDom)),
                ComS= comMemory::allocString(S),
                memory::copy(ComParameterPtr,
                    uncheckedConvert(pointer,myDom(X, ComS)),
                    sizeofdomain(myDom)),
                ComParameter= uncheckedConvert(myDom, ComParameterPtr)
            ),
          TraceId, processTraceId(TraceId)),
        !.
    someComPredicate(uncheckedConvert(myDom, null)) = e_fail.
 
clauses
    somePredicate() = Parameter :-
        %some implementation here

You can notice that inner parameters of structure domains should be handled as well.

Please also notice that Prolog predicates should be trapped, as COM system cannot process Prolog fail or Prolog exception.

Implement a COM Predicate. In/Out Parameter

When we create our own COM component or when we implement a callback predicate, we can receive a memory, which can be changed (i.e. [in, out]).

Then we should allocate COM memory, fill it with proper values, and release the replaced COM memory.

In the example below the parameter has myDom type.

domains
    myDom = myDom(integer X, string S).
clauses
    someComPredicate(ComParameter) = s_ok :-
        trap(
            (
                myDom(X, ComS)= ComParameter
                S = string::createCopy(ComS),
                myDom(NewX, NewS) = somePredicate(myDom(X, S)),
                ComParameterPtr= uncheckedConvert(pointer, ComParameter),
                comMemory::taskMemFree(ComS),
                NewComS = comMemory::allocString(NewS),
                memory::copy(ComParameterPtr,
                    uncheckedConvert(pointer,myDom(NewX, NewComS)),
                    sizeofdomain(myDom))
            ),
            TraceId, processTraceId(TraceId)),
        !.
    someComPredicate(_) = e_fail.
 
clauses
    somePredicate(Parameter) = NewParameter :-
        %some implementation here

Please notice the replaced parameter is released after invocation of the Prolog predicate. Hence, an exception in an implementation of somePredicate will not crash the mutable input parameter.

References