Interop question about StringBuilder - c#

I am calling a C# method from C code.
The C# method:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void p_func(StringBuilder arg);
public static void callback(StringBuilder arg)
{
Console.WriteLine(arg.ToString());
}
The C method:
extern "C" void c_method(p_func f)
{
char msg[4];
::strcpy(msg,"123");
char* p="123";
f(msg); // this is ok
f(p); //Error: Attempted to read or write protected memory!
}
But if I use String instead of StringBuilder as below in my C# method declaration, f(p) and f(msg) both work. Why?
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void p_func(String arg);
public static void callback(String arg)
{
Console.WriteLine(arg.ToString());
}
Note
the calling logic is like this:
c_method()---->delegate p_func--->callback()
not the reverse.
I checked the arg in the callback(StringBuilder arg), the Length, MaxCapacity, Capacity are all the same for char *p or msg[]. Only that *p leads to the exception. Why?

When you use String as the parameter type, the CLR will not attempt to write any changes back to the native memory buffer. When you use a StringBuilder (which is the right choice for in/out string parameters), it will. But the memory p points to will be read-only because of the way you declared it, that's why yout get an error.

Related

My c# custom delegate behaves weird (No overload for <X> matches <my custom delegate>)

Okay, so after reading into creating custom function delegates after Func<> didnt accept a ref keyword inside the type declaration, I ended up with this delegate:
private delegate TResult RefFunc<T, out TResult>(ref T arg);
inside my little test project. I also declared these two functions:
private static string FirstFunction(string arg) {
return "";
}
private static string SecondFunction(string arg) {
return "";
}
(Since I discovered the problem, this truly is the only function content!)
that adhere to the above delegate and are passed as parameters to this function:
private static void SampleFunction(RefFunc<String, String> argOne, RefFunc<String, String> argTwo) { ... }
Like so (simplified):
private static void Main(string[] args) {
SampleFunction(FirstFunction, SecondFunction);
}
That function call wouldn't work out because "Conversion of 'method group' to '<class>.RefFunc<string, string>' isn't possible", which makes sense - I'm directly passing a function without turning it into a delegate first - although I'm pretty sure I've seen that same syntax working with the Action<> Delegate somewhere. Anyways, I then modified my caller code in the Main() function in many ways while looking into the issue, however none of the below approaches resolved the issue.
SampleFunction(new RefFunc<String, String>(FirstFunction), ...); // Causes "No overload for FirstFunction matches delegate RefFunc<string, string>" at the first function parameter
SampleFunction(RefFunc FirstFunction, ...); // Was worth a try
RefFunc handler = FirstFunction; SampleFunction(handler, ...); // As described in docs. Causes "The usage of type "RefFunc<T, TResult>" (generic) requires 2 type arguments" at keyword RefFunc
RefFunc<String, String> handler = FirstFunction; SampleFunction(handler, ...); // Didn't help it. Causes the same error as with the first attempt
// and a few others
Docs
I've finally decided to turn to stackoverflow. Since my functions clearly adhere to the delegate I created, I can't really understand why C# believes they do not. I appreciate any help!
Please note that while I'm only showing code relevant to the error caused, the project is very small, so I'm sure the problem doesn't lie elsewhere
Since my functions clearly adhere to the delegate I created
Unfortunately, neither of your FirstFunction and SecondFunction is of RefFunc type, just because the T is passed by ref in the delegate's defintion and in the same time you lack ref in your actual functions.
Either you modify both functions
public class Program
{
private delegate TResult RefFunc<T, out TResult>(ref T arg);
private static string FirstFunction(ref string arg) {
return "";
}
private static string SecondFunction(ref string arg) {
return "";
}
private static void SampleFunction(
RefFunc<String, String> argOne, RefFunc<String, String> argTwo)
{
}
public static void Main()
{
SampleFunction(FirstFunction, SecondFunction);
}
}
or you drop ref
private delegate TResult RefFunc<T, out TResult>(T arg);
which now is a correct type for
private static string SecondFunction(string arg) {
return "";
}

Wrap delegate in std::function?

