I have tried many different combinations of the various methods to marshal this call. This is a DLL that returns a pointer to a pointer to an array of structures. The types like debugPort are actually enums.
/**
* \struct debugConnectParameters
* \brief Get device characterization and specify connection parameters through ST-LINK interface.
*/
typedef struct debugConnectParameters {
debugPort dbgPort; /**< Select the type of debug interface #debugPort. */
int index; /**< Select one of the debug ports connected. */
char serialNumber[33]; /**< ST-LINK serial number. */
char firmwareVersion[20]; /**< Firmware version. */
char targetVoltage[5]; /**< Operate voltage. */
int accessPortNumber; /**< Number of available access port. */
int accessPort; /**< Select access port controller. */
debugConnectMode connectionMode; /**< Select the debug CONNECT mode #debugConnectMode. */
debugResetMode resetMode; /**< Select the debug RESET mode #debugResetMode. */
int isOldFirmware; /**< Check Old ST-LINK firmware version. */
frequencies freq; /**< Supported frequencies #frequencies. */
int frequency; /**< Select specific frequency. */
int isBridge; /**< Indicates if it's Bridge device or not. */
int shared; /**< Select connection type, if it's shared, use ST-LINK Server. */
} debugConnectParameters;
int getStLinkList(debugConnectParameters** stLinkList, int shared);
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public unsafe struct debugConnectParameters
{
debugPort dbgPort; /**< Select the type of debug interface #debugPort. */
int index; /**< Select one of the debug ports connected. */
fixed char serialNumber[33]; /**< ST-LINK serial number. */
fixed char firmwareVersion[20]; /**< Firmware version. */
fixed char targetVoltage[5]; /**< Operate voltage. */
int accessPortNumber; /**< Number of available access port. */
int accessPort; /**< Select access port controller. */
debugConnectMode connectionMode; /**< Select the debug CONNECT mode #debugConnectMode. */
debugResetMode resetMode; /**< Select the debug RESET mode #debugResetMode. */
int isOldFirmware; /**< Check Old ST-LINK firmware version. */
frequencies freq; /**< Supported frequencies #frequencies. */
int frequency; /**< Select specific frequency. */
int isBridge; /**< Indicates if it's Bridge device or not. */
int shared; /**< Select connection type, if it's shared, use ST-LINK Server. */
}
[DllImport(dllPath + "CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public unsafe extern static int getStLinkList(
debugConnectParameters** stLinkList,
int shared
);
I have tried replacing the using "out" and "ref" options. I have tried to find a version of IntPtr that it likes. I keep getting: System.AccessViolationException. I have contacted STMicroelectronics about how to interface with this DLL, but so far they have been no help. The rest of the calls in the DLL are pretty simple but I must have this one working to get started, since it is the information about what JTAG device is actually connected.
Updated (6/12/20) to add the actual calling function and now the calling function copies the structures from unmanaged to managed structures. But the call to getStLinkList() still does not work.
public unsafe static int GetDeviceList(ref debugConnectParameters[] debugParams, int maxCount)
{
IntPtr unManagedListPtr = Marshal.AllocHGlobal(sizeof(debugConnectParameters*));
IntPtr *unManagedListPtrPtr = &unManagedListPtr;
int count = getStLinkList((debugConnectParameters**)unManagedListPtrPtr, 0);
IntPtr copyPtr = unManagedListPtr;
if (count > maxCount) count = maxCount;
for (int i = 0; i < count; i++)
{
debugParams[i] = (debugConnectParameters)Marshal.PtrToStructure(copyPtr, typeof(debugConnectParameters));
copyPtr += sizeof(debugConnectParameters);
}
Marshal.FreeHGlobal(unManagedListPtr);
return (count);
}
I also tried these changes with no luck. I am still seeing access violations. Attempt to read or write protected memory.
[DllImport(dllPath + "CubeProgrammer_API.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
unsafe private static extern int getStLinkList(
ref IntPtr stLinkList,
int shared
);
unsafe public static int GetDeviceList(List<debugConnectParameters> list)
{
IntPtr unManagedListPtr = Marshal.AllocHGlobal(sizeof(debugConnectParameters*));
IntPtr copyPtr = unManagedListPtr;
int count = getStLinkList(ref unManagedListPtr, 0);
if (count > 10) count = 10;
for (int i = 0; i < count; i++)
{
debugConnectParameters parameter = (debugConnectParameters)Marshal.PtrToStructure(copyPtr, typeof(debugConnectParameters));
list.Add(parameter);
copyPtr += sizeof(debugConnectParameters);
}
Marshal.FreeHGlobal(unManagedListPtr);
return (count);
}
There are a two things working against us in regards to calling the
getStLinkListfunction of the "CubeProgrammer_API.dll" library in particular. Without correcting them the library will not work, even from C++.First, the dependencies for that library found in the api/lib folder of the STM32CubeProgrammer installation are not all compiled for x64. The versions in the bin folder are and those are the libraries you'll want to have available at runtime. I've done this by setting the working directory to that directory in the project Debug settings. This was verified by checking the target machine of each DLL in the api/lib and bin folders.
Second, the "CubeProgrammer_API.dll" needs to be initialized before calling
getStLinkListby calling thesetLoadersPathandsetDisplayCallbacksfunctions. The first function must be provided the path to the "Flash Loader", which is bin/FlashLoader in my case. ThesetDisplayCallbacksfunction takes a structure of three function pointers.The example below might not be the best example of type marshaling, but it will illustrate the process.
And main:
This will output the following with two debuggers attached, a STLink V3 and an STLink V2 respectively. This should get you far enough along to discover the correct types to use.