I'm working with an unmanaged C library which has a callback function that gets called from an internal thread in the C library. The callback has a void * 'context' parameter.
I want to set this context to the address of a C# class instance so that I can access member fields and properties from the callback.
My understanding is that I need to pin the memory of the class to ensure the GC does not move it since the C code will take a copy of address of the instance.
However, GCHandle.Alloc() states that: "An instance with nonprimitive (non-blittable) members cannot be pinned."
And sure enough code attempting to pin a class or a struct containing a field of class type fails at runtime.
How can I pass the address of a C# class instance to my C++ code and ensure that the address remains valid (i.e. is not moved by GC)?
EDIT #1
Correction: the library is a C library, not C++ as previously stated (corrected in text above). Not that it should make any difference.
This the prototype for the C callback:
void Callback(uint32_t hCamera, uint32_t dwInterruptMask, void *pvParams);
And wrapped in C#:
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
public unsafe delegate void PHX_AcquireCallBack(uint hCamera, uint dwInterruptMask,
IntPtr pvParams);
pvParams is passed into the C library when the callback is installed. The library stores the pointer, but does not attempt to access it in any way. It just passes it back whenever it calls the callback.
When using the library from C++ code, I usually pass a this pointer for pvParams.
Related
Assume the following:
Let us have a C++ class:
class ExampleClass
{
private:
int var1;
public:
void DoSomething();
}
Assume also following C++ function which we want to call from .NET Core app with PInvoke or using EmitCalli:
extern "C" ExampleClass CreateObject()
So C++ function returns instance of ExampleClass by value. Is there any way to get this instance on managed part as byte array (assuming that size of ExampleClass is known).
As I remember, in most native x86(x64) calling conventions C++ functions which return structures actully have pointer to the structure to fill as one of the parameters. Will this hint work with NET Core: allocate byte array on managed part and pass pointer as first parameter to unmanaged call?
Thanks!
in C++
ExampleClass CreateClass();
will return a block of bytes of sizeof(ExampleClass) and in absence of virtual members and inheritance it will be plain object and equivalent to same struct definition (see more about this here: Structure of a C++ Object in Memory Vs a Struct )
P/Invoke marshaller should be able to deal with this without a problem.
It it not an IntPtr, it's an actual memory block for C and C++.
IntPtr equivalent would be
ExampleClass * CreateClass();
I am not sure why it returns the instance instead of a pointer but they probably had their reasons.
so if you define it
public struct ExampleClass
{
public int Var1;
}
you should be fine (except if class structure is more complex you would need to check what was layout in C++ compilation, specifically padding. For example class with 2 bools may yield size of 16 in some padding).
see more here:
https://learn.microsoft.com/en-us/dotnet/framework/interop/marshaling-classes-structures-and-unions
https://learn.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.structlayoutattribute
I have unmanaged class. In that class I have a managed object. Now I want a pin_ptr for the managed object in my unmanged class. And when I try to do this I get this error "error C3265: cannot declare a managed _pinnedProject in an unmanaged ProjectWrapper How can I achieve this?
Here is the code.
class ProjectWrapper
{
private:
msclr::auto_gcroot<Project^> _project; // --> This works fine,
pin_ptr<msclr::auto_gcroot<Project^>> _pinnedProject; // ---> This gives error
public:
ProjectWrapper()
{
_project = gcnew Project();
pin_ptr<msclr::auto_gcroot<Project^>> anotherPinnedProject = &_project; // --> This one works,
//_pinnedProject = _project; // --> I want to use this instead of above live,
}
}
The MSDN article about pin_ptr<> is not shy about telling you why this cannot work:
Pinning pointers can only be declared as non-static local variables on the stack.
Pinning pointers cannot be used as:
function parameters
the return type of a function
a member of a class
the target type of a cast.
Which is all for a rather good reason, this kind of object pinning is very efficient. It doesn't require an explicit call into the CLR at all, the pin is discovered when the garbage collector walks the stack looking for roots. That requires the object reference to be a local variable, the compiler emits it in the MSIL with the [pinned] attribute. Also exposed in the C# language with the fixed keyword.
So, no can do, nor should you pursue this. Pinning an object for a long time is very detrimental to GC, it is a rock in the road and prevents heap segments from getting recycled. You should only pin at the exact moment in time that you need the pointer to be stable, that moment only occurs inside of the code of the function that uses the pointer.
If you want to wish this problem away then you need to fall back to the heavy pin. That requires GCHandle::Alloc(), passing GCHandleType::Pinned. You get the pointer you need from AddrOfPinnedObject(), release with the Free() method.
I am passing a C# object (PSObject) to managed C++ in the following way. But it crashes in the commented statement. Am I missing something?
Am I passing PSObject correctly to managed C++?
or am I accessing it wrongly?
I am using clr:oldsyntax.
Actually C# will pass a PSObject to managed C++,
in managed C++ I want to examine the properties present in PSObject. (An object returned by running PowerShell commands)
In C#, using delegate concept I am calling managed C++ method to pass PSObject.
The delegate is declared as below:
delegate bool CFuncDelegate(PSObject Arg);
funcObj (a IntPtr) is a pointer to addData function in C++ (I didn't write that code here, since it is not relevant) and I am calling addData in csharp as:
CFuncDelegate func = (CFuncDelegate)Marshal.GetDelegateForFunctionPointer(funcObj, typeof(CFuncDelegate));
bool isCompleted = func(psoObject); //psoObject is a PSObject
and in managed C++,
static bool __clrcall addData(System::Management::Automation::PSObject* curPsObj)
{
log(Marshal::StringToHGlobalUni(curPsObj->AdaptedMemberSetName));
//prints psadapted
System::Object* value = curPsObj->Properties->get_Item("DisplayName");
//crashes
}
It would be better if someone just post two lines of code to pass object from C# and accessing it in managed C++. Thanks in advance.
I think it's really time that you abandoned oldsyntax and moved to C++/CLI. Anyway, even doing that is not going to solve your problem. You define a delegate like this:
delegate bool CFuncDelegate(PSObject Arg);
And then you assign a delegate variable like this:
CFuncDelegate func = (CFuncDelegate)Marshal.GetDelegateForFunctionPointer(
funcObj, typeof(CFuncDelegate));
The documentation for Marshal.GetDelegateForFunctionPointer says:
Converts an unmanaged function pointer to a delegate.
So, your code can only work if funcObj is an unmanaged function. And your addData method is certainly not that. You need to stop using GetDelegateForFunctionPointer here. It is simply not compatible with calling the managed function addData.
I've no idea where funcObj comes from. You said:
funcObj (a IntPtr) is a pointer to addData function in cpp (i didnt write that code here, since it is not relevant)
In fact it is not only relevant to the problem, it is the root cause of the problem. What I would expect to see here would be for you to add the C++ assembly to your C# project as a reference, at which point addData could be referenced directly.
Of course, none of this even mentions the fact that your calling conventions are mis-matched. The managed code uses clrcall and your unmanaged function pointer is taken to be stdcall.
Update
There's some more information in the comments. Pass addData as a delegate. You'll need to declare the delegate type in the C# code which I believe you reference from your C++ assembly.
I am new to using C++ libraries in C# and also to the C++ programming in general. I have a DLL built from a C++ code which I believe is a 'managed' code as the name of the DLL is "TestManaged.dll". I am not 100% sure if the dll/C++ code is managed/unmanaged.
I want to use classes and methods of this DLL in my C# windows forms application code. There are multiple classes in this DLL. When I chekced these classes and methods inside those classes in Object Browser, all of them have Public identifier.
So far, I have added this DLL to my references of C# application code. There are three classes I would talk about in my question: Product, ReqStatus, ProductData. I could create an object(s) for various classes of this DLL as follows.
Product testCall = new ProductClass();
There is another class called ProductData in this DLL and I could get the C++ code for this class which is as follows. In this case, ProductData is shown as class in Object Browser in C# where as it is actually a struct in C++ code. I am not sure if this is important to answer my question (at the end).
Following is a C++ code that defines ProductData struct - ProductData.h file.
#ifdef WIN32_MANAGED
public ref struct ProductData
#else
struct ProductData
#endif
{
UINT32 ProductId; //!< Product ID
UINT32 PRoductRev; //!< Build Revision
};
Following is a C++ code that defines ReqStatus enum - ReqStatus.h file. I have created the same enum in my C# code with no identifier specified.
enum ReqStatus
{
SUCCESS, //!< Method was successful
//Connection errors
NOT_CONNECTED, //!< Connection not open
CONN_TIMEOUT, //!< Connection timed out commuincating with device
};
Now, there are two methods I want to call and have problems with both:
Method 1: is a getProductData method inside Product class which accepts object of ProductData type as a parameter and returns the ReqStatus which is an enum type in C++. So following is the declaration of the gerProductData method (as seen in the Object Browser):
public ReqStatus getProductData(ProductData data)
The same method's C++ delcaration is: (The actual method is too long and hence just giving the declaration): This method is inside Prodcut.cpp file
ReqStatus Product::getProductData(ProductData PLATFORM_PTR data)
PLATFORM_PTR is defined as below in Platform.h
#ifdef WIN32_MANAGED
#define PLATFORM_PTR ^
#else
#define PLATFORM_PTR *
#endif
Method 2: is a getConnected method inside Product class which accepts a character array (I am not sure of this) and an object of ProductData type as a parameter and returns the ReqStatus which is an enum type in C++. So following is the declaration of the getConnected method (as seen in the Object Browser):
public ReqStatus getConnected(sbyte* someChar, ProductData data)
The same method's C++ delcaration is: (The actual method is too long and hence just giving the declaration): This method is inside Prodcut.cpp file
ReqStatus Product::getConnected(const char *someChar, ProductData PLATFORM_PTR data)
C++ code calls the methods as follows:
private : Product^ _testProduct;
testProduct = gcnew Product();
ProductData ^ data = gcnew ProductData();
int portNum = Decimal::ToInt16(7);
char portName[32];
_snprintf(&portName[0], sizeof(portName),"COM%d", portNum);
ReqStatus status = _testProduct->getConnected(&portName[0], data); //Calling getConnected
There is an internal call to getProductData method inside the getConnected method.
ReqStatus status = getProductData(data); //data is the same which was passed to the getConnected method
MY C# code is as follows and I got errors at both method calls: I have put errors on the same line in the below code snippet. Both methods are independent. Its just that the getProductData is called from getConnected method in C++ code. I wanted to check if I can call both individually.
ProductData pData = new ProductData(); // OK
Product _testProduct = new Product(); // OK
ReqStatus status1 = _testProduct.getConnected("COM5", pData ); //Error 1: The best overloaded method getConnected has some invalid arguments
ReqStatus status2 = (ReqStatus)_testProduct.getProductData(pData ); // Error 2: Method is inaccessible due to its protection level
For Error 1, I tried solutions from various articles on StackOverflow and other forums but, could not solve it. Just for a reference, I tried to change the "SomePortCOM" as follows but it din't work.
UPDATE: This code works fine now and I don't see Error 1(Invalid arguments). Now, I only need to get rid of the Error 2 (Protection level error). Kindly provide any suggestions. Thank you.
String str = "COM5";
byte[] bytes = Encoding.ASCII.GetBytes(str);
unsafe
{
fixed (byte* p = bytes)
{
sbyte* sp = (sbyte*)p;
//SP is now what you want
ReqStatus status1 = _testProduct.getConnected(sp, pData );
}
}
For Error2, I searched so many blogs and found that one of the possible solution could be the use of DLLImport, I tried that as well and I have following issue:
C# declaration of DLLImport:
[DllImport("TestManaged.dll",EntryPoint="getConnected")]
public static extern ReqStatus getConnected(String SerialPort, ref ProductData pData);
I am calling this function as below from my C# code:
ProductData pData = new ProductData();
String str = "COM7";
ReqStatus status1 = getConnected(str, ref pData);
However, I am getting Entry point not found error. I tried to run the dumpbin function to get the list of functions exported by this DLL. But, I do not see any functions. Rather just a random output as below.
Microsoft (R) COFF/PE Dumper Version 10.00.40219.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file C:\Rumit\TestManaged.dll
File Type: DLL
Summary
2000 .data
22000 .rdata
1000 .reloc
1000 .rsrc
13000 .text
UPDATE:
Also, I do not see any methods in this DLL via Dependency Walker.
Now, I have got the source code for C++. But I am fairly new to C++ coding. In case any change is required to C++ code, kindly give the directions.
Regards,
Rumit
enum ReqStatus
That's your biggest hang-up. That declares a native enum type, it is not usable in managed code and makes any code that uses it inaccessible. You must declare the managed version of it with the enum class keyword, like this:
public enum class ReqStatus {
// etc...
}
The unsafe block around your code will make it so your assembly cannot be verified for security protocols, so be wary of that. When I've called methods from C++ (native or not) from C# I've had to use PInvoke (Platform Invoke) to call them. For a protection level, I know you said everything is public in the C++, but if you're new to C++ you may have made a quick syntax mistake. In C#, all methods need to be preceded by a storage specifier (public, protected, etc...) but in C++ you place a storage specifier followed by a colon and everything between THAT storage and the next declared storage will be of that storage type. Maybe this is your issue?
Thank you Hans, for the pointing out the problem. Just that, I made the enum definition as 'public'. But, I'm not sure if you put the 'class' by mistake or was it intentional as it was giving me so many errors as it wasn't taking it as an enum and was asking for an object at every place I used enum. Let me know if I misunderstood anything here.
So, I got it worked just by making the enum public. However, I am still not able to find how to pass the correct values to that the C++ function from C#. (Error1 in my original post).
When I debuged the C++ code, it passes the "0x0034E808 "COM5" value (I assume it's a memory location and a value?) for the first parameter of the getConnected method. I tried to pass this value by implementing the unsafe method (explained in my original post for Error 1), it passes "0x0277aab8" (again seems some memory address), but could not get connected to it (Getting serial port timeout errors). Am I passing the value incorrectly compared to C++ method?
Regards,
Rumit
I have the following marshalling code in my project. I have few questions on this.
[DllImport=(Core.dll, SetLastError=true, EntryPoint="CoreCreate", CharSet="CharSet.Ansi", CallingConvention="CallingConvention.Cdecl")]
internal static extern uint CoreCreate(ref IntPtr core);
Why 'internal static extern' is required? Is this compulsory? Why this is used?
What is SetLastError?
[StructLayout(LayoutKind.Sequential,CharSet=CharSet.Ansi)]
internal struct Channel
{
internal byte LogicalChannel;
}
Why LayoutKind.Sequential?
Why 'internal static extern' is required?
The internal modifier just set visibility of your method. It's not required to be internal so you can declare the method private or public as you need and as you would do with any other standard method.
The static modifier is required because it's not an instance method and that method doesn't know any class (it hasn't a this pointer).
Finally extern is required to inform the compiler that the method isn't implemented here but in another place (and you'll specify where using attributes). Evey extern method must be declared static too (because it's a simple function call without any knowledge about objects).
What is SetLastError?
It indicates that method may change the thread's last-error code value. See the GetLastError() function for details about this. If the called function will change this value then it's a good thing to set SetLastError to true, from MSDN:
The runtime marshaler calls GetLastError and caches the value returned to prevent it from being overwritten by other API calls. You can retrieve the error code by calling GetLastWin32Error.
In short it saves the value returned by GetLastError() to an internal cache so any other call to system API (even internal to others framework functions) won't overwrite that value.
Why LayoutKind.Sequential?
Class layout in .NET isn't required to be sequential in memory (sequential = if A is declared before B then memory layout has A before B). This is not true in C where the declaration order matters (declaration is used by the compiler to understand the layout, in memory, of raw data). If you have to interop with C functions then you have to be sure about the layout of the data you pass them. This is how LayoutKind.Sequential works: it instructs the compiler to respect the declaration sequential order for data in the struct. This is not the only option to interop with unmanaged world, you can even explicitly set the offset (from structure beginning) of each field (see LayoutKind.Explicit).
This is not an answer, just a few comments:
"internal static" is one thing and "extern" is another thing needed when calling external dll's.
SetLastError or GetLastError is methods we used a lot in the "old" days to get error messages from windows about the latest handling.
LayoutKind.Sequential is a way to inform the compiler to layout the struct in a specified way - you may need to do this if marchalling to other systems.