I have a native, unmanaged C++ library that I want to wrap in a managed C++ class to provide clean and type safe way to access the unmanaged class from C# without having to do PInvoke.
One the methods I'm trying to wrap have the following signature:
void Unmanaged::login(
const std::wstring& email,
const std::wstring& password,
std::function<void()> on_success,
std::function<void(int, const std::wstring&)> on_error);
Trying to wrap this however turns out to be not easy at all. The obvious way:
public delegate void LoginSuccess();
public delegate void LoginFailed(int, String^);
void Wrapper::login(String ^ email, String ^ password, LoginSuccess ^ onSuccess, LoginFailed ^ onError)
{
unmanaged->login(convert_to_unmanaged_string(email), convert_to_unmanaged_string(password), [onSuccess]() {onSuccess(); }, [](int code, const std::wstring& msg) {onError(code,convert_to_managed_string(msg))});
}
Fails because managed C++ doesn't allow (local) lambdas (in members).
I know I can use Marshal::GetFunctionPointerForDelegate to get a native pointer to the delegate, but I still need to provide a "middleware" to convert between managed/unmanaged types (such as std::wstring).
Is there perhaps a better way than using managed C++ altogether?
Your code doesn't compile because you can't capture a managed object in a native lambda. But you can easily wrap a managed object in an unmanaged one, with the help of the gcroot class:
You'll need these headers:
#include <vcclr.h>
#include <msclr/marshal_cppstd.h>
And here's the wrapper code:
static void managedLogin(String^ email, String^ password, LoginSuccess^ onSuccess, LoginFailed^ onError)
{
gcroot<LoginSuccess^> onSuccessWrapper(onSuccess);
gcroot<LoginFailed^> onErrorWrapper(onError);
Unmanaged::login(
msclr::interop::marshal_as<std::wstring>(email),
msclr::interop::marshal_as<std::wstring>(password),
[onSuccessWrapper]() {
onSuccessWrapper->Invoke();
},
[onErrorWrapper](int code, const std::wstring& message) {
onErrorWrapper->Invoke(code, msclr::interop::marshal_as<String^>(message));
}
);
}
public ref class Wrapper
{
public:
static void login(String ^ email, String ^ password, LoginSuccess ^ onSuccess, LoginFailed ^ onError)
{
managedLogin(email, password, onSuccess, onError);
}
};
The gcroot object wraps a System::Runtime::InteropServices::GCHandle, which will keep the managed object alive. It's an unmanaged class you can capture in a lambda. Once you know this, the rest is straightforward.
For some reason, the compiler complains if you try to use a lambda in a member function, but it's totally fine with a lambda in a free function. Go figure.
This is the best thing I could come up with quickly. I think you're gonna be stuck with wrappers to hand marshal your args back to managed types unless there is some whole auto callback marshaling thing I'm not aware of. Which is quite possible since I've only been doing managed programming for a week.
delegate void CBDelegate(String^ v);
typedef void(*CBType)(String^);
typedef std::function<void(const wchar_t*)> cb_type;
cb_type MakeCB(CBType f)
{
gcroot<CBDelegate^> f_wrap(gcnew CBDelegate(f));
auto cb = [f_wrap](const wchar_t *s) {
f_wrap->Invoke(gcnew String(s));
};
return cb;
}
void f(cb_type);
void MyCallback(String^ s) {
Console::WriteLine("MyCallback {0}", s);
}
void main() {
f(MakeCB(MyCallback));
}
#pragma unmanaged
void f(cb_type cb)
{
cb(L"Hello");
}
EDIT 1: Improved code.
EDIT 2: Steal good ideas from #Lucas Trzesniewski. Also CBWrap was not needed
EDIT 3: If you prefer to wrap the function instead of the callback.
delegate void CBDelegate(String^ v);
typedef void(*CBType)(String^);
typedef std::function<void(const wchar_t*)> cb_type;
void f(cb_type);
void f_wrap(CBType cb) {
gcroot<CBDelegate^> cb_wrap(gcnew CBDelegate(cb));
auto cb_lambda = [cb_wrap](const wchar_t *s) {
cb_wrap->Invoke(gcnew String(s));
};
f(cb_lambda);
}
void MyCallback(String^ s) {
Console::WriteLine("MyCallback {0}", s);
}
void main() {
f_wrap(MyCallback);
}

Passing a C# class object in and out of a C++ DLL class

