In my c# application i receive pointer to c++ struct in callback/delegate. I'm not sure if class can do the trick but just casting c++ pointer to appropriate c# struct works fine, so I'm using c# struct for storing data.
Now I want to pass reference to struct for further processing
I can't use class because it probably will not "map" perfectly to c++ struct.
I don't want to copy struct for better latency
How can I do that?
This example demonstrates that struct is passed by value, not by reference:
using System;
namespace TestStruct
{
struct s
{
public int a;
}
class Program
{
static void Main(string[] args)
{
s s1 = new s
{
a = 1
};
Foo(s1);
Console.WriteLine("outer a = " + s1.a);
}
private static void Foo(s s1)
{
s1.a++;
Console.WriteLine("inner a = " + s1.a);
}
}
}
Output is:
inner a = 2
outer a = 1
It sounds like you just want to use ref to pass the struct by reference:
private static void Foo(ref s s1)
{
s1.a++;
Console.WriteLine("inner a = " + s1.a);
}
And at the call site:
Foo(ref s1);
See my article on parameter passing in C# for more details.
Note that other than for interop, I would normally strongly recommend against using mutable structs like this. I can understand the benefits here though.
Related
Let's say I have a struct that's not one of the C# primitives.
I'd like to know if there's a way (including unsafe code) to get a pointer from an instance of the struct type (the pointer doesn't have to be type-safe, it could be void*) and get an instance of the struct type from a pointer, but without boxing involved.
(I know that the System.Runtime.InteropServices.Marshal class offers almost what I'm describing, but it involves passing and getting structs as object, which means that boxing is involved.)
You can use an unsafe region to get the pointer using the & operator and convert back to the type using the * operator.
public struct Foo
{
public int bar;
};
void Main()
{
var foo = new Foo();
foo.bar = 5;
unsafe
{
Foo * fooPtr = &foo;
Console.WriteLine("Foo address {0:X}", *fooPtr);
Foo anotherFoo = *fooPtr;
Console.WriteLine("Bar= {0}", anotherFoo.bar);
}
}
I don't quite understand what is the problem with taking the address of the struct with & operator. If you're not aware of that, there exist address of operator.
For example:
struct Demo
{
public int SomeField;
}
internal static class Program
{
static Demo demo = new Demo { SomeField = 5 };
private static void Main()
{
Print();
}
public static unsafe void Print()
{
fixed (Demo* demop = &demo)
{
Console.WriteLine(demop->SomeField);
}
}
}
This code doesn't boxes the struct, though it needs unsafe context to compile.
i am writing a simple code by using ref keyword. As i understand struct can be really slow on copying things. In order to make it faster you should use ref. So i wrote this simple code below.
using System;
namespace ConsoleApplication4
{
class Program
{
public static void returns(ref s s1)
{
for (int i = 0; i < 100;i++)
{
s1.z += i;
}
}
static void Main(string[] args)
{
s s1 = new s();
returns(ref s1);
}
}
}
it gave me the error "Error 1 Inconsistent accessibility: parameter type 'ref is less accessible than method". I checked one of stackoverflow question. Using ref seemed like that. What is my mistake here. Can you give me any advice.
Thanks in advance.
Likely the type s is not a public type.
oh thanks i just forgot to type these lines to here.
public struct s
{
public int z;
}
i did this private not i changed to a public. One of my friend suggest me to do private struct blah and do property of struct public. Is it false logic.
I have a C++ project containing a nonmanaged class method used to display string in a user interface :
void MyProject::displayIHM(std::string mystring);
This project is compiled with /clr because it calls another one made with C#/.NET 4.0. The goal of the .NET project is to make heavy computation tasks. During computation, we want to get back from it some information to the user interface.
My idea was to create two new methods in the C++-cli project :
void MyProject::displayFromDotNet(String^ mystring)
{
displayIHM(ToStdString(mystring));
}
string ToStdString ( String ^ s)
{
const char* chars = (const char*)(Marshal::StringToHGlobalAnsi(s)).ToPointer();
string os = chars;
Marshal::FreeHGlobal(IntPtr((void*)chars));
return os;
}
Until now, everything is ok but now the difficult part : how to provide displayFromDotNet to the .NET project. My idea was to provide a function pointer in the constructor of the .NET class and then to launch the process :
void (MyProject::*pointeurFunc)(String^) = &MyProject::displayFromDotNet;
ComputationProject^ kernel = gcnew ComputationProject((this->*pointeurFunc));
kernel->Compute();
The second line does not work. The constructor of ComputationProject has a IntPtr parameter but I do not know if I can convert a function pointer to an IntPtr^ in C++. I also made some attempts using Marshal::GetDelegateForFunctionPointer but it could not compile.
I do not know what to do, any help would be appreciated!
EDIT : yes ComputationProject is my C#/.NET project. The error with line 2 is "cannot convert parameter 1 from 'overloaded function type' to 'System::IntPtr'".
I finally find a (ugly) way.
My main problem was I could not pass a method pointer to C# because it is not a real function pointer (so I cannot cast it to IntPtr).
I decided to create a second class containing a static MyProject object and a static method calling displayIHM on the static object :
class StaticMyProject
{
public :
static MyProject staticObject;
static void DisplayInIHM(char *);
};
In cpp :
MyProject StaticMyProject::objetStatique;
void StaticMyProject::DisplayInIHM(char *message)
{
std::string message2(message);
staticObject.displayIHM(message2);
}
Now for calling Compute method of ComputationProject, I modified the code like this :
StaticMyProject::objetStatique = *this;
void (*funcPointer)(char*) = StaticMyProject::DisplayInIHM;
ComputationProject^ kernel = gcnew ComputationProject((IntPtr)funcPointer);
kernel->Compute();
And in my ComputationProject.cs :
public class ComputationProject
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void FunctionPointer([MarshalAs(UnmanagedType.LPStr)]string message);
public readonly FunctionPointer DisplayMethod;
public ComputationProject(IntPtr ptr)
{
this.DisplayMethod = (FunctionPointer)Marshal.GetDelegateForFunctionPointer(ptr, typeof(FunctionPointer));
}
public int Compute()
{
this.DisplayMethod("Beginning computation...");
...
}
}
This is maybe an obvious question but I have been googling for 2 hours and can't figure it out. So I've got a class in C++ that looks like this:
class MyClass
{
public:
static void Function(float& r) { r = 10; }
};
I have an interop wrapper class that looks like this:
public ref class Wrapper
{
public:
static void WrappedFunction(float& r) { MyClass::Function(r); }
};
And now I want to call it from C#, like this:
float r;
Wrapper.WrappedFunction(ref r);
However I get a compile error on the C# side which says this:
cannot convert from 'ref float' to 'float*'
I have a vague notion that I need to do something in the Wrapper class but I don't know what. Also, I know this use case is trivial with dllimport/pinvoke but I want to wrap this with C++/CLI for performance reasons.
Any help is greatly appreciated!
Wrapper class needs to be
public ref class Wrapper
{
public:
static void WrappedFunction(float% r)
{ float copy = r; MyClass::Function(copy); r = copy; }
};
References defined with % work almost exactly like ones defined using &, except they work together with the garbage collector.
Another option is
public ref class Wrapper
{
public:
static void WrappedFunction(float% r)
{ pin_ptr<float> p = &r; MyClass::Function(*p); }
};
Which will pin the object in memory preventing the garbage collector from using it, but have the native code work directly on the managed variable. Not usually important, but if you had another thread needing to see intermediate values, this is how.
I want to keep a pointer to a managed Exception object in an unmanaged C assembly.
I've tried a bunch of ways. This is the only one I've found that passes my very preliminary tests.
Is there a better way?
What I'd really like to do is handle the alloc and free methods in the ExceptionWrapper constructor and destructor, but structs can't have constructors or destructors.
EDIT: Re: Why I would like this:
My C structure has a function pointer that is set with a managed delegate marshaled as an unmanaged function pointer. The managed delegate performs some complicated measurements using external equipment and an exceptions could occur during those measurements. I'd like to keep track of the last one that occurred and its stack trace. Right now, I'm only saving the exception message.
I should point out that the managed delegate has no idea it's interacting with a C DLL.
public class MyClass {
private IntPtr _LastErrorPtr;
private struct ExceptionWrapper
{
public Exception Exception { get; set; }
}
public Exception LastError
{
get
{
if (_LastErrorPtr == IntPtr.Zero) return null;
var wrapper = (ExceptionWrapper)Marshal.PtrToStructure(_LastErrorPtr, typeof(ExceptionWrapper));
return wrapper.Exception;
}
set
{
if (_LastErrorPtr == IntPtr.Zero)
{
_LastErrorPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(ExceptionWrapper)));
if (_LastErrorPtr == IntPtr.Zero) throw new Exception();
}
var wrapper = new ExceptionWrapper();
wrapper.Exception = value;
Marshal.StructureToPtr(wrapper, _LastErrorPtr, true);
}
}
~MyClass()
{
if (_LastErrorPtr != IntPtr.Zero) Marshal.FreeHGlobal(_LastErrorPtr);
}
}
This doesn't work. You are hiding a reference to the Exception object in unmanaged memory. The garbage collector cannot see it there so it cannot update the reference. When the C spits the pointer back out later, the reference won't point the object anymore after the GC has compacted the heap.
You'll need to pin the pointer with GCHandle.Alloc() so the garbage collector cannot move the object. And can pass the pointer returned by AddrOfPinnedObject() to the C code.
That's fairly painful if the C code holds on that pointer for a long time. The next approach is to give the C code a handle. Create a Dictionary<int, Exception> to store the exception. Have a static int that you increment. That's the 'handle' value you can pass to the C code. It is not perfect, you'll run into trouble when the program has added more than 4 billion exceptions and the counter overflows. Hopefully you'll never actually have that many exceptions.
You want serialization.
As a side note, your statement of:
What I'd really like to do is handle the alloc and free methods in the ExceptionWrapper constructor and destructor, but structs can't have constructors or destructors.
is not true. structs in C# can have and do have constructor(s), just not allowing user to declare parameterless constructor explicitly. That is, for example, you can declare a constructor which accepts an Exception. For destructors, which is not widely used in managed code, you should implement IDisposable if your class will hold some unmanaged resources.
Exception is non-blittable, you may not marshalling it the way you described, but it can be serialized as byte arry thus makes interop possible. I've read your another question:
Implications of throwing exception in delegate of unmanaged callback
and take the some of the usage from your code. Lets's to have two projects, one for managed and the other for the unmanaged code. You can create them with all the default settings but note the bitness of the executable images should be set the same. There are just one file per project need to be modified:
Managed console application - Program.cs:
using System.Diagnostics;
using System.Runtime.Serialization.Formatters.Binary;
using System.Runtime.InteropServices;
using System.IO;
using System;
namespace ConsoleApp1 {
class Program {
[DllImport(#"C:\Projects\ConsoleApp1\Debug\MyDll.dll", EntryPoint = "?return_callback_val##YGHP6AHXZ#Z")]
static extern int return_callback_val(IntPtr callback);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate int CallbackDelegate();
static int Callback() {
try {
throw new Exception("something went wrong");
}
catch(Exception e) {
UnmanagedHelper.SetLastException(e);
}
return 0;
}
static void Main() {
CallbackDelegate #delegate = new CallbackDelegate(Callback);
IntPtr callback = Marshal.GetFunctionPointerForDelegate(#delegate);
int returnedVal = return_callback_val(callback);
var e = UnmanagedHelper.GetLastException();
Console.WriteLine("exception: {0}", e);
}
}
}
namespace ConsoleApp1 {
public static class ExceptionSerializer {
public static byte[] Serialize(Exception x) {
using(var ms = new MemoryStream { }) {
m_formatter.Serialize(ms, x);
return ms.ToArray();
}
}
public static Exception Deserialize(byte[] bytes) {
using(var ms = new MemoryStream(bytes)) {
return (Exception)m_formatter.Deserialize(ms);
}
}
static readonly BinaryFormatter m_formatter = new BinaryFormatter { };
}
}
namespace ConsoleApp1 {
public static class UnmanagedHelper {
[DllImport(#"C:\Projects\ConsoleApp1\Debug\MyDll.dll", EntryPoint = "?StoreException##YGHHQAE#Z")]
static extern int StoreException(int length, byte[] bytes);
[DllImport(#"C:\Projects\ConsoleApp1\Debug\MyDll.dll", EntryPoint = "?RetrieveException##YGHHQAE#Z")]
static extern int RetrieveException(int length, byte[] bytes);
public static void SetLastException(Exception x) {
var bytes = ExceptionSerializer.Serialize(x);
var ret = StoreException(bytes.Length, bytes);
if(0!=ret) {
Console.WriteLine("bytes too long; max available size is {0}", ret);
}
}
public static Exception GetLastException() {
var bytes = new byte[1024];
var ret = RetrieveException(bytes.Length, bytes);
if(0==ret) {
return ExceptionSerializer.Deserialize(bytes);
}
else if(~0!=ret) {
Console.WriteLine("buffer too small; total {0} bytes are needed", ret);
}
return null;
}
}
}
Unnamaged class library - MyDll.cpp:
// MyDll.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
#define DLLEXPORT __declspec(dllexport)
#define MAX_BUFFER_LENGTH 4096
BYTE buffer[MAX_BUFFER_LENGTH];
int buffer_length;
DLLEXPORT
int WINAPI return_callback_val(int(*callback)(void)) {
return callback();
}
DLLEXPORT
int WINAPI StoreException(int length, BYTE bytes[]) {
if (length<MAX_BUFFER_LENGTH) {
buffer_length=length;
memcpy(buffer, bytes, buffer_length);
return 0;
}
return MAX_BUFFER_LENGTH;
}
DLLEXPORT
int WINAPI RetrieveException(int length, BYTE bytes[]) {
if (buffer_length<1) {
return ~0;
}
if (buffer_length<length) {
memcpy(bytes, buffer, buffer_length);
return 0;
}
return buffer_length;
}
With these code you can serialize the exception first and then deserialize it at any later time for retrieving the object that represents the original exception - just the reference would not be as same as the original, so are the objects it referenced.
I'd add some note of the code:
The dll name and method entry point of DllImport should be modified as your real build, I didn't manipulate the mangled names.
Unmanaged Helper is just a demonstration of the interop with the example unmanaged methods StoreException and RetrieveException; you don't have to write the code like how I deal with them, you might want to design in your own way.
If you want to deserialize the exception within the unmanaged code, you might want to design your own formatter rather than BinaryFormatter. I don't know an existing implementation of Microsoft's specification in non-cli C++.
In case you want to build the code in C instead of C++, you'll have to change the Compile As option(Visual Studio) and rename MyDll.cpp to MyDll.c; also note the mangled names would be like _StoreException#8; use DLL Export Viewer or hex editors to find the exact name.