I'm having an issue where I get a different memory layout when debugging with ReSharper.
I have an unmanaged method that returns an array of (at most) 7-character, null-terminated strings. When executing this method without ReSharper's debugger, the start of the "next" string is 16 bytes later. When executing it with ReSharper's debugger (via ReSharper's Unit Test form choosing the "Debug Unit Tests" option), the start is 64 bytes later.
The method signature is similar to the snippet below. The string array is then "created" similar to the solution here.
[return: MarshalAs(UnmanagedType.I1)]
[DllImport("myDll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static extern bool GetStrings(IntPtr sourceFile,
out IntPtr ptrToStrings,
out uint numberOfStrings);
Try using this to obtain the strings:
[return: MarshalAs(UnmanagedType.I1)]
[DllImport("myDll.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)]
private static unsafe extern bool GetStrings(IntPtr sourceFile,
[Out] out byte* ptrToStrings,
[Out] out uint numberOfStrings);
[SecuritySafeCritical]
private static unsafe string[] ManagedMethod(IntPtr sourceFile)
{
uint size;
byte* array;
if (!GetStrings(sourceFile, out array, out size))
{
throw new Exception("Unable to read strings.");
}
string[] retval = new string[size];
for (int i = 0, p = 0; i < size; i++, p += 8)
{
retval[i] = Marshal.PtrToStringAnsi(new IntPtr(&array[p]));
}
return retval;
}
Related
I'm trying to connect a C# executable to a C++ dll. One of the methods of the dll receives a const char* and an int* (the first one specifying an input value, and the second one, an address to return a value):
extern "C" __declspec(dllexport)
int setVal(long handle, const char* ptrVal, int* ptrRet);
The first thing this function does is to check whether ptrVal is null, and returns -1 if so.
On the other hand, the C# code invokes the dll as follows:
[DllImport(dllName,
EntryPoint = "setVal",
ExactSpelling = true,
CallingConvention = CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
public static extern int setVal(long handle,
[MarshalAs(UnmanagedType.LPStr)] string str,
ref int ptrRes);
In the main function, I have
long handle = 0;
int result = 0;
int res = 0;
string str = "Hello World!";
result = setVal(handle, str, ref res);
When calling this function, I always receive a null pointer at the C side, which makes result equal to -1. I have tried different approaches when declaring the wrapper function, without success:
public static extern int setVal(long handle,
[MarshalAs(UnmanagedType.LPStr)] [In] string str,
[Out] int ptrRes);
public static unsafe extern int setVal(long handle,
[MarshalAs(UnmanagedType.LPStr)] string str,
ref int ptrRes);
public static extern int setVal(long handle,
StringBuilder sb,
ref int ptrRes); // also the unsafe version
public static extern int setVal(long handle,
byte[] value,
ref int ptrRes); // also the unsafe version
I'm using Visual Studio 2017, and .NET framework 4.6.1.
Why am I always receiving NULL as the second argument (const char*) of the dll function?
I did a bit of a google so this would be untested code but I can see it's something you haven't tried
declare external like so
[DllImport(dllName, EntryPoint = "setVal", ExactSpelling = true, CallingConvention, CallingConvention.Cdecl,CharSet = CharSet.Ansi)]
public static extern int setVal(int handle, StringBuilder sb, ref int ptrRes);
and use it like so
int handle = 0;
int result = 0;
int res = 0;
StringBuilder sb = new StringBuilder("Hello World");
result = setVal(handle, sb, ref res);
On Windows, the long handle in C/C++ is int handle in C# both at 32 and 64 bits. You can check it by doing a sizeof(long) in C/C++. Windows is LLP64.
I'm doing a project to call Matlab function by call MCR by P/Invoke.
It throw an exception but I cannot solve it, I search a lot but cannot find the solution.
This is the error
[DllImport(#"C:\Program Files\MATLAB\MATLAB Compiler Runtime\v80\runtime\win64\mclmcrrt8_0.dll", EntryPoint = "mclInitializeApplication_proxy", CallingConvention = CallingConvention.Cdecl)]
private static extern bool mclInitializeApplication(string options, Int32 count);
private static void Main(string[] args)
{
var option = "-nojvm";
var result = mclInitializeApplication(option, 1);
Console.WriteLine(result);
Console.ReadLine();
}
The correct marshalling for "mclIntializeApplication" is:
[DllImport((#"C:\Program Files\MATLAB\MATLAB Compiler Runtime\v80\runtime\win64\mclmcrrt8_0.dll, EntryPoint = "mclInitializeApplication_proxy", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
public static extern bool mclInitializeApplication([MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] string[] options, int count);
That is the first parameter is an array of strings (char**). You should then call it this way:
var options = new [] { "-nojvm" };
var result = mclInitializeApplication(options, options.Length);
To avoid passing length and controlling for boolean result you can also wrap above function to something more C# friendly:
public static void mclInitializeApplication(params string[] options)
{
var count = 0;
if (options != null) { count = options.Length; }
if (!_mclInitializeApplication(options, count))
{
var reason = mclGetLastErrorMessage();
throw new Exception(String.Format("Call to '{0}' failed: {1}", "mclInitializeApplication", reason));
}
}
With:
[DllImport(#"C:\Program Files\MATLAB\MATLAB Compiler Runtime\v80\runtime\win64\mclmcrrt8_0.dll", EntryPoint = "mclGetLastErrorMessage_proxy", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, ExactSpelling = true)]
private static extern IntPtr _mclGetLastErrorMessage();
public static String mclGetLastErrorMessage()
{
var ptr = _mclGetLastErrorMessage();
return Marshal.PtrToStringAnsi(ptr);
}
But, in my real opinion, if you want to target .NET, the best solution is to let Matlab to compile in .NET directly with the SDK provided by Mathworks.
I'd like to call an unmanaged method that allocates memory, creates an array of LPWSTRs, and returns it to managed code. I'd like to avoid in/out parameters and writing code to manage memory and variable scopes as much as possible so I decided I would rely on using CoTaskMemAlloc and let the marshaller automagically clean up after me.
Here's what I have (a modified version of a p/invoke tutorial method on MSDN):
extern "C" DLL1_API LPWSTR *TestArrayOfStrings(_In_ int count)
{
STRSAFE_LPWSTR temp = NULL;
wchar_t * ppStrArray[10] = { NULL };
const size_t alloc_size = sizeof(wchar_t *) * 10;
for (int i = 0; i < 10; i++)
{
temp = (STRSAFE_LPWSTR)CoTaskMemAlloc(alloc_size);
if (i % 2 == 0)
StringCchCopy(temp, alloc_size, L"0123456789");
else
StringCchCopy(temp, alloc_size, L"9876543210");
CoTaskMemFree(ppStrArray[i]);
ppStrArray[i] = temp;
}
count = 10;
return ppStrArray;
}
and on the managed side:
[DllImport("Dll1.Windows.dll", CharSet = CharSet.Unicode)]
[return: MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0, ArraySubType = UnmanagedType.LPWStr)]
public static extern string[] TestArrayOfStrings(out int count);
As you can see I've tried to use additional attributes but the marshaller just doesn't seem to like it--I keep getting "Cannot marshal 'return value': Invalid managed/unmanaged type combination." I am trying to maintain typing as an array of LPWSTRs and would like to avoid SAFEARRAY, for which marshalling is marked obsolete.
Slightly modified code, but the signature was what was important. This method assigns a string value (the third parameter) to the first element of the uninitialized out array passed in.
[DllImport("Dll1.Windows.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern void TestArrayOfStrings(
[MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 1)] [Out] out string[] test,
out int size, string someString);
extern "C" DLL1_API void TestArrayOfStrings(wchar_t ***strings, int *size, wchar_t *someString){
const size_t alloc_size = 64;
STRSAFE_LPWSTR temp = (STRSAFE_LPWSTR)CoTaskMemAlloc(alloc_size);
StringCchCopy(temp, alloc_size, someString);
*strings = (wchar_t **)CoTaskMemAlloc(sizeof(wchar_t));
*strings[0] = temp;
*size = 1;
}
The search term RpcMgmtEpEltInqNext on both Stackoverflow and PInvoke.net yields zero results, so this should be worth asking. I've been fiddling around on MSDN and looking through Win SDK *.h files all day, and feeling a bit out of my element.
I'm basically attempting to query the MSRPC endpoint mapper with managed code. Here is what I have so far:
[DllImport("Rpcrt4.dll", CharSet = CharSet.Auto)]
public static extern int RpcBindingFromStringBinding(string StringBinding, out IntPtr Binding);
[DllImport("Rpcrt4.dll")]
public static extern int RpcBindingFree(ref IntPtr Binding);
[DllImport("Rpcrt4.dll", CharSet = CharSet.Auto)]
public static extern int RpcMgmtEpEltInqBegin(IntPtr EpBinding,
int InquiryType, // 0x00000000 = RPC_C_EP_ALL_ELTS
int IfId,
int VersOption,
string ObjectUuid,
out IntPtr InquiryContext);
[DllImport("Rpcrt4.dll", CharSet = CharSet.Auto)]
public static extern int RpcMgmtEpEltInqNext(ref IntPtr InquiryContext,
out int IfId,
out IntPtr Binding,
out string ObjectUuid,
out string Annotation);
public static List<int> QueryEPM(string host)
{
List<int> ports = new List<int>();
int retCode = 0; // RPC_S_OK
IntPtr bindingHandle = IntPtr.Zero;
IntPtr inquiryContext = IntPtr.Zero;
IntPtr elementBindingHandle = IntPtr.Zero;
int elementIfId = 0;
string elementUuid = string.Empty;
string elementAnnotation = string.Empty;
try
{
retCode = RpcBindingFromStringBinding("ncacn_ip_tcp:" + host, out bindingHandle);
if (retCode != 0)
throw new Exception("RpcBindingFromStringBinding: " + retCode);
retCode = RpcMgmtEpEltInqBegin(bindingHandle, 0, 0, 0, string.Empty, out inquiryContext);
if (retCode != 0)
throw new Exception("RpcMgmtEpEltInqBegin: " + retCode);
retCode = RpcMgmtEpEltInqNext (ref inquiryContext, out elementIfId, out elementBindingHandle, out elementUuid, out elementAnnotation);
if (retCode != 0)
throw new Exception("RpcMgmtEpEltInqNext: " + retCode);
}
The above code isn't complete, but it illustrates my point. The code consistently returns:
RpcMgmtEpEltIngNext: 87
87 meaning Parameter is Incorrect according to here.
So I'm basically stumped at this point, and I'm sure it's because of my extremely crappy PInvoke code.
The p/invoke declaration is wrong. It needs to be:
[DllImport("Rpcrt4.dll", CharSet = CharSet.Auto)]
public static extern int RpcMgmtEpEltInqNext(
IntPtr InquiryContext,
out RPC_IF_ID IfId,
out IntPtr Binding,
out Guid ObjectUuid,
out IntPtr Annotation
);
The first parameter must be passed by value.
You'll need to translate the RPC_IF_ID struct. It's quite a simple one.
public struct RPC_IF_ID
{
public Guid Uuid;
public ushort VersMajor;
public ushort VersMinor;
}
The two string parameters you used were just wrong. They lead to the marshaller calling CoTaskMemFree on the returned value for Annotation. You don't want that. You will need to use Marshal.PtrToStringAnsi to read the value.
Don't forget to call RpcBindingFree and RpcStringFree as per the docs.
I am having an interesting problem with using pinvoke in C# to call _snwprintf. It works for integer types, but not for floating point numbers.
This is on 64-bit Windows, it works fine on 32-bit.
My code is below, please keep in mind that this is a contrived example to show the behavior I am seeing.
class Program
{
[DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private static extern int _snwprintf([MarshalAs(UnmanagedType.LPWStr)] StringBuilder str, IntPtr length, String format, int p);
[DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private static extern int _snwprintf([MarshalAs(UnmanagedType.LPWStr)] StringBuilder str, IntPtr length, String format, double p);
static void Main(string[] args)
{
Double d = 1.0f;
Int32 i = 1;
Object o = (object)d;
StringBuilder str = new StringBuilder(32);
_snwprintf(str, (IntPtr)str.Capacity, "%10.1lf", (Double)o);
Console.WriteLine(str.ToString());
o = (object)i;
_snwprintf(str, (IntPtr)str.Capacity, "%10d", (Int32)o);
Console.WriteLine(str.ToString());
Console.ReadKey();
}
}
The output of this program is
0.0
1
It should print 1.0 on the first line and not 0.0, and so far I am stumped.
I'm not exactly sure why your calls do not work, but the secured versions of these methods do work properly in both x86 and x64.
The following code does work, as expected:
class Program
{
[DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private static extern int _snwprintf_s([MarshalAs(UnmanagedType.LPWStr)] StringBuilder str, IntPtr bufferSize, IntPtr length, String format, int p);
[DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private static extern int _snwprintf_s([MarshalAs(UnmanagedType.LPWStr)] StringBuilder str, IntPtr bufferSize, IntPtr length, String format, double p);
static void Main(string[] args)
{
// Preallocate this to a given length
StringBuilder str = new StringBuilder(100);
double d = 1.4;
int i = 7;
float s = 1.1f;
// No need for box/unbox
_snwprintf_s(str, (IntPtr)100, (IntPtr)32, "%10.1lf", d);
Console.WriteLine(str.ToString());
_snwprintf_s(str, (IntPtr)100, (IntPtr)32, "%10.1f", s);
Console.WriteLine(str.ToString());
_snwprintf_s(str, (IntPtr)100, (IntPtr)32, "%10d", i);
Console.WriteLine(str.ToString());
Console.ReadKey();
}
}
It is possible with the undocumented __arglist keyword:
using System;
using System.Text;
using System.Runtime.InteropServices;
class Program {
[DllImport("msvcrt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
private static extern int _snwprintf(StringBuilder str, int length, String format, __arglist);
static void Main(string[] args) {
Double d = 1.0f;
Int32 i = 1;
String s = "nobugz";
StringBuilder str = new StringBuilder(666);
_snwprintf(str, str.Capacity, "%10.1lf %d %s", __arglist(d, i, s));
Console.WriteLine(str.ToString());
Console.ReadKey();
}
}
Please don't use that.
uint is 32 bits. The length parameter of snprintf is size_t, which is 64 bits in 64 bit processes. Change the second parameter to IntPtr, which is the closest .NET equivalent of size_t.
In addition, you need to preallocate your StringBuilder. Currently you have a buffer overrun.
Try MarshalAs R8 (Thats for real/floating 8 (which is double)) on last parameter in second function.
Take a look at these two articles:
http://www.codeproject.com/Messages/2840231/Alternative-using-MSVCRT-sprintf.aspx (this is actually note to CodeProject article)
http://bartdesmet.net/blogs/bart/archive/2006/09/28/4473.aspx