Re: am having a problem with pinvoke and StringBuilder[ ]
- From: "Willy Denoyette [MVP]" <willy.denoyette@xxxxxxxxxx>
- Date: Wed, 30 Apr 2008 22:49:31 +0200
If you don't have the source of the DLL then you are in a world of pain, that means you wont be able to use the interop marshaler, you need to "custom" marshal.
The function takes a char*** boards, that means boards is a pointer to a pointer to a pointer to a char.
And the function returns a count of boards, so my guess is that the final pointer points to buffer of zero terminated char arrays, like this:
VGA\0NIC\0....
What you need to do is pass a ref to an IntPtr that points to a buffer that holds an IntPtr pointing to a buffer.
On return you know by means of the returned boards count how many zero terminated strings you have in the buffer.
Following is a working sample illustrating the process.
// C# test.cs
using System;
using System.Text;
using System.Runtime.InteropServices;
class CallDll
{
[DllImport("TestLib.dll", CharSet = CharSet.Ansi)]
public static extern int EnumerateBoards(ref int numBoards, ref IntPtr ptr);
public static void Main()
{
int numBoards = 0;
int maxBoards = 5;
IntPtr boards = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IntPtr)));
IntPtr ptr = Marshal.AllocHGlobal(100); // allocate buffer in unmanaged large enough to hold all strings returned
Marshal.WriteIntPtr(boards, ptr);
EnumerateBoards(ref numBoards, ref boards);
Console.WriteLine("Found " + numBoards + " boards");
string[] sa = new string[numBoards];
for(int i = 0; i < numBoards; i++)
{
sa[i] = Marshal.PtrToStringAnsi(ptr);
Console.WriteLine("{0}", sa[i]);
ptr = new IntPtr(ptr.ToInt32() + sa[i].Length + 1); // new offset including string terminator
}
}
}
// C++ file testlib.cpp
#include <string.h>
extern "C" __declspec(dllexport) int __stdcall EnumerateBoards(int *numBoards, char ***boards)
{
strcpy_s(static_cast<char*>(**boards), 4, "VGA\0");
strcpy_s(static_cast<char*>(**boards + 4), 4, "NIC\0");
strcpy_s(static_cast<char*>(**boards + 8), 6, "SOUND\0");
*numBoards = 3;
return 0;
}
Willy.
"LK" <lkant2000@xxxxxxxxx> wrote in message news:Oz%23dN6nqIHA.1768@xxxxxxxxxxxxxxxxxxxxxxx
Hi Willy,
thanks a lot for your feedback.
1) I tried out your suggestions and that did not work either.
Although I dont get NullPointerExceptions, no data gets
passed between managed and unmanaged code. Here is
the output from my modified code.
c# b4: board0: 1111111111111111111
c# b4: board1: 2222222222222222222
DLL: Hello from TestLib.dll
DLL: board0: +4ù (prints junk instead of 1111111....)
DLL: board1: +4ù (prints junk instead of 2222222....)
c# Found 2 boards
c# after: board0: 1111111111111111111 (should actually print VGA )
c# after: board1: 2222222222222222222 (should actually print NIC)
2) Unfortunately, I dont have the source code for the thirdparty DLL and
it does take a char *** argument. Is there any way that I can pass a
char *** argument from C# to a DLL.
again, thanks for your help.
LK
"Willy Denoyette [MVP]" <willy.denoyette@xxxxxxxxxx> wrote in message news:uulFLphqIHA.4928@xxxxxxxxxxxxxxxxxxxxxxx"LK" <lkant2000@xxxxxxxxx> wrote in message news:u0RgHEhqIHA.524@xxxxxxxxxxxxxxxxxxxxxxxHi,
From a C# program I need to access an existing DLL that enumerates the
boards detected via a hardware probe of the system. The function that
does the enumeration expects an int * where it stores the number of boards
detected and a char *** where it stores the name of each board detected.
Since the String class is immutable, I have used StringBuilder in my code
but this results in a NullPointerException. Just for testing, I replaced
StriingBuilder[] with String[] and the code worked just fine (i.e, no
NullPointerExceptions occured and all messages populated in
the String[] in managed code was correctly printed by the DLL).
I am wondering what I am doing wrong in my code. For reference, I am
enclosing my C# code and my sample DLL code
thank you for your help.
Laxmikant Rashinkar (LK)
Here is the C# code
---------------------
using System;
using System.Text;
using System.Runtime.InteropServices;
class CallDll
{
[DllImport("TestLib.dll")]
public static extern void AccessCheck();
[DllImport("TestLib.dll")]
public static extern int EnumerateBoards(ref int numBoards, ref StringBuilder[] boards);
public static void Main()
{
int numBoards = 0;
int maxBoards = 5;
int i;
StringBuilder[] boards = new StringBuilder[maxBoards];
for(i=0; i<maxBoards; i++)
boards[i] = new StringBuilder(100);
AccessCheck();
EnumerateBoards(ref numBoards, ref boards);
Console.WriteLine("Found " + numBoards + " boards\n");
for(i=0; i<numBoards; i++)
Console.WriteLine("" + boards[i] + "\n");
}
}
Here is the DLL code
----------------------
#include <stdio.h>
#include <string.h>
extern "C" __declspec(dllexport) void AccessCheck()
{
printf("Hello from TestLib.dll\n");
}
extern "C" __declspec(dllexport) int EnumerateBoards(int *numBoards, char ***boards)
{
char **cpp = *boards;
strcpy(cpp[0], "VGA");
strcpy(cpp[1], "NIC");
*numBoards = 2;
return 0;
}
Here is the output from my program:
------------------------------------
Hello from TestLib.dll
Unhandled Exception: System.NullReferenceException: Object reference not set
to an instance of an object.
at CallDll.EnumerateBoards(Int32& numBoards, StringBuilder[]& boards)
at CallDll.Main()
And here is how I compile my test code
----------------------------------------
@echo off
cl TestLib.cpp -LD -FeTestLib.dll
csc CallDll.cs
CallDll
Why the triple (***) indirection, a stringBuilder is passed as a pointer and a StringBuilder[] is passed as a pointer to a pointer?
..
extern "C" __declspec(dllexport) int EnumerateBoards(int *numBoards, char **boards)
{
char **cpp = boards;
..
Remove the ref from the DllImport declaration and the function call in your C# code and it should work.
...
public static extern int EnumerateBoards(ref int numBoards, StringBuilder[] boards);
EnumerateBoards(ref numBoards, boards);
Willy.
.
- References:
- am having a problem with pinvoke and StringBuilder[ ]
- From: LK
- Re: am having a problem with pinvoke and StringBuilder[ ]
- From: Willy Denoyette [MVP]
- Re: am having a problem with pinvoke and StringBuilder[ ]
- From: LK
- am having a problem with pinvoke and StringBuilder[ ]
- Prev by Date: Re: View windows forms control in a web page
- Previous by thread: Re: am having a problem with pinvoke and StringBuilder[ ]
- Next by thread: Re: am having a problem with pinvoke and StringBuilder[ ]
- Index(es):
Relevant Pages
|