1

I have a regular dll with the following function exported.

extern "C" __declspec(dllexport) int FindNearestStuff(double _latitude, double _longitude , LocationStruct * locations[])

LocationStruct is very simple

struct LocationStruct
  {
   long positionIndex;
   long item;
  };

I'm tryign to call it from c# using

 [DllImport("myclever.dll", CharSet = CharSet.None)]
        private static extern int FindNearestStuff(double _latitude, double _longitude, 
                                                    ref LocationStruct [] locations);

It's all cool and funky and I can step into the dll function from the debugger. Inside the dll the LocationStruct array is populated correctly and all is very good.

The problem I have is when it returns back from the dll, the LocationStruct array is not coming back with the data - just empty values...

What am I missing?

8
  • struct LocationStruct { long roadIndex; long tdist; }; and in c# internal struct LocationStruct { public int roadIndex; //its a long in the C code public int tdist;//its a long in the C code }; Commented Nov 19, 2009 at 3:00
  • I don't think P/Invoke can handle double indirection here (array passed by ref on C# side, pointer-to-pointer on C++ side). Do you actually need it; won't a pointer to first element in the array of structs do in C++? Commented Nov 19, 2009 at 3:16
  • Easiest way I can think of is to use unsafe C#. Also remember to use StructLayout.Sequential Commented Nov 19, 2009 at 3:34
  • 1
    All C# structs are implicitly Sequential. Commented Nov 19, 2009 at 3:38
  • thanks Pavel; that just saved me a lot of typing! Commented Nov 19, 2009 at 3:41

2 Answers 2

2

thanks so much for your help - you certainly put me onthe right direction and i really appreciate your assistance!

This is the solution which seems to work for me;

[DllImport("myclever.dll", CharSet = CharSet.None)]
        private static extern int FindNearestStuff(double _latitude, double _longitude,  IntPtr locations);


public static int FindNearestStuff(double _latitude, double _longitude, LocationStruct[] locations)
        {
            int returnValue = -1;
            LocationStruct temp;
            temp.roadIndex = 1;
            temp.tdist = 1;
            int iStructSize = Marshal.SizeOf(temp);
            try
            {
                IntPtr locationsPtr = IntPtr.Zero;

                IntPtr buffer = Marshal.AllocHGlobal(iStructSize * 10);
                FindNearestRoads(_latitude, _longitude,  buffer);
                for (int i = 0; i < 10; i++)
                {
                    IntPtr ptr = new IntPtr(buffer.ToInt32() + iStructSize * i);
                    locations[i] = (LocationStruct)Marshal.PtrToStructure(ptr, typeof(LocationStruct));

                }

                returnValue = 0;
            }
            catch
            {
            }
            return returnValue;
        }
Sign up to request clarification or add additional context in comments.

2 Comments

and of course dont forget to Marshal.FreeHGlobal(buffer); !
If that works for you, you indeed had one level of indirection too much in your original code, just like Pavel hinted. You don't have to do all this allocation and copying yourself, you should be able to take your original function declaration and just remove the ref modifier on the array parameter.
1

I'm not sure that you will be able to do this automatically since C# has no way of knowing how many items are returned in the locations variable (I'm assuming that the return value of FindNearestStuff is the number of entries in locations.)

You will have to manually marshal your data using the Marshall class and a process like this:

[DllImport("myclever.dll", CharSet = CharSet.None)]
private static extern int FindNearestStuff(double _latitude, double _longitude, 
                                           out IntPtr locations);

public static LocationStruct[] FindNearestStuff(double latitude, double longitude) {
    IntPtr locationsPtr = IntPtr.Zero;
    int numLocations = FindNearestStuff(latitude, longitude, out locationsPtr);

    LocationsStruct[] locations = new LocationsStruct[numLocations];

    for (int i = 0; i < numLocations; i++) {

          // locationsPtr is a pointer to the struct, so read the value
          // at locationPtr which will be the address of the struct and
          // then manually marshal the struct from that address
          locaitonsStruct[i] = (LocationStruct)Marshal.PtrToStructure(
              Marshal.ReadIntPtr(locationsPtr), typeof(LocationsStruct));

          // Move to the location pointer to the next address of a 
          // pointer to a struct
          locationsPtr += IntPtr.Size;  
    }

    return locations;
}

I haven't actually tried this so caveat emptor.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.