1
 [DllImport("SiUSBXp.dll", CharSet = CharSet.Auto, EntryPoint = 
 "SI_GetProductStringSafe")]
        static extern SI_STATUS GetProductString(
            int dwDeviceNum,
            out IntPtr lvpDeviceString,
            int deviceStringLenInBytes,
            int dwFlags);

        static void Main(string[] args)
        {

            uint test = GetProductString(0, out IntPtr desc, 100, 0);

            Console.WriteLine($"Hello World! {Marshal.PtrToStringAuto(desc)}");
        }

Gives me a System.AccessViolationException.

test is 0, so the GetProductString call succeeded.

How do I get the string from lvpDeviceString which is a void* in unmanaged code.?

Here is function declaration:

/// @brief Gets a Product String
/// @param dwDeviceNum TBD
/// @param lpvDeviceString points at a buffer into which the Product String will be copied and returned
/// @param DeviceStringLenInBytes the length in bytes of lpvDeviceString the buffer into which the Product String will be copied and returned
/// @param dwFlags is the indication of which Product String to return 
/// @returns Returns SI_SUCCESS on success, another SI_STATUS if there is an error:
///         SI_DEVICE_IO_FAILED -- a driver-internal error occurred
_Check_return_
_Ret_range_(SI_SUCCESS, SI_DEVICE_NOT_FOUND)
_Success_(SI_SUCCESS)
SI_USB_XP_API
SI_STATUS WINAPI SI_GetProductStringSafe(
    _In_ _Pre_defensive_ const DWORD dwDeviceNum,
    _Out_writes_bytes_(DeviceStringLenInBytes) LPVOID lpvDeviceString,
    _In_ _Pre_defensive_ const size_t DeviceStringLenInBytes,
    _In_ _Pre_defensive_ const DWORD dwFlags
    );

and here is some documentation: https://www.silabs.com/documents/public/application-notes/AN169.pdf

Piglet
  • 27,501
  • 3
  • 20
  • 43
  • 1
    Use the *Memory* window in Visual Studio to look at what is really in there. – aybe Aug 21 '23 at 14:05
  • 1
    @aybe thanks. I only see question marks at the address stored in the pointer. am I doing something wrong with importing the function? – Piglet Aug 21 '23 at 14:12
  • 1
    Kinda hard to tell, but quickly looked at the DLL loaded from whatever website, are you doing things correctly? i.e. I would assume that you probably called `SI_GetNumDevices` then iterate with `SI_GetProductString`, else it would be garbage. This is just an assumption, i.e. in C-Style APIs there are generally an 'get product count' method and then an 'get product whatever data' method. The latter could return whatever memory in case `dwDeviceNum` is unknown rather than crashing the system. – aybe Aug 21 '23 at 14:19
  • 1
    @aybe yes. SI_GetNumDevices correctly returns 1. I then call SI_GetProductStringSafe for dwDeviceNum 0, it would return an error code for any other number as there is only one device connected. I just don't know how to get the string from lvpDeviceString – Piglet Aug 21 '23 at 14:26
  • 1
    There doesn't seem to be any kind of public online documentation for that SDK... I would read it once again. Possibly, there might also be usage examples, see if there is any sample project you could look at in the folder of that kit. – aybe Aug 21 '23 at 14:40
  • 1
    Could you show the original definition and how this interface is used in C? The corresponding type of `out IntPtr` should be `void**`, however, what you mentioned in the title is `void*`. – shingo Aug 21 '23 at 14:48
  • 1
    @shingo I added more context. according to the doc you're supposed to get the number of devices and then iterate over ( 0 - numberOfDevices -1) to get the descriptions – Piglet Aug 21 '23 at 14:52
  • 1
    I only find `SI_GetProductString` in the docs, according to it, you should prepare the buffer yourself. (e.g. `new byte[100]`) Then pass it as the second parameter. – shingo Aug 21 '23 at 15:15
  • 1
    @shingo you say out IntPtr should be void**, if so is out int and IntPtr equivalent? – Piglet Aug 21 '23 at 15:43
  • No, IntPtr is equivalent to nint (or void*), out int is equivalent to int* – shingo Aug 22 '23 at 04:20

1 Answers1

3

I solved it using StringBuilder. Not sure if this is ideal but it works.

using System;
using System.Runtime.InteropServices;
using System.Text;

namespace DllImportTest
{
    using SI_STATUS = UInt32;
    class Program
    {

        [DllImport("SiUSBXp.dll", CharSet = CharSet.Auto, EntryPoint = "SI_GetNumDevices")]
        static extern SI_STATUS GetNumDevices(
            out uint lpdwNumDevices);

        [DllImport("SiUSBXp.dll", CharSet = CharSet.Auto, EntryPoint = "SI_GetProductStringSafe")]
        static extern SI_STATUS GetProductString(
            int dwDeviceNum,
            [MarshalAs(UnmanagedType.LPStr)]
            StringBuilder lvpDeviceString,
            int deviceStringLenInBytes,
            int dwFlags);



        static void Main(string[] args)
        {
            SI_STATUS status = GetNumDevices(out uint numDevices);
            
            for (int i = 0; i < numDevices; i++)
            {
                StringBuilder sb = new StringBuilder(100);
                status = GetProductString(i, sb, 100, 0x01);

                Console.WriteLine($"Device {i}: {sb}");
            }
        }
    }
}
Piglet
  • 27,501
  • 3
  • 20
  • 43