-1

I have below implementation in C++ (have created a DLL of the same)

double *getData()
{
    double *eyeTrackData = new double[10];
    const unique_ptr<Fove::IFVRHeadset> headset{ Fove::GetFVRHeadset() };

    CheckError(headset->Initialise(Fove::EFVR_ClientCapabilities::Gaze), 
"Initialise");

    Fove::SFVR_GazeVector leftGaze, rightGaze;
    const Fove::EFVR_ErrorCode error = headset->GetGazeVectors(&leftGaze, 
    &rightGaze);


    // Check for error
    switch (error)
    {

    case Fove::EFVR_ErrorCode::None:
        eyeTrackData[0] = (double)leftGaze.vector.x;
        eyeTrackData[1] = (double)leftGaze.vector.y;
        eyeTrackData[2] = (double)rightGaze.vector.x;
        eyeTrackData[3] = (double)rightGaze.vector.y;
        break;


    default:
        // Less common errors are simply logged with their numeric value
        cerr << "Error #" << EnumToUnderlyingValue(error) << endl;
        break;
    }

    return eyeTrackData;
}

I have included

extern "C"
{
    __declspec(dllexport) double *getData();
}

in the header file.

I try to receive this in C-sharp.

[DllImport("C:\\Users\\BME 320 - Section 1\\Documents\\Visual Studio 2015\\Projects\\EyeTrackDll\\x64\\Debug\\EyeTrackDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr eyeData();

But I don't know how to receive the array inside buttonClick event.

I appreciate any help on this.

user527248
  • 45
  • 7
  • Read the array with Marshal.Copy. But, how do you plan to deallocate the array? And how does the caller know the length of the array? If the length is fixed then better for caller to allocate. – David Heffernan Jun 13 '18 at 20:42

1 Answers1

0

Following the @DavidHeffernan tips, I will try to give you an example of how to achieve your objective. Please, take the C# part as pseudocode, as I'm not expert in that language.

I would use a struct as previously said, just to make things clearer, and I consider it a good practice to give some type protection and the possibility to add a "size" attribute to the struct if don't know exactly how many doubles will be. In your case, as always are 4, isn't needed.

Function in C++ Library:

void getData(double* eyeTrackData)
{
    const unique_ptr<Fove::IFVRHeadset> headset{ Fove::GetFVRHeadset() };

    CheckError(headset->Initialise(Fove::EFVR_ClientCapabilities::Gaze), "Initialise");

    Fove::SFVR_GazeVector leftGaze, rightGaze;
    const Fove::EFVR_ErrorCode error = headset->GetGazeVectors(&leftGaze, &rightGaze);


    // Check for error
    switch (error)
    {

    case Fove::EFVR_ErrorCode::None:
        eyeTrackData[0] = (double)leftGaze.vector.x;
        eyeTrackData[1] = (double)leftGaze.vector.y;
        eyeTrackData[2] = (double)rightGaze.vector.x;
        eyeTrackData[3] = (double)rightGaze.vector.y;
        break;

    default:
        // Less common errors are simply logged with their numeric value
        cerr << "Error #" << EnumToUnderlyingValue(error) << endl;
        break;
    }
}

C# side:

[DllImport("C:\\Users\\BME 320 - Section 1\\Documents\\Visual Studio 2015\\Projects\\EyeTrackDll\\x64\\Debug\\EyeTrackDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void eyeData(IntPtr); 

private void button1_Click(object sender, EventArgs e) 
{ 
    try 
    { 
        IntPtr ipr = Marshal.AllocHGlobal(4); // Memory blob to pass to the DLL
        eyeData(ipr);
        double[] eyeTrackData = new double[4]; // Your C# data
        Marshal.Copy(ipr, eyeTrackData, 0, 4); // Convert?
    }
    finally 
    { 
        Marshal.FreeHGlobal(ipr); 
    }
} 

Again, sorry for my "bad C#" xD. Hope this helps.

LuisGP
  • 431
  • 3
  • 13
  • does the getData() remain the same ? how about receiving them at the c# end ? – user527248 Jun 13 '18 at 20:21
  • LuisGP solution works, but it produces a memory leak because getDat() returns an array of doubles allocated trough new, and .Net does not know how to free it. Either change the method to use a double buffer supplied by the caller or stay with IntPtr return, provide an additional deleteData()-Method to free the intptr and use System.Runtime.Interop.Marshal.PtrToStructure – wech Jun 13 '18 at 20:22
  • You must use the struct instead the double*, of course. In the C# side I'm not an expert. As wech says, it will produce a memory leak if you don't provide a deleteData() method, of manage the liberation of memory in any other way. – LuisGP Jun 13 '18 at 20:24
  • No need to use a struct at all. Nonsense. IntPtr is fine. You use Marshal class to read the unmanaged memory. Deallocator has to be exported to. Or allocation on a shared heap. – David Heffernan Jun 13 '18 at 20:40
  • This is where I am stuck at C-sharp end: private void button1_Click(object sender, EventArgs e) { IntPtr ipr = eyeData(); double[] eyeTrackData = new double[10]; int j = 0; eyeTrackData[0] = Marshal.PtrToStringAnsi(Marshal.ReadIntPtr(ipr, 11*j)); The last line gives error. Also, I am passsing array of double type, so how do I deal with this ? – user527248 Jun 13 '18 at 20:49
  • An array of double is not a string – David Heffernan Jun 13 '18 at 20:51
  • @DavidHeffernan but I need to receive data that is sent to be double. – user527248 Jun 13 '18 at 21:06
  • Right. So why are you trying to convert it to text? Rather than communicate in comments to this poor answer, how about addressing the points in my comment to the question? – David Heffernan Jun 13 '18 at 21:08
  • The length of array returned from unmanaged code will always be four. I want to convert it to text, because I want to display it on the TextBox. – user527248 Jun 13 '18 at 21:17
  • OK. Have the caller allocate it and pass the array as a parameter that the callee populates. – David Heffernan Jun 14 '18 at 05:46
  • [DllImport("C:\\Users\\BME 320 - Section 1\\Documents\\Visual Studio 2015\\Projects\\EyeTrackDll\\x64\\Debug\\EyeTrackDll.dll", CallingConvention = CallingConvention.Cdecl)] public static extern IntPtr eyeData(); private void button1_Click(object sender, EventArgs e) { IntPtr ipr = eyeData(); try { double[] eyeTrackData = new double[4]; Marshal.Copy(ipr, eyeTrackData, 0, 4); finally { Marshal.FreeHGlobal(ipr); } – user527248 Jun 14 '18 at 16:06
  • I wrote this and getting EntryPoinNotFound exception. Any suggestions @DavidHeffernan – user527248 Jun 14 '18 at 16:08
  • I'm fed up with doing this in comments to an answer. I'm out. You keep ignoring my suggestions so it's just a waste of my time. – David Heffernan Jun 14 '18 at 16:24
  • I updated my answer to try summarize all David Heffernan tips. – LuisGP Jun 14 '18 at 17:13
  • @LuisGP your solution doesn't work. eyeData(ipr); error: Cannot convert from System.IntPtr[] to System.IntPtr Also, Marshal.Copy(ipr, eyeTrackData, 0, 4); Cannot convert from double[] to int – user527248 Jun 14 '18 at 17:25
  • I edited my answer with a little more of IntPtr knowledge I just googled. Why didn't you? – LuisGP Jun 14 '18 at 17:30
  • I did that. I am now stuck with EntryPointNotFoundException which I googled a lot and came up with 2 fixes, which didn't work out. – user527248 Jun 14 '18 at 19:02
  • Did you see this [link](https://stackoverflow.com/questions/12892271/entrypointnotfoundexception-when-binding-c-dll-in-c-sharp)? Maybe you should try [DependencyWalker](http://www.dependencywalker.com/). Sounds like you are not exporting/importing the function in the dll properly. – LuisGP Jun 14 '18 at 20:09
  • It could be made simpler with (in C#): `void eyeData(double[] array);` and then `double[] eyeTrackData = new double[4]; `eyeData(eyeTrackData);`. There is autopinning of arrays, and primitive type arrays aren't marshaled but simply passed as reference. – xanatos Jun 15 '18 at 06:28