pinvoking not modifiying value of int argument

96 views Asked by At

The function is defined in the C# project as:

[DllImport("Project2.dll", CallingConvention = CallingConvention.Cdecl)]
static extern void modifyVar(ref int x);

And the function call is:

modifyVar(ref a)

where a = 4

The native C++ function definition is as follows:

extern "C" _declspec(dllexport) void modifyVar(int* a)
{   
        int x = 1;
        a = &x;
}

Why in the C# project, a is not pointing to the new variable with value 1 after the function call? It still remains 4.

However, if i modify the C++ function to this, it works fine:

 extern "C" _declspec(dllexport) void modifyVar(int* a)
{   
        int x = 1;
        *a = x;
}

But, how do I get it to point to a new memory location rather than modifying the value?

Does it having something to do with using IntPtr rather than ref int?

2

There are 2 answers

5
David Heffernan On BEST ANSWER

But, how do I get it to point to a new memory location rather than modifying the value?

So, as I understand the question, you are asking how to return the address of a variable in the native module. Do it like this:

static int i = 42;

extern "C" _declspec(dllexport) int* getPtr()
{   
    return &i;
}

Note that the variable whose address is returned is not a local variable. It cannot be the address of a local variable because the scope of the local variable ends when the function returns.

And from C# call it like this:

[DllImport("...", CallingConvention = CallingConvention.Cdecl)]
static extern IntPtr getPtr();

....

IntPtr ptr = getPtr();
int i = Marshal.ReadInt32(ptr);

If you'd rather return the pointer through a parameter do it like this:

static int i = 42;

extern "C" _declspec(dllexport) void getPtr(int** ptr)
{   
    *ptr = &i;
}

And from C#:

[DllImport("...", CallingConvention = CallingConvention.Cdecl)]
static extern void getPtr(out IntPtr ptr);

....

IntPtr ptr;
getPtr(out ptr);
int i = Marshal.ReadInt32(ptr);

However, having said all of that, I really doubt that you actually want to return the address of a variable. That global variable doesn't look like it's going to be very useful. And the alternative is heap allocated memory. But then you need to export a deallocator, or allocate off a shared heap. Again, not much fun to be had there. And a pain in the neck too for marshalling.

What is much more likely to be useful is to allocate the variable in the calling code, the managed code, and ask the native code to modify the value of that variable. That makes the lifetime management trivial, and allows you to let the pinvoke marshaller do all the work.

1
Nicholas Carey On

Your first snippet:

void modifyVar( int* a )
{   
  int x = 1;
  a = &x;
}

takes an int* (pointer to int). When called, it receives, on the stack, a word containing an address, presumably the location of an int, in the caller's scope. You then set that word, on the stack, to the address of a local variable in the current function's stack frame. When this function returns, the stack is popped and the exiting function's stack frame and the work pushed on the stack as its parameter list are freed.

The caller has no knowledge of what your function just did as you haven't modified anything in the caller's scope. You have not modified the thing (an int) to which your int* is pointing.

In your second snippet

void modifyVar( int* a )
{   
  int x = 1;
  *a = x;
}

The same thing happens on the call. The function body does something different: the statement *a = x; does this:

  • takes the address contained in the int* named a,
  • de-references it, and
  • stores the value of the int variable x in that location.

Now you have modified the caller's storage and the change is known to the caller.

In the first instance, all you did is modified one of your parameters, a pointer: you didn't modify the memory it located and so the caller was unaware of it.

If you had returned the address of one of your local variables to the caller and it made use of it, bad things would likely have happened as the caller would then be mucking with stack memory that might or might not be in use.

You need to get a copy of The C Programming Language (aka K+R):

C Programming Language Cover