I have a C# function, a callback, called from a Win32 DLL written in C++. The caller gives me a UTF8 string, but I can't receive it properly, all the hungarian special characters go wrong.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int func_writeLog(string s);
When I changed the parameter type to IntPtr, and wrote the code, it writes properly. But I find this is a very slow solution:
byte[] bb = new byte[1000];
int i = 0;
while (true)
{
byte b = Marshal.ReadByte(pstr, i);
bb[i] = b;
if (b == 0) break;
i++;
}
System.Text.UTF8Encoding encodin = new System.Text.UTF8Encoding();
var sd = encodin.GetString(bb, 0, i);
I tried to write some attribute to string parameter, like:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int func_writeLog([In, MarshalAs(UnmanagedType.LPTStr)] string s);
no one was working. Any advice please? Thanks in advance!
There's no decent way to do this fast in pure managed code, it always requires copying the string and that's very awkward because you don't know the required buffer size. You'll want to pinvoke a Windows function to do this for you, MultiByteToWideChar() is the work-horse converter function. Use it like this:
using System.Text;
using System.Runtime.InteropServices;
...
public static string Utf8PtrToString(IntPtr utf8) {
int len = MultiByteToWideChar(65001, 0, utf8, -1, null, 0);
if (len == 0) throw new System.ComponentModel.Win32Exception();
var buf = new StringBuilder(len);
len = MultiByteToWideChar(65001, 0, utf8, -1, buf, len);
return buf.ToString();
}
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern int MultiByteToWideChar(int codepage, int flags, IntPtr utf8, int utf8len, StringBuilder buffer, int buflen);
Related
I've read most of the hints but I can't get it to work.
I have a native C dll with this prototype:
int utl_Conv_HexString(U8 u8_Mode, void* DataIn, void* DataOut, int *piInOutLen, int maxOutLen);
This dll converts several string formats in byte arrays:
The dll is used in a system with unmanaged code (written in C)
Now I would like to use this dll in a C# / WPF Enviroment.
I still use other dll's in C#, but all have prototypes with no void*.
Examples from C:
//ByteArr to Telegramm
u8_Dst[0] = 0xAA;
u8_Dst[1] = 0xBB;
u8_Dst[2] = 0xCC;
u32_InOutLen = 3;
s32_res = utl_Conv_HexString(UTL_CONV_BYTEARR_TO_TELEGRAM, u8_Dst, ac8_Src, &u32_InOutLen, sizeof(ac8_Src));
or
strcpy(ac8_Src, "0xAA,0xBB,0xCC");
memset(u8_Dst, 0, sizeof(u8_Dst));
s32_res = utl_Conv_HexString(UTL_CONV_TELEGRAM_TO_BYTEARR, ac8_Src, u8_Dst, &u32_InOutLen, sizeof(u8_Dst));
My problem is that I can not figure out how this could be used in C#
You should use it like:
public enum U8
{
UTL_CONV_BYTEARR_TO_TELEGRAM = 1, // TODO
UTL_CONV_TELEGRAM_TO_BYTEARR = 2,
}
(you will have to put here your constants...)
[DllImport("SomeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int utl_Conv_HexString(U8 u8Mode, byte[] dataIn, byte[] dataOut, ref int piInOutLen, int maxOutLen);
(note that the CallingConvention could be StdCall... you'll have to check your code)
and then:
byte[] src = Encoding.UTF8.GetBytes("0xAA, 0xBB, 0xCC");
byte[] dest = new byte[64];
int lenSrc = src.Length;
int res = utl_Conv_HexString(U8.UTL_CONV_TELEGRAM_TO_BYTEARR, src, dest, ref lenSrc, dest.Length);
The void* is normally translated to a byte[].
I have dll with function:
extern "C"
int
doJob(char** buffer);
Its usage with C++ looks like this:
char* buf;
int status = doJob(&buf);
What definition should I have for this function in C#?
How can I use this function in C#?
One of the possible patterns is:
[DllImport("containsdojob.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 doJob(out IntPtr buffer);
[DllImport("containsdojob.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void freeMemory(IntPtr buffer);
and
IntPtr buffer = IntPtr.Zero;
string str = null;
try
{
doJob(out buffer);
if (buffer != IntPtr.Zero)
{
str = Marshal.PtrToStringAnsi(buffer);
}
}
finally
{
if (buffer != IntPtr.Zero)
{
freeMemory(buffer);
}
}
Note that you'll need a freeMemory method to free the memory allocated by doJob.
There are other possible patterns, for example based on BSTR and SysAllocString that are easier to implement C#-side (but more difficult to implement C-side)
The "pattern" for using BSTR:
C-side:
char *str = "Foo"; // your string
int len = strlen(str);
int wslen = MultiByteToWideChar(CP_ACP, 0, str, len, 0, 0);
BSTR bstr = SysAllocStringLen(NULL, wslen);
MultiByteToWideChar(CP_ACP, 0, str, len, bstr, wslen);
// bstr is the returned string
C#-side:
[DllImport("containsdojob.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern Int32 doJob([MarshalAs(UnmanagedType.BStr)] out string buffer);
string str;
doJob(out str);
The memory is automatically handled (freed) by the CLR.
If you are using Visual C++ you can even
char *str = "Foo"; // your string
_bstr_t bstrt(str);
BSTR bstr = bstrt.Detach();
// bstr is the returned string
Or C-side you could use one of the two allocators that can be freed C#-side: LocalAlloc or CoTaskMemAlloc:
char *str = "Foo"; // your string
char *buf = (char*)LocalAlloc(LMEM_FIXED, strlen(str) + 1);
// or char *buf = (char*)CoTaskMemAlloc(strlen(str) + 1);
strcpy(buf, str);
// buf is the returned string
Then you use the first example, but instead of calling
freeMemory(buffer);
you call:
Marshal.FreeHGlobal(buffer); // for LocalAlloc
or
Marshal.FreeCoTaskMem(buffer); // for CoTaskMemAlloc
I want to use Ghostscript in a .NET / C# application to convert a .tiff file to PDF.
My problem: When the file path contains non-ansi characters (e.g. Umlaute), the function
gsapi_init_with_args
fails. (With GS 8.x, it works fine!).
I found information that the behaviour was changed in 9.x, and I also found a function called
gsapi_init_with_argsW
And this function should work with .NET without any problems (see http://permalink.gmane.org/gmane.comp.printing.ghostscript.cvs/31721)
So I use the following DLLImport:
[DllImport(#"gsdll32.dll")]
public static extern int gsapi_init_with_argsW( IntPtr instace, int argc, string[] argv);
but this still does not work, I get the error:
Error: /undefinedfilename
in (C:\\304NDERUNGEN\\TEST.PDF)
The name of the file schould be
C:\\ÄNDERUNGEN\\TEST.PDF
so the umlaut "Ä" is not recognized correctly.
I´ve search the web a lot but did not found a solution.
Any idea?
Thank you!
I suspect that you will need to use UTF-8 here. Make a call to gs_set_arg_encoding passing GS_ARG_ENCODING_UTF8.
Any strings that you pass to Ghostscript should be declared as IntPtr. To convert from a C# string to a null-terminated UTF-8 encoded string use this function provided by Hans Passant:
public static IntPtr NativeUtf8FromString(string managedString)
{
int len = Encoding.UTF8.GetByteCount(managedString);
byte[] buffer = new byte[len + 1]; // null-terminator allocated
Encoding.UTF8.GetBytes(managedString, 0, managedString.Length, buffer, 0);
IntPtr nativeUtf8 = Marshal.AllocHGlobal(buffer.Length);
Marshal.Copy(buffer, 0, nativeUtf8, buffer.Length);
return nativeUtf8;
}
Make sure that you remember to clean up with a call to Marshal.FreeHGlobal.
The overall code might look a little like this:
public class Ghostscript
{
public const int GS_ARG_ENCODING_LOCAL = 0;
public const int GS_ARG_ENCODING_UTF8 = 1;
[DllImport("gsdll32.dll")]
private static extern int gsapi_new_instance(out IntPtr inst, IntPtr handle);
[DllImport("gsdll32.dll")]
private static extern int gsapi_set_arg_encoding(IntPtr inst, int encoding);
[DllImport("gsdll32.dll")]
private static extern int gsapi_init_with_args(IntPtr inst, int argc, IntPtr[] argv);
[DllImport("gsdll32.dll")]
private static extern int gsapi_exit(IntPtr inst);
[DllImport("gsdll32.dll")]
private static extern void gsapi_delete_instance(IntPtr inst);
private static void checkReturnValue(int retval)
{
if (retval != 0)
throw ...; // implement error handling here
}
public static void run(string[] argv)
{
IntPtr inst;
checkReturnValue(gsapi_new_instance(out inst, IntPtr.Zero));
try
{
IntPtr[] utf8argv = new IntPtr[argv.length];
for (int i=0; i<utf8argv.Length; i++)
utf8argv[i] = NativeUtf8FromString(argv[i]);
try
{
checkReturnValue(gsapi_set_arg_encoding(inst, GS_ARG_ENCODING_UTF8));
checkReturnValue(gsapi_init_with_args(inst, utf8argv.Length, utf8argv));
checkReturnValue(gsapi_exit(inst));
finally
{
for (int i=0; i<utf8argv.Length; i++)
Marshal.FreeHGlobal(utf8argv[i]);
}
}
finally
{
gsapi_delete_instance(inst);
}
}
}
I am using the method below to read bytes in memory. I want to read values in memory addresses which are very near each other. Previously I have been making individual calls for each byte in memory and adding the result to an array using a for loop. This became really inefficient, so instead I want to adapt the below code to read a large block of memory and then try to do an itteration through the array to pull out the bytes I want. I have spent a bit of time trying to work it out, but really struggling. FYI, this method reads a pointer, and then if that value is a pointer, it reads that pointer, and so forth until it gets to the static address then reads the byte value at that address.
[DllImport("kernel32", EntryPoint = "ReadProcessMemory")]
private static extern byte ReadProcessMemoryByte(int Handle, int Address, ref byte Value, int Size, ref int BytesRead);
public static byte ReadPointerByte(string EXENAME, int Pointer, int[] Offset)
{
byte Value = 0;
checked
{
try
{
Process[] Proc = Process.GetProcessesByName(EXENAME);
if (Proc.Length != 0)
{
int Bytes = 0;
int Handle = OpenProcess(PROCESS_ALL_ACCESS, 0, Proc[0].Id);
if (Handle != 0)
{
foreach (int i in Offset)
{
ReadProcessMemoryInteger((int)Handle, Pointer, ref Pointer, 4, ref Bytes);
Pointer += i;
}
ReadProcessMemoryByte((int)Handle, Pointer, ref Value, 2, ref Bytes);
CloseHandle(Handle);
}
}
}
catch
{ }
}
return Value;
}
What I have so far:
private void label1_Click(object sender, EventArgs e)
{
int[] valuesSeperated[200];
List<byte> PreArray = new List<byte>();
Process[] test = Process.GetProcessesByName("MyProcess"); //Get process handle
int baseAddress = test[0].MainModule.BaseAddress.ToInt32(); //Get base address
byte ReadX = MyClass.ReadPointerByte("MyProcess", BaseAddress, new int[] { 0xc, 0x0, 0x2 }); //call memory reading function (including memory offsets)
PreArray.Add(ReadX);
byte[] PreArrayToInt = PreArray.ToArray();
int[] MYConvertedBytes = PreArray ToInt.Select(x => (int)x).ToArray();
foreach (int i in MYConvertedBytes)
{
valuesSeperated // (don't really know what to do here, if the read was successful I would have a long number at [0], so now need to seperate these as if I had read each one in memory one at a time.
}
string TestString = MYConvertedBytes[0].ToString();
label1.Text = TestString;
}
So to summarize: I don't know how to read a larger block of memory (say 200 addresses at once) using the above method. I don't know how best to extract the values from the resulting array to form a new array that has the bytes now separated. Please ask if anything is unclear, I am quite new and really want to learn so any hints/help would be really appreciated.
Your interop signature looks completely wrong to me.
The c signature is:
BOOL WINAPI ReadProcessMemory(
__in HANDLE hProcess,
__in LPCVOID lpBaseAddress,
__out LPVOID lpBuffer,
__in SIZE_T nSize,
__out SIZE_T *lpNumberOfBytesRead
);
It should be something like:
[DllImport("kernel32", EntryPoint = "ReadProcessMemory",SetLastError=true)]
private static extern unsafe bool NativeReadProcessMemory(IntPtr processHandle, IntPtr baseAddress, byte* buffer, IntPtr size, out IntPtr bytesRead);
static unsafe void ReadProcessMemory(IntPtr processHandle, IntPtr baseAddress, byte[] buffer,int start, int size)
{
fixed(byte* pBuffer=buffer)
{
IntPtr bytesRead;
if(!NativeReadProcessMemory(processHandle, baseAddress, pBuffer+start,(IntPtr)size, out bytesRead))
throw new Win32Exception(Marshal.GetLastWin32Error());
if((int)bytesRead!=size)
throw new Exception("Incomplete read");//User better exception type here
}
}
I've successfully read a PE header from an unmanaged module loaded into memory by another process. What I'd like to do now is read the names of this module's exports. Basically, this is what I have so far (I've left out a majority of the PE parsing code, because I already know it works):
Extensions
public static IntPtr Increment(this IntPtr ptr, int amount)
{
return new IntPtr(ptr.ToInt64() + amount);
}
public static T ToStruct<T>(this byte[] data)
{
GCHandle handle = GCHandle.Alloc(data, GCHandleType.Pinned);
T result = (T)Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return result;
}
public static byte[] ReadBytes(this Process process, IntPtr baseAddress, int size)
{
int bytesRead;
byte[] bytes = new byte[size];
Native.ReadProcessMemory(process.Handle, baseAddress, bytes, size, out bytesRead);
return bytes;
}
public static T ReadStruct<T>(this Process process, IntPtr baseAddress)
{
byte[] bytes = ReadBytes(process, baseAddress, Marshal.SizeOf(typeof(T)));
return bytes.ToStruct<T>();
}
public static string ReadString(this Process process, IntPtr baseAddress, int size)
{
byte[] bytes = ReadBytes(process, baseAddress, size);
return Encoding.ASCII.GetString(bytes);
}
GetExports()
Native.IMAGE_DATA_DIRECTORY dataDirectory =
NtHeaders.OptionalHeader.DataDirectory[Native.IMAGE_DIRECTORY_ENTRY_EXPORT];
if (dataDirectory.VirtualAddress > 0 && dataDirectory.Size > 0)
{
Native.IMAGE_EXPORT_DIRECTORY exportDirectory =
_process.ReadStruct<Native.IMAGE_EXPORT_DIRECTORY>(
_baseAddress.Increment((int)dataDirectory.VirtualAddress));
IntPtr namesAddress = _baseAddress.Increment((int)exportDirectory.AddressOfNames);
IntPtr nameOrdinalsAddress = _baseAddress.Increment((int)exportDirectory.AddressOfNameOrdinals);
IntPtr functionsAddress = _baseAddress.Increment((int)exportDirectory.AddressOfFunctions);
for (int i = 0; i < exportDirectory.NumberOfFunctions; i++)
{
Console.WriteLine(_process.ReadString(namesAddress.Increment(i * 4), 64));
}
}
When I run this, all I get is a pattern of double question marks, then completely random characters. I know the header is being read correctly, because the signatures are correct. The problem has to lie in the way that I'm iterating over the function list.
The code at this link seems to suggest that the names and ordinals form a matched pair of arrays, counted up to NumberOfNames, and that the functions are separate. So your loop may be iterating the wrong number of times, but that doesn't explain why you're seeing bad strings from the very beginning.
For just printing names, I'm having success with a loop like the one shown below. I think the call to ImageRvaToVa may be what you need to get the correct strings? However I don't know whether that function will work unless you've actually loaded the image by calling MapAndLoad -- that's what the documentation requests, and the mapping did not seem to work in some quick experiments I did using LoadLibrary instead.
Here's the pInvoke declaration:
[DllImport("DbgHelp.dll", CallingConvention = CallingConvention.StdCall), SuppressUnmanagedCodeSecurity]
public static extern IntPtr ImageRvaToVa(
IntPtr NtHeaders,
IntPtr Base,
uint Rva,
IntPtr LastRvaSection);
and here's my main loop:
LOADED_IMAGE loadedImage = ...; // populated with MapAndLoad
IMAGE_EXPORT_DIRECTORY* pIID = ...; // populated with ImageDirectoryEntryToData
uint* pFuncNames = (uint*)
ImageRvaToVa(
loadedImage.FileHeader,
loadedImage.MappedAddress,
pIID->AddressOfNames,
IntPtr.Zero);
for (uint i = 0; i < pIID->NumberOfNames; i++ )
{
uint funcNameRVA = pFuncNames[i];
if (funcNameRVA != 0)
{
char* funcName =
(char*) (ImageRvaToVa(loadedImage.FileHeader,
loadedImage.MappedAddress,
funcNameRVA,
IntPtr.Zero));
var name = Marshal.PtrToStringAnsi((IntPtr) funcName);
Console.WriteLine(" funcName: {0}", name);
}
}