Tonyd138

I have a C# application that loads an unmanaged DLL at runtime, and calls several functions in it using P/Invoke, and delegates. In every case, when I pass a pointed to a buffer, I used the fixed key word to prevent GC from moving the memory.

Code Snippet

byte[] bPlate = new byte[15];

fixed (byte* pPlate = bPlate)

{

retVal = _LocateVehicleBarcode(Instance, ncodeToBytes(barcode.ToCharArray()), out customer, pPlate);

}


I have one function however that takes a pointer to a struct. The closest I can get to a fixed pointer to a struct is a fixed pointer to the first element in the struct. This is the unmanaged signature.

Code Snippet

int __stdcall __export POSAddJournalRecord(tTenderedMoney* money);


This is the declaration of the delegate used to call it.

Code Snippet

private unsafe delegate dbErrEnum POSAddJournalRecordD(ref tTenderedMoney money);

private static POSAddJournalRecordD _POSAddJournalRecord;

_POSAddJournalRecord = (POSAddJournalRecordD)Marshal.GetDelegateForFunctionPointer(
(IntPtr)ICSAPI.DllCalls.GetProcAddress("POSAddJournalRecord"), typeof(POSAddJournalRecordD));


Then when I call the delegate I pass the struct by ref.

Code Snippet

public dbErrEnum POSAddJournalRecord(tTenderedMoney money)
{
return _POSAddJournalRecord(ref money);
}



My question is, will this cause problems by not being fixed Will the framework marshal that type to a fixed pointer I don't want GC to come along and move my struct in the middle of this call. What is the right way to do this

Thanks in advance!



Re: Visual C# Language Passing a C# struct to an unmanaged DLL

Vivek Ragunathan

Hi

It is not normally required to pin references when you pass them as parameters to an unmanaged API via PInvoke.

The PInvoke layer takes care of pinning them.

For instance, buffers and delegates etc are automatically pinned and more efficiently as required by the PInvoke layer.

But it is required to pin when you pass around buffers/delegates to an unmanaged api that operates asynchronously.

Hope that helps.

Regards





Re: Visual C# Language Passing a C# struct to an unmanaged DLL

Hugo

You need to grasp that "fixed" is only needed for managed data, unmanaged (e.g. stack based) data does not require it.

"fixed" lets you take the address of something that is managed (or part of something that is managed) under normal circumstances the C# compiler disallows this.

If you attempt to take an address of data within a "fixed" expression and that data does not require it (i.e. it is not managed by GC) you will get this compiler diagnostic:

"You cannot use the fixed statement to take the address of an already fixed expression".

Personally I hate this message, it should be more like this:

"You can only use the fixed statement to take the address of managed data".

Imagine you had a C# object (a true managed object) but that it had a member that was simply an "int" then you would need to use "fixed" because although the int itself is unmanaged (and you can take its address) the object it resides in, is not and may move, this is why we have "fixed".

Here is some elementary code that requires fixed:

public class O
{
String S;
StringBuilder B;
public int I;

}


public unsafe partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
Bitmap I = Properties.Resources.cab;

imageList1.Images.Add(I);

listView1.Items[0].ImageIndex = 0;
}

private unsafe void Method()
{
O ob = new O();

fixed (int* p = &(ob.I))
{


}

}
}

If you try to take the address of member "I" without using "fixed" you will get an error, one can ONLY take addresses of managed (members) by using fixed.

It seems to me that if the compilers does not insist that you use "fixed" then it is 100% safe to assume that you don't need to use "fixed".

Note however, that this compiles:

private unsafe void LoopHole(int arg)
{
int* J = &(arg);

}

But this does not (it requires fixed)

private unsafe void LoopHole(ref int arg)
{
int* J = &(arg);

}

The compiler has no way (in the latter case) of knowing whether the argument passed in at runtime will be managed or unmanaged; if we do pass in a ref to a pure unmanaged int however, then the runtime will (it seems) actually fix the address of the unmanged int, I have no idea if this is valid or could lead to a failure.

Hugh