LKeene

Hello all,

I'm trying to figure out how to marshall an int array and an int pointer ("normal" int should marshall automatically) to an unmanaged dll function. Given the dll function as follows, just for purposes of illustration:

SomeDLLfunc(int* i1, int length, int* result)

{

//Loop through and compute a result:

for(int x=0; x<length; x++){

*(result) += *(il+x);

}

}

Then, in .NET 2.0 do I just do the following (assuming the proper "[DllImport(...}]") :

int[] numArray = new int[length];

int* result;

*(result) = 0;

SomDllfunc(numArray, length, ref result);

Is the above, in fact, correct



Re: Visual C# Language P/Invoke and data marshalling

Peter Ritchie

You could do something like:

Code Snippet

using System.Runtime.InteropServices;

static class NativeMethods

{

[DllImport("nativelib.dll", CharSet = CharSet.Unicode)]

public static extern void SomeDllfunc([MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]int [] il, int arrayLength, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 1)]int [] ol);

}

...and you'd call it as such:

Code Snippet

int[] il = new int[2]{1,2};

int[] ol = new int[2];

NativeMethods.SomeDllfunc(il, il.Length, ol);






Re: Visual C# Language P/Invoke and data marshalling

LKeene

Thanks Peter, but before I can try that I thought I'd make sure I've got the interop mechanism"working" so I wrote a dead-simple Dll to test things and, for some reason, I just can't get it to work.Here's what I've done:

In VC++ 2005 I created a non-MFC Win32 app, chose "Dll" as the application type and checked the "Empty project" check box. I created a new header file consisting solely of the following (modeled closely after code I found in a book):

#ifndef __MYLOUSYDLL_H

#define __MYLOUSYDLL_H

#ifndef __MYLOUSYDLL__

#define __MYLOUSYDLLLIB__ __declspec(dllimport)

#else

#define __MYLOUSYDLLLIB__ __declspec(dllexport)

#endif

__MYLOUSYDLLLIB__ int MathTest(int val);

#endif

My source file consists solely of the following:

#define __MYLOUSYDLL__

#include "MyLousyDll.h"

__MYLOUSYDLLLIB__ int MathTest(int val)

{

//Do some serious number crunching:

return (val+10);

}

I then created a little test app in c++ to test this by Including the header, linking to the .lib file and putting the .dll file in the .exe directory of the test app and calling the function. Everything worked fine. Then it was time to test it from C#. I created a new C# dialog app with a button on it. In my form.cs I have:

public partial class Form1 :Form

{

[DllImport("MyLousyDll.dll")]

private static extern int MathTest(int val);

.

.

.

private void button1_Click(object sender, EventARgs e)

{

int NewVal = MathTest(1);

}

That's it. I copy the Dll file into the folder containg the c# executable and run. It compiles but I get a runtime exception at the function call claiming "Unable to find an entry point named "MathTest" in dll "MyLousyDll.dll". The strange thing is that it worked fine in C++. Any ideas what might be going on here As always,thanks everyone!





Re: Visual C# Language P/Invoke and data marshalling

Peter Ritchie

If the code for MathTest is in a .CPP file, the code is compiled as C++ Code. A side effect of that is that exported names get "decorated" because of overloading (where more than one method of the same name exists). The exported name is decorated with extra characters to make it unique for those parameters. You can avoid this if you wrap your MathTest method in extern "C" {} then you'll get an export that will match C#'s request for "MathTest". For example:

Code Snippet

extern "C" {

__MYLOUSYDLLLIB__ int MathTest(int val)

{

//Do some serious number crunching:

return (val+10);

}

}






Re: Visual C# Language P/Invoke and data marshalling

LKeene

Presto! Thanks Peter, worked like a charm.





Re: Visual C# Language P/Invoke and data marshalling

LKeene

Oops...no it didn't. I'm having trouble getting any values out of the function. The original function doesn't return a value, rather it loaded the results into pointers that were passed as arguments. I'm not sure if the arrays that I'm passing using the [MarshalAs(...)] method you listed above are being passed purely by value or not. The code will run but I get garbage number in my "result" pointer. This is proving to be an EXTREMELY frustrating experience since all the tutorials I can find online seem to either take return values from the function itself, or do some tortured confusing process that I don't understand. I simply want to be able to pass in two int (or float) variables to the function and have them store the result. Passing them as "ref int result" doesn't seem to work.

Help, anybody! This is a terrible waste of time. As always thank you.





Re: Visual C# Language P/Invoke and data marshalling

LKeene

Okay, got it: need to marshall with "[In, Out]" attribute and then pass argument with "ref" keyword. Thanks, everyone!





Re: Visual C# Language P/Invoke and data marshalling

Peter Ritchie

Are you sure Your original example showed your C++ method was accepting a pointer to int (or an int array). Passing an array with the ref keyword would really be a pointer to an int pointer in C++ (int**).






Re: Visual C# Language P/Invoke and data marshalling

LKeene

Hi Peter, This is what I've ended up with (a little different than the original example):

Original 'C' Dll function:

"void PhaseCorrelation(int* sub1, int* sub2, int arrayLength, int width, int height, float* Xshift, float* Yshift)"

My P/Invoke from 'C#':

[DllImport("SpeckleAnalysis.dll")]

private static extern void PhaseCorrelation([MarshalAsAttribute(UnmanagedType.LPArray, SizeParamIndex = 2)] int[] subImage1, [MarshalAsAttribute(UnmanagedType.LPArray, SizeParamIndex = 2)] int[] subImage2, int ArrayLength, int subHeight, int subWidth, [In, Out] ref float Xresult, [In, Out] ref float Yresult);

Seems a shame to clutter up a perfectly fine C# app like this, but at the moment there aren't any good comprehensive and affordable numerical libraries available. Thanks for the help!





Re: Visual C# Language P/Invoke and data marshalling

Peter Ritchie

Ah, okay, the ref applied to a single value not an array. That makes sense then.