When I googled around I saw posts saying that passing a C# class
is the same as passing struct
by reference (i.e. a ref SomeStruct name
parameter) to a C API while using PInvoke (here is one the posts C# PInvoke struct vs class access violation).
However when running an example I am seeing a different behavior than expected. Where a ref
to a struct
acts as a real pointer while 'class' does not
C code :
//PInvokeProvider.h
#include "stdafx.h"
typedef struct Animal_s
{
char Name[10000];
} Animal;
extern "C" void __declspec(dllexport) ChangeName(Animal* pAnimal);
//PInvokeProvider.cpp
#include "stdafx.h"
#include <stdio.h>
#include "PInvokeProvider.h"
extern "C" {
void ChangeName(Animal* pAnimal)
{
printf("Entered C++\n");
printf("Recieved animal : %s\n", pAnimal->Name);
printf("This function will change the first letter of animal to 'A'\n");
pAnimal->Name[0] = 'A';
printf("Animal changed to : %s\n", pAnimal->Name);
printf("Leaving C++\n");
}
}
Now onto C# using struct
for `Animal':
namespace PInvokeConsumer
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Animal
{
/// char[10000]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10000)]
public string Name;
public Animal(string name)
{
Name = name;
}
}
public partial class NativeMethods
{
[DllImportAttribute("PInvokeProvider.dll",
EntryPoint = "ChangeName",
CallingConvention = CallingConvention.Cdecl)]
public static extern void ChangeName(ref Animal pAnimal);
}
internal class Program
{
public static void Main(string[] args)
{
Animal animal = new Animal("Lion");
Console.WriteLine("Animal : {0}", animal.Name);
Console.WriteLine("Leaving C#");
NativeMethods.ChangeName(ref animal);
Console.WriteLine("Back to C#");
Console.WriteLine("Animal : {0}", animal.Name);
Console.ReadKey();
}
}
}
The output using a ref
to a struct
is as expected changing the the first letter to 'A' in C# realm:
Animal : Lion
Leaving C#
Entered C++
Recieved animal : Lion
This function will change the first letter of animal to 'A'
Animal changed to : Aion
Leaving C++
Back to C#
Animal : Aion
However when I try to use class
instead of a ref
to a struct
:
namespace PInvokeConsumer
{
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public class Animal
{
/// char[10000]
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10000)]
public string Name;
public Animal(string name)
{
Name = name;
}
}
public partial class NativeMethods
{
[DllImportAttribute("PInvokeProvider.dll",
EntryPoint = "ChangeName",
CallingConvention = CallingConvention.Cdecl)]
public static extern void ChangeName(Animal pAnimal);
}
public static void Main(string[] args)
{
Animal animal = new Animal("Lion");
Console.WriteLine("Animal : {0}", animal.Name);
Console.WriteLine("Leaving C#");
NativeMethods.ChangeName(animal);
Console.WriteLine("Back to C#");
Console.WriteLine("Animal : {0}", animal.Name);
Console.ReadKey();
}
}
The output is:
Animal : Lion
Leaving C#
Entered C++
Recieved animal : Lion
This function will change the first letter of animal to 'A'
Animal changed to : Aion
Leaving C++
Back to C#
Animal : Lion
So when I use a class the memory allocated to Animal does not get modified. The question is why not?