0

I have the following signature from c++

IDTECHREADER_EXPORTS void ReadTwoBlocks(char *pathConfig, 
    char *datablock1, char *datablock2, int timeout, bool &ret )

I was able to Marshal this correctly on the Full .NetFrameWork and it works as below

[DllImport("IDTechReader.dll", EntryPoint = "ReadTwoBlocks" )]
private static extern void _ReadTwoBlocks(
   [In][MarshalAs(UnmanagedType.LPStr)] string pathConfig,
   [Out][MarshalAs(UnmanagedType.LPStr)] StringBuilder datablock1, 
   [Out] [MarshalAs(UnmanagedType.LPStr)] StringBuilder datablock2,
   int TimeOut,
   ref bool result);

However using the same Marshaling as above doesn't work on the NetcompactFramework(Windows CE) it gives an error "NotSupported Exception"

How do we correctly Marshal the above C++ method signature so that it will work correctly on the .NET CompactFramework(windows CE)

any ideas are apperciated...thanks.

Tom van Enckevort
  • 4,170
  • 1
  • 25
  • 40
saroll
  • 217
  • 2
  • 4
  • 9
  • Are you sure the mentioned ReadTwoBlocks method is supported both on the full .NET Framework and the .NETCF? – Tom van Enckevort Nov 10 '11 at 14:49
  • @tomlog ReadTwoBlocks is a method exported from a native dll, saroll is trying write a wrapper. – garzanti Nov 10 '11 at 15:58
  • Yes, I understand that. But I guess what I meant to say is whether the native DLL is supported to run on both Windows CE and normal Windows 7/Vista/XP. – Tom van Enckevort Nov 10 '11 at 19:03
  • Nobody has explicitly mentioned it, and I'm not sure whether or not you know this, but CF omits lots of support for ANSI. You can either do the wide to ANSI conversion in the C# marshalling code, do the conversion in the C++ code, or switch to using wide characters in your C++ code. – David Heffernan Nov 11 '11 at 05:45
  • Did anybody answer your question? If so please indicate. Thanks. – Damon8or Nov 18 '11 at 00:15

2 Answers2

3

The marshaler is probably choking on the MarshalAs(UnmanagedType.LPStr). You're either going to have to change the signature to a fixed size byte[] and do the string conversion in managed code using Encoding.ASCII.GetString(), or you could use an IntPtr type and allocate the memory using Marshal.AllocHGlobal/FreeHGlobal and deal with the string conversion in your code.

I think this might work..

        private const int MAX_STRING = 256;

    [DllImport("IDTechReader.dll", EntryPoint = "ReadTwoBlocks")]
    private static extern void _ReadTwoBlocks(
       byte[] pathConfig,
       [Out] byte[] datablock1,
       [Out] byte[] datablock2,
       int TimeOut,
       ref bool result);


    public void ReadTwoBlocks(string pathConfig,
                              StringBuilder datablock1,
                              StringBuilder datablock2,
                              int TimeOut,
                              ref bool result)
    {
        var pathConfigBuff = new byte[MAX_STRING];
        var datablock1Buff = new byte[MAX_STRING];
        var datablock2Buff = new byte[MAX_STRING];

        // Convert unicode string to null terminated single byte charater string
        Array.Copy(Encoding.ASCII.GetBytes(pathConfig), pathConfigBuff, pathConfig.Length);

        // Call your native method
        _ReadTwoBlocks(pathConfigBuff, datablock1Buff, datablock2Buff, TimeOut, ref result);

        // If success, copy the datablocks to the StringBuffers
        if (result)
        {
            datablock1.Append(Encoding.ASCII.GetString(datablock1Buff, 0, MAX_STRING).Replace('\0', ' ').Trim());
            datablock2.Append(Encoding.ASCII.GetString(datablock2Buff, 0, MAX_STRING).Replace('\0', ' ').Trim());
        }
    }
Damon8or
  • 527
  • 2
  • 11
  • You're probably going to have to `Trim()` the `ASCII.GetString` result, otherwise it ends up with a load of '\0' characters on it. – ctacke Nov 11 '11 at 17:36
  • Yeah, I saw that after the post. Also the first parameter doesn't need to be sent in a fixed buffer. @ctacke Do you know if the out params should be by ref? – Damon8or Nov 12 '11 at 00:10
  • A `byte[]` parameter (or any array param for that mater) automatically goes by ref. Adding `out` or `ref` to it (which is actually identical) would make it a double pointer, which is not what you'd want in this case. – ctacke Nov 12 '11 at 17:39
1

It would be something like this:

[DllImport("IDTechReader.dll")]
private static extern void ReadTwoBlocks(IntPtr pathConfig, 
                IntPtr datablock1, IntPtr datablock2, int timeout, ref bool ret);

and when you use it like this:

string pathConfig = "..\program.ini";
IntPtr ptrPathConfig = IntPtr.Zero;
ptrPathConfig = Marshal.StringToHGlobalAnsi(pathConfig);
IntPtr ptrDatablock1 = IntPtr.Zero;
IntPtr ptrDatablock2 = IntPtr.Zero;
int timeout = 300;
bool ret = false;
ReadTwoBlocks(ptrPathConfig, ptrDatablock1, ptrDatablock2, timeout, ref ret);

string db1 = Marshal.PtrToStringAnsi(ptrDatablock1);
string db2 = Marshal.PtrToStringAnsi(ptrDatablock2);
garzanti
  • 2,162
  • 1
  • 22
  • 30
  • This looks like a good start, but compact framework doesn't have PtrToStringAnsi, and I think you need to allocate the ptrDatablock1/2 using Marshal.AllocHGlobal(), and free them using Marshal.FreeHGlobal() in a finally clause. – Damon8or Nov 10 '11 at 23:45
  • @Damon8or Yes you are write, WinCE is Unicode, and it doesn't have PtrToStringAnsi, but he can still work with char* datatype, so I should allocate the memory as you said. – garzanti Nov 11 '11 at 06:33
  • I have actuall got another version of the native code that compiles and works for the CE.. and it seems I may noot need to do the Marshaling at all..for now Thanks. – saroll Nov 11 '11 at 17:54