I got memory leak issue when I call .net method from clarion application. I have used libmaker to convert .net DLL to lib file and used in clarion project. I am passing BSTRING parameters to the .net method and receives BSTRING result in reference variable. Check the below code
Clarion side:
MAP
MODULE('NetApp')
ApiValidate (BSTRING pData, BSTRING pSoftwareKey, *BSTRING pResult), LONG, PASCAL, NAME('Validate')
END
END
ApiClass.Run Procedure()
lcData BSTRING
sKey BSTRING
result BSTRING
retval long
CODE
lcData= GETREG(REG_LOCAL_MACHINE, RegKey ,'Data') //retrieve some data from registry
sKey =’test data’
retval= ApiValidate (lcData, sKey , result)
clear(lcData)
clear(sKey)
clear(result)
.Net side
namespace MLNetToolbox
{
public enum MLError
{
ML_OK = 0
}
public sealed class WinAPI
{
[DllExport("Validate", CallingConvention = CallingConvention.StdCall)]
public static MLError Validate ([MarshalAs(UnmanagedType.BStr)] string data, [MarshalAs(UnmanagedType.BStr)] string softwareKey, [MarshalAs(UnmanagedType.BStr)] out string result)
{
result =”test”;
return MLError.ML_OK;
}
}
}
We are creating web application where on each request the application will call the above clarion method. While running the performance test, we have noticed significant increase in memory consumption. It shoots up to 500 mb. I used .Net framework 4.8 to build .net dll. Couldn’t figure out the potential cause of memory leak. It would be great if someone can help me on this.
Sorry Bruce I forgot to mention one thing. Actually the method which I mentioned in the post consumes around 274 MB. But if I call other .Net methods then certainly it can go beyond 500 MB
You’ll want to change your .Net to CallingConvention = CallingConvention.Pascal since Clarion is prototyped as PASCAL.
It seems like your code would not work because the parameters are in a different order i.e. Pascal pushed Left to Right versus Right to Left for StdCall.
I just got confused with explanation of pascal attribute in clarion help. There it says “In 32-bit programs, both C and PASCAL conventions pass the parameters to the stack from right to left.”
You might try passing all 3 BSTRING by address as *BSTRING
Passing by Value the RTL would have to duplicate them and maybe it’s not disposing. It also will be faster.
AFAIK BString support in Clarion is not complete. Like you cannot put them in a Group or Queue it will crash or leak. It might not really be passing them by value.
If you have the time, you can try to rewrite the Clarion/.NET parameters exchange using HeapAlloc/HeapFree.
For example, to return a string, the .NET side allocates heap memory, sets its value as an ANSI C string, and returns the heap address.
The Clarion side can read the string using st.SetValueByCstringAddress(addr) and then release the memory with HeapFree(GetProcessHeap(),0,addr). Of course, it will be better to write a front-end class.
I did this recently and it worked great. I used Claude Code to write the .NET side. I asked it to create a native AOT 32-bit DLL (requires .NET 9) to be called from C programs.