views:

196

answers:

2

I am trying to use the GPS_DEVICE structure from within .NET CF 3.5 and I keep getting an error (87) returned from the call to GPSGetDeviceState. As far as I know, 87 means invalid parameter, but I do not know which parameter is invalid!

Can anybody please advise to what I have done wrong with my code as I have now spent the best part of two nights getting nowhere.

The simple test case is...

NativeMethods.GpsDevice gpsDevice = new NativeMethods.GpsDevice();
int result = NativeMethods.GPSGetDeviceState(ref gpsDevice);

My interop is defined as follows...

private const string GpsApi = @"gpsapi.dll";

private const int GPS_VERSION_1 = 1;
private const int GPS_MAX_SATELLITES = 12;
private const int GPS_MAX_PREFIX_NAME = 16;
private const int GPS_MAX_FRIENDLY_NAME = 64;

[DllImport(GpsApi)]
public static extern int GPSGetDeviceState(ref GpsDevice pGPSDevice);

[StructLayout(LayoutKind.Sequential)]
public class GpsDevice {

public UInt32 dwVersion;

 public UInt32 dwSize;

 public IoctlServiceStatus dwServiceState;

 public IoctlServiceStatus dwDeviceState;

 public FileTime ftLastDataReceived;

 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeMethods.GPS_MAX_PREFIX_NAME)]
 public string szGPSDriverPrefix;

 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeMethods.GPS_MAX_PREFIX_NAME)]
 public string szGPSMultiplexPrefix;

 [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeMethods.GPS_MAX_FRIENDLY_NAME)]
 public string szGPSFriendlyName;

 public GpsDevice() {

  this.dwVersion = NativeMethods.GPS_VERSION_1;
  this.dwSize = (UInt32)Marshal.SizeOf(this);

 }

}

[StructLayout(LayoutKind.Sequential)]
public struct FileTime {
 UInt32 dwLowDateTime;
 UInt32 dwHighDateTime;
}

public enum IoctlServiceStatus : uint {

 Off = 0,

 On = 1,

 StartingUp = 2,

 ShuttingDown = 3,

 Unloading = 4,

 Uninitialised = 5,

 Unknown = 0xffffffff

}

I hope I have included all information that may be required; if not, please prompt me for more.

Thanks in advance.

+3  A: 

Define GpsDevice as struct and not as class, then use MArshal class to marshal it to unmanaged code

See example here on how to convert struct to pionter and back.

http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.marshal.structuretoptr.aspx

Alex Reitbort
Your reference to StructureToPtr and PtrToStructure is correct and will also work with classes as well as structures (+1).I was expecting/hoping however to be able to pass the structure as a parameter to the GPSGetDeviceState API - this seems to be where my solution is failing.That said, the clue may be in the API definition...DWORD GPSGetDeviceState( GPS_DEVICE *pGPSDevice);Which seems to expect a pointer!
Martin Robins
It won't work with class, only with struct. That is the reason that it is called StructToPtr and not ObjectToPtr.
Alex Reitbort
+1  A: 

I changed the definition of the GpsDevice from a class to a structure and it works!

[StructLayout(LayoutKind.Sequential)]
public struct GpsDevice {
...

}

Had to remove the constructor though (which was actually the reason that I coded it as a class in the first place - so that I could automatically initialise the dwSize and dwVersion members).

NativeMethods.GpsDevice gpsDevice = new NativeMethods.GpsDevice {
    dwVersion = NativeMethods.GPS_VERSION_1,
    dwSize = (uint)Marshal.SizeOf(typeof(NativeMethods.GpsDevice))
};

result = NativeMethods.GPSGetDeviceState(ref gpsDevice);

Real shame that you cannot have constructors on structs!

Martin Robins
structs have constructors http://msdn.microsoft.com/en-us/library/aa288208(VS.71).aspx
Allen
Allen - Sorry, perhaps I should have been more specific. I wish structs could have parameterless constructors.
Martin Robins
Just add a static Create method that encapsulates the initialization of the struct size and version and returns a new instance of the struct.
Keith Hill