Convert IntPtr to char** in C# - c#

I'd like to interpret the output of the following unmanaged function:
afc_error_t afc_get_device_info (afc_client_t client, char ***device_information)
I import the dll with the code:
[DllImport("libimobiledevice.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern short afc_get_device_info(IntPtr client, out IntPtr info);
As long as I only needed to convert response to string Marshal.PtrToStringAnsi was okay. However I have no idea how to convert that IntPtr back to char array.

It should be something like:
IntPtr di;
int result = afc_read_directory(client, #"C:\", out di);
if (di == IntPtr.Zero)
{
throw new Exception();
}
IntPtr di2 = di;
while (true)
{
IntPtr ptr = Marshal.ReadIntPtr(di2);
if (ptr == IntPtr.Zero)
{
break;
}
string str = Marshal.PtrToStringAnsi(ptr);
if (str == string.Empty)
{
break;
}
di2 = di2 + IntPtr.Size;
}
Try if it works, then I'll explain how...
important you are leaking memory here...
I've found this example in C:
char **dirs = NULL;
afc_read_directory(afc, "/eafaedf", &dirs);
if (!dirs)
afc_read_directory(afc, "/", &dirs);
printf("Directory time.\n");
for (i = 0; dirs[i]; i++) {
printf("/%s\n", dirs[i]);
free(dirs[i]);
}
if (dirs)
free(dirs);
you are responsible for freeing the memory (see the free inside the cycle and the final free?). In this case (and for other methods that return arrays of C-strings you can use afc_dictionary_free. Note that other methods like afc_receive_data that return a single block of memory you can't use it.

Related

I can't get the input parameter of a PInvoke to C++ DLL from C# to be used as ouput with IntPtr

I have a function in a C++ DLL that takes one input. I'm trying to have that input be used as an output to the C# call.
Here is my C++ function:
MYAPI int testStuff3(unsigned char* str)
{
printf("%s\n", str);
str = (unsigned char*)malloc(9);
str[0] = 'G';
str[1] = 'o';
str[2] = 'o';
str[3] = 'd';
str[4] = 'b';
str[5] = 'y';
str[6] = 'e';
str[7] = '!';
str[8] = '\0';
return 1;
}
Here is the C# code:
public class Program
{
[DllImport("NativeLib.dll")]
private static extern int testStuff3([In, Out] IntPtr str);
static void Main(string[] args)
{
IntPtr junk3 = IntPtr.Zero;
int ret = testStuff3(junk3);
Byte[] stuff3 = new byte[9];
Marshal.Copy(junk3, stuff3, 0, 9);
}
}
When the Marshal.Copy is called, it gives an error saying that the source (junk3) can not be null.
Will this not work, sending a null pointer to C++ DLL from C# and having the DLL allocate the memory and store something inside and return it to the caller? I want to keep it an IntPtr and not a StringBuilder because the data won't necessarily be a string in the final code. Just an unsigned char array in C++ and I want the IntPtr to point to it.
I've tried different variations of [In, Out], [Out], out and ref for the IntPtr passing.
Never ever allow memory allocations to cross a DLL boundary. That way lies madness, and/or Sparta.
(For the pedantic: you can allocate memory and then pass a pointer across, as long as you either pass ownership back to free it, or guarantee that the same allocator is used as part of a contract. But it's still something to avoid when possible.)
Typically to use a string output parameter you should pass a StringBuilder as the argument, setting its capacity to the maximum expected length. Then in the native code you simply fill this existing buffer.
See the "Fixed length string buffers" section here for an example.
Thanks for the help!
Here's what I ended up with.
C++ function:
MYAPI int testStuff4(wchar_t* str)
{
unsigned char* stuff = (unsigned char*)malloc(10);
stuff[0] = 'G';
stuff[1] = 'o';
stuff[2] = 'o';
stuff[3] = 'd';
stuff[4] = 'b';
stuff[5] = 'y';
stuff[6] = 'e';
stuff[7] = '!';
stuff[8] = '\0';
mbstowcs(str, (const char*)stuff, 1024);
free(stuff);
return 1;
}
C# function:
public class Program
{
[DllImport("NativeLib.dll")]
private static extern int testStuff4(IntPtr str);
static void Main(string[] args)
{
IntPtr junk4 = Marshal.AllocHGlobal(1024);
int ret = testStuff4(junk4);
string junkString = Marshal.PtrToStringUni(junk4);
Console.WriteLine(junkString);
Marshal.FreeHGlobal(junk4);
}
}
Your C++ function doesn’t modify the passed string. It allocates a new one with malloc, stores it in a local variable forgetting the passed value, then returns leaking the memory.
If for some reason you want to do manual marshalling, you probably want something like this (assuming this is for Windows):
MYAPI BOOL __stdcall testStuff3( char** pp )
{
if( nullptr == pp )
return FALSE; // null pointer
if( nullptr != *pp )
{ // Print & release an old string
printf( "%s\n", *pp );
CoTaskMemFree( *pp );
*pp = nullptr;
}
// Allocate a new one
const char* const str = CoTaskMemAlloc( 9 );
if( nullptr == str ) return FALSE;
strncpy( str, "Goodbye!", 9 );
*pp = str;
return TRUE;
}
C#:
public class Program
{
[DllImport( "NativeLib.dll" )]
private static extern bool testStuff3( [In, Out] ref IntPtr str );
static void Main( string[] args )
{
IntPtr ptr = IntPtr.Zero;
if( testStuff3( ref ptr ) )
{
Console.WriteLine( Marshal.PtrToStringAnsi( ptr ) );
Marshal.FreeCoTaskMem( ptr );
}
}
}
However, this is not something I recommend doing unless you have very good reasons. In most cases automatic marshalling is better. For C# -> C++ way it’s trivially simple, const char* or const wchar_t* in C++, string (with correct attributes) in C#. For C++ -> C# you can allocate a StringBuilder in C#, pass char* or wchar_t* to C++, and buffer length in another argument.

PInvoke FbwfFindFirst - FbwfCacheDetail problems

I'm trying to create a PInvoke for FbwfFindFirst and am struggling with the struct FbwfCacheDetail.
In short, I'm not sure how to marshal WCHAR fileName[1]; seeing as it's a variable length array and a non-null terminated.
Any help would be welcomed
Since the whole structure is of variable size, one way to do this is like this (I can't test it because I don't have this dll on my system):
string volume = "";
int size = 0;
// ask for whole structure size
FbwfFindFirst(volume, IntPtr.Zero, ref size); // this call should return ERROR_MORE_DATA which is ok
// allocate for the structure
var ptr = Marshal.AllocHGlobal(size);
try
{
FbwfFindFirst(volume, ptr, ref size); // should not return error
// get the easy part
var detail = Marshal.PtrToStructure<FbwfCacheDetail>(ptr);
// compute filename offset and get the string
// file name length is in bytes, per documentation
var fileName = Marshal.PtrToStringUni(ptr + Marshal.OffsetOf<FbwfCacheDetail>("fileName").ToInt32(), detail.fileNameLength / 2);
}
finally
{
Marshal.FreeHGlobal(ptr);
}
[DllImport("fbwflib", CharSet = CharSet.Unicode)]
static extern int FbwfFindFirst(string volume, IntPtr cacheDetail, ref int size);
[StructLayout(LayoutKind.Sequential)]
struct FbwfCacheDetail
{
public int cacheSize;
public int openHandleCount;
public int fileNameLength;
byte fileName; // don't use this member
}
Simon Mourier's answer is 99% correct, and with normal APIs it would have definitely worked, but it appears as if this particular API doesn't follow "normal rules", whatever that might be ;). As such, I needed to modify a couple things and this is what worked for me:
const int ERROR_INSUFFICIENT_BUFFER = 122;
const int ERROR_MORE_DATA = 234;
var volume = "C:";
var fileName = string.Empty;
var size = 0;
while (true)
{
var ptr = Marshal.AllocHGlobal(size); // FbwfFindFirst fails if given IntPtr.Zero - regardless of what the value of size is.
try
{
var result = FbwfFindFirst(volume, ptr, ref size);
// Despite documentation saying otherwise, it can return either of these
if (result == ERROR_MORE_DATA || result == ERROR_INSUFFICIENT_BUFFER)
{
continue;
}
if (result != 0)
{
throw new Exception($"Failed with {result}");
}
// get the easy part
var detail = (FbwfCacheDetail) Marshal.PtrToStructure(ptr, typeof(FbwfCacheDetail));
// compute filename offset and get the string
// file name length is in bytes, per documentation
fileName = Marshal.PtrToStringUni(ptr + Marshal.OffsetOf(typeof(FbwfCacheDetail), "fileName").ToInt32(), detail.fileNameLength / 2);
break;
}
finally
{
Marshal.FreeHGlobal(ptr);
}
}
EDIT
Forgot to say that the pinvoke for FbwfFindFirst needs to be as follows, else it will return ERROR_INVALID_PARAMETER:
[DllImport("fbwflib.dll")]
public static extern uint FbwfFindFirst(
[MarshalAs(UnmanagedType.LPWStr)]
string volume,
IntPtr cacheDetail,
ref int size
);

Marshalling wchar_*t via pinvoke in c# does not return its value back to StringBuilder

First of all I want to say that I know enough about c++ and pinvoke to be frustrated.
I am currently working on a c++ wrapper and pinvoke that is not returning the info I need.
Below are the pieces of what I am doing:
I have a c++ method with the signature of:
DllExport int LookupFamilyName(__in PCWSTR familyName,__out_opt wchar_t* result, int lengthIn)
With the internal code:
UINT32 count = 0;
UINT32 length = 0;
LONG rc = GetPackagesByPackageFamily(familyName, &count, NULL, &length, NULL);
if (rc == ERROR_SUCCESS)
{
return int(rc);
}
else if (rc != ERROR_INSUFFICIENT_BUFFER) //122
{
return int(rc);
}
PWSTR *fullNames = (PWSTR *) malloc(count * sizeof(*fullNames));
if (fullNames == NULL)
{
return 700010; //faile to allocate memeory
}
PWSTR buffer = (PWSTR) malloc(length * sizeof(WCHAR));
if (buffer == NULL)
{
return 700011; //faile to allocate memeory
}
rc = GetPackagesByPackageFamily(familyName, &count, fullNames, &length, buffer);
if (rc != ERROR_SUCCESS)
{
return int(rc);
}
else
{
result = new wchar_t[lengthIn];
for (UINT32 i = 0; i < count; ++i)
{
wcscpy_s(result,lengthIn,fullNames[i]);
}
}
free(buffer);
free(fullNames);
I am then using the following to invoke:
[DllImport("ClassLibrary1.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int LookupFamilyName(string familyName, ref StringBuilder sbPackageFullName, int len);
And then I am calling it from C# with the following:
string packagename = "Microsoft.MicrosoftEdge_8wekyb3d8bbwe";
StringBuilder showmethemoney = new StringBuilder(64);
int x = LookupFamilyName(packagename, ref showmethemoney,showmethemoney.Capacity);
I'm passing in a StringBuilder for the wchar_t* and that value is filled in with what I want by the time we get to the end of the LookupFamilyName method but when we get back to the c# code, the StringBuilder does not have the value that the wchar_t* had in the c++ code.
For those of you that know far more then I, what do I need to do to make sure that the the value in the wchar_t* result gets passed back to the StringBuilder?

Cast LPVOID to struct

I want to read the arguments I have send through CreateRemoteThread to my injected DLL inside another process.
I can call the function without problem, I just don't know how to cast LPVOID to a struct.
This is a example:
#pragma pack(push,1)
struct tagRemoteThreadParams
{
int Param1;
int Param2;
} RemoteThreadParams, *PRemoteThreadParams;
#pragma pack(pop)
DWORD WINAPI testfunction(LPVOID param)
{
// cast LPVOID to tagRemoteThreadParams (param)
WriteToLog("YES YOU CALLED THE FUNCTION WITH PARAM: ");
return 0;
}
This is my struct and how I have allocated the mem inside the process:
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct RemoteThreadParams
{
[MarshalAs(UnmanagedType.I4)]
public int Param1;
[MarshalAs(UnmanagedType.I4)]
public int Param2;
}
public uint CallFunction(int _arg1)
{
RemoteThreadParams arguments = new RemoteThreadParams();
arguments.Param1 = 1;
arguments.Param2 = 2;
//pointer to the function im trying to call
IntPtr _functionPtr = IntPtr.Add(this.modulePtr, 69772);
// Allocate some native heap memory in your process big enough to store the
// parameter data
IntPtr iptrtoparams = Marshal.AllocHGlobal(Marshal.SizeOf(arguments));
// Copies the data in your structure into the native heap memory just allocated
Marshal.StructureToPtr(arguments, iptrtoparams, false);
//allocate som mem in remote process
IntPtr lpAddress = VirtualAllocEx(this.processHandle, IntPtr.Zero, (IntPtr)Marshal.SizeOf(arguments), AllocationType.Commit | AllocationType.Reserve, MemoryProtection.ExecuteReadWrite);
if (lpAddress == IntPtr.Zero)
{
return 0;
}
if (WriteProcessMemory(this.processHandle, lpAddress, iptrtoparams, (uint)Marshal.SizeOf(arguments), 0) == 0)
{
return 0;
}
//Free up memory
Marshal.FreeHGlobal(iptrtoparams);
uint threadID = 0;
IntPtr hThread = CreateRemoteThread(this.processHandle, IntPtr.Zero, 0, _functionPtr, lpAddress, 0, out threadID);
if (hThread == IntPtr.Zero)
{
//throw new ApplicationException(Marshal.GetLastWin32Error().ToString());
throw new Win32Exception();
}
WaitForSingleObject(hThread, 0xFFFFFFFF);
// wait for thread to exit
// get the thread exit code
uint exitCode = 0;
GetExitCodeThread(hThread, out exitCode);
// close thread handle
CloseHandle(hThread);
return exitCode;
}
If I understand your code correctly, you inject UT8 encoded string into the other's process memory (I'm kind of surprised that it works).
Assuming it does work, in your C++ code you need to convert the UTF8 encoded byte array pointed by param to some kind of string that C++ understands.
One way to do it is to use MultiByteToWideChar
Another way is to use STL. I found a question about it here.
And the answer to the cast problem was:
struct tagRemoteThreadParams *tData = (struct tagRemoteThreadParams *)param;
Thank you for the help guys

Japanese to Romaji with Kakasi

I want to transliterate Japanese to Romaji with Kakasi tool, using C#. For this, I created a wrapper:
[DllImport("kakasi.dll")]
static extern int kakasi_getopt_argv(int size, IntPtr param);
[DllImport("kakasi.dll")]
static extern IntPtr kakasi_do([MarshalAs(UnmanagedType.LPStr)]string str);
public static void SetParams(string [] paramz)
{
kakasi_getopt_argv(paramz.Length, StringToIntPtr(paramz));
}
public static string DoKakasi(string japanese)
{
return Marshal.PtrToStringAuto(kakasi_do(japanese));
}
private static IntPtr StringToIntPtr(string[] strings)
{
int bytesCount;
IntPtr ptr = IntPtr.Zero;
ArrayList stringBytes = new ArrayList();
foreach (string str in strings)
{
stringBytes.AddRange(Encoding.Unicode.GetBytes(str));
stringBytes.Add((byte)'\0');
}
bytesCount = stringBytes.Count;
try
{
ptr = Marshal.AllocHGlobal(bytesCount);
Marshal.Copy((byte[])stringBytes.ToArray(typeof(byte))
, 0
, ptr
, bytesCount);
return ptr;
}
catch
{
if (ptr != IntPtr.Zero)
Marshal.FreeHGlobal(ptr);
throw;
}
}
And then:
KakasiCs.SetParams(new[] { "kakasi", "-ja", "-ga", "-ka", "-Ea", "-Ka", "-Ha", "-Ja", "-U", "-s",});
var x = KakasiCs.DoKakasi("さかき");
I have 2 problems:
Bad output - I receive no romaji, but something strange: "㼿?Äꈎᅵ鄠".
In VS2010 every time I receive a warning with PInvokeStackImbalance exception.
Any help is appreciated. Thanks.
I have used this library(only with c++ builder). Before passing the string to the kakasi, you should konvert string to the SHIFT-JIS code page. After processing convert it back to the unicode.
Here the code that I use
...
char*shift_jis=CodePageConverter::fromUnicode(932,InputTextBox->Text.c_bstr());
char*converted_text=ProcessText(shift_jis);
OutputTextBox->Text=CodePageConverter::toUnicode(932,converted_text);
...
char* TForm1::ProcessText(char*string)
{
int paramscount=0;
char**argv=CreateParameters(paramscount);
kakasi_getopt_argv(paramscount, argv);
char*result=kakasi_do(string);
DeleteArguments(argv,paramscount);
return result;
}
...

Categories

Resources