I've been working on a prototype code application that runs in C# and uses classes and functions from older C++ code (in the form of an imported DLL). The code requirement is to pass in a class object to the unmanaged C++ DLL (from C#) and have it be stored/modified for retrieval later by the C# application. Here's the code I have so far...
Simple C++ DLL Class:
class CClass : public CObject
{
public:
int intTest1
};
C++ DLL Functions:
CClass *Holder = new CClass;
extern "C"
{
// obj always comes in with a 0 value.
__declspec(dllexport) void SetDLLObj(CClass* obj)
{
Holder = obj;
}
// obj should leave with value of Holder (from SetDLLObj).
__declspec(dllexport) void GetDLLObj(__out CClass* &obj)
{
obj = Holder;
}
}
C# Class and Wrapper:
[StructureLayout(LayoutKind.Sequential)]
public class CSObject
{
public int intTest2;
}
class LibWrapper
{
[DLLImport("CPPDLL.dll")]
public static extern void SetDLLObj([MarshalAs(UnmanagedType.LPStruct)]
CSObject csObj);
public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)]
ref CSObject csObj);
}
C# Function Call to DLL:
class TestCall
{
public static void CallDLL()
{
...
CSObject objIn = new CSObject();
objIn.intTest2 = 1234; // Just so it contains something.
LibWrapper.SetDLLObj(objIn);
CSObject objOut = new CSObject();
LibWrapper.GetDLLObj(ref objOut);
MessageBox.Show(objOut.intTest2.ToString()); // This only outputs "0".
...
}
}
Nothing but junk values appear to be available within the DLL (coming from the passed in C# object). I believe I am missing something with the class marshalling or a memory/pointer issue. What am I missing?
Edit:
I changed the above code to reflect changes to the method/function definitions, in C#/C++, suggested by Bond. The value (1234) being passed in is retrieved by the C# code correctly now. This has exposed another issue in the C++ DLL. The 1234 value is not available to the C++ code. Instead the object has a value of 0 inside the DLL. I would like to use predefined C++ functions to edit the object from within the DLL. Any more help is greatly appreciated. Thanks!
Bond was correct, I can't pass an object between managed and unmanaged code and still have it retain its stored information.
I ended up just calling C++ functions to create an object and pass the pointer back into C#'s IntPtr type. I can then pass the pointer around to any C++ function I need (provided it's extern) from C#. This wasn't excatly what we wanted to do, but it will serve its purpose to the extent we need it.
Here's C# the wrapper I'm using for example/reference. (Note: I'm using StringBuilder instead of the 'int intTest' from my example above. This is what we wanted for our prototype. I just used an integer in the class object for simplicity.):
class LibWrapper
{
[DllImport("CPPDLL.dll")]
public static extern IntPtr CreateObject();
[DllImport("CPPDLL.dll")]
public static extern void SetObjectData(IntPtr ptrObj, StringBuilder strInput);
[DllImport("CPPDLL.dll")]
public static extern StringBuilder GetObjectData(IntPtr ptrObj);
[DllImport("CPPDLL.dll")]
public static extern void DisposeObject(IntPtr ptrObj);
}
public static void CallDLL()
{
try
{
IntPtr ptrObj = Marshal.AllocHGlobal(4);
ptrObj = LibWrapper.CreateObject();
StringBuilder strInput = new StringBuilder();
strInput.Append("DLL Test");
MessageBox.Show("Before DLL Call: " + strInput.ToString());
LibWrapper.SetObjectData(ptrObj, strInput);
StringBuilder strOutput = new StringBuilder();
strOutput = LibWrapper.GetObjectData(ptrObj);
MessageBox.Show("After DLL Call: " + strOutput.ToString());
LibWrapper.DisposeObject(ptrObj);
}
...
}
Of course the C++ performs all the needed modifications and the only way for C# to access the contents is, more or less, by requesting the desired contents through C++. The C# code does not have access to the unmanged class contents in this way, making it a little longer to code on both ends. But, it works for me.
This is the references I used to come up with the basis of my solution:
http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class
Hopefully this can help some others save more time than I did trying to figure it out!
I believe you should declare your returning method like this
__declspec(dllexport) void getDLLObj(__out CClass* &obj)
and respectively the C# prototype
public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)]
ref CSObject csObj);
the inbound method should take a pointer to CClass too, the C# prototype is ok.
If you using the class just from C# ,you should use GCHandle for that http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle%28v=vs.110%29.aspx
edit:
CClass *Holder;
extern "C"
{
// obj always comes in with a 0 value.
__declspec(dllexport) void SetDLLObj(CClass* obj)
{
(*Holder) = (*obj);
}
// obj should leave with value of Holder (from SetDLLObj).
__declspec(dllexport) void GetDLLObj(__out CClass** &obj)
{
(**obj) = (*Holder);
}
}

Call c++ function pointer from c#

Is it possible to call a c(++) static function pointer (not a delegate) like this
typedef int (*MyCppFunc)(void* SomeObject);
from c#?
void CallFromCSharp(MyCppFunc funcptr, IntPtr param)
{
funcptr(param);
}
I need to be able to callback from c# into some old c++ classes. C++ is managed, but the classes are not ref classes (yet).
So far I got no idea how to call a c++ function pointer from c#, is it possible?
dtb is right. Here a more detailed example for Marshal.GetDelegateForFunctionPointer. It should work for you.
In C++:
static int __stdcall SomeFunction(void* someObject, void* someParam)
{
CSomeClass* o = (CSomeClass*)someObject;
return o->MemberFunction(someParam);
}
int main()
{
CSomeClass o;
void* p = 0;
CSharp::Function(System::IntPtr(SomeFunction), System::IntPtr(&o), System::IntPtr(p));
}
In C#:
public class CSharp
{
delegate int CFuncDelegate(IntPtr Obj, IntPtr Arg);
public static void Function(IntPtr CFunc, IntPtr Obj, IntPtr Arg)
{
CFuncDelegate func = (CFuncDelegate)Marshal.GetDelegateForFunctionPointer(CFunc, typeof(CFuncDelegate));
int rc = func(Obj, Arg);
}
}
Have a look at the Marshal.GetDelegateForFunctionPointer method.
delegate void MyCppFunc(IntPtr someObject);
MyCppFunc csharpfuncptr =
(MyCppFunc)Marshal.GetDelegateForFunctionPointer(funcptr, typeof(MyCppFunc));
csharpfuncptr(param);
I don't know if this really works with your C++ method, though, as the MSDN documentation states:
You cannot use this method with function pointers obtained through C++

C# Interop: Out params that can also be null

Consider the following DllImport:
[DllImport("lulz.so")]
public static extern int DoSomething(IntPtr SomeParam);
This is actually referencing a C style function like this:
int DoSomething(void* SomeParam);
Consider that SomeParam is an "out" param, but can also be NULL. The C function behaves differently if the param is NULL. So I would probably want:
[DllImport("lulz.so")]
public static extern int DoSomething(out IntPtr SomeParam);
But, if I make it an out param in my import, I cannot pass it NULL, i.e. I can't do this:
int retVal = DoSomething(IntPtr.Zero)
What are my options here?
If you're trying to pass a value, then out is not the right keyword; change it to ref. You'll still need to explicitly pass a variable, but it can be a null reference.
For example...
[DllImport("lulz.so")]
public static extern int DoSomething(ref IntPtr SomeParam);
You can then call it like this:
IntPtr retVal = IntPtr.Zero;
DoSomething(ref retVal);
However
What is telling you that it needs to be either out or ref? Passing an IntPtr as out or ref is really akin to passing a double pointer. It would actually seem more appropriate to pass the parameter as an IntPtr.
The typical procedure is either to allocate the necessary memory in managed code and pass an IntPtr representing that allocated memory, or IntPtr.Zero to represent a null pointer. You do not need to pass the IntPtr as out or ref in order to send data back to .NET; you only need to do that if the function you're calling would actually change the pointer's address.
I don't understand what the problem is....
This runs:
private void button2_Click(object sender, EventArgs e) {
object bar;
Method(out bar);
bar = IntPtr.Zero;
Method(out bar);
}
private void Method(out object foo) {
foo = null;
}
What's the intention of passing NULL? Is it intended to call the method as usual, but to simply not set the output parameter?
In that case, I think I'd just wrap the extern method with an overload in C#. That overload (without the out parameter) would be like this:
public void DoSomething()
{
IntPtr dummy;
DoSomething(out dummy);
}
I ran into this once. I ended up marshaling the pointer myself (see Marshal Members for the library functions to do so).
Personally, I'd import this function twice, first time with 'out' parameter, second with 'in'.
[DllImport("lulz.so")]
public static extern int DoSomething(out IntPtr SomeParam);
// call as DoSomethingWithNull(IntPtr.Zero)
[DllImport("lulz.so", EntryPoint="DoSomething")]
public static extern int DoSomethingWithNull(IntPtr SomeParam);
This will solve your problem and will make code more readable.

Categories