GeoTrans p / invoke wrapper NullReferenceException

I am trying to expose the methods that I need to call from the C ++ GeoTrans library, but I ran into problems. Any help would be great!

I have the following C ++ file that I run nmake to compile in dll.

#include <iostream> #include "CoordinateConversionService.h" #include "CoordinateSystemParameters.h" #include "GeodeticParameters.h" #include "CoordinateTuple.h" #include "GeodeticCoordinates.h" #include "CartesianCoordinates.h" #include "Accuracy.h" #include "MGRSorUSNGCoordinates.h" #include "UTMParameters.h" #include "UTMCoordinates.h" #include "CoordinateType.h" #include "HeightType.h" #include "CoordinateConversionException.h" using MSP::CCS::Precision; int main(int argc, char **argv){} extern "C"__declspec(dllexport) void __stdcall convertGeodeticToGeocentric(const double lat,const double lon, const double height, double& x, double& y, double& z) { MSP::CCS::CoordinateSystemParameters geocentricParameters(MSP::CCS::CoordinateType::geocentric); MSP::CCS::CoordinateConversionService ccs( "WGE", &geodeticParameters, "WGE", &geocentricParameters ); MSP::CCS::Accuracy sourceAccuracy; MSP::CCS::Accuracy targetAccuracy; MSP::CCS::GeodeticCoordinates sourceCoordinates(MSP::CCS::CoordinateType::geodetic, lon, lat, height); MSP::CCS::CartesianCoordinates targetCoordinates(MSP::CCS::CoordinateType::geocentric); ccs.convertSourceToTarget( &sourceCoordinates, &sourceAccuracy, targetCoordinates, targetAccuracy ); x = targetCoordinates.x(); y = targetCoordinates.y(); z = targetCoordinates.z(); } extern "C"__declspec(dllexport) void __stdcall convertGeocentricToGeodetic(const double x, const double y, const double z, double& lat,double& lon, double& height) { MSP::CCS::CoordinateSystemParameters geocentricParameters(MSP::CCS::CoordinateType::geocentric); MSP::CCS::GeodeticParameters geodeticParameters(MSP::CCS::CoordinateType::geodetic, MSP::CCS::HeightType::ellipsoidHeight); MSP::CCS::CoordinateConversionService ccs( "WGE", &geocentricParameters, "WGE", &geodeticParameters ); MSP::CCS::Accuracy sourceAccuracy; MSP::CCS::Accuracy targetAccuracy; MSP::CCS::CartesianCoordinates sourceCoordinates(MSP::CCS::CoordinateType::geocentric, x, y, z); MSP::CCS::GeodeticCoordinates targetCoordinates; ccs.convertSourceToTarget( &sourceCoordinates, &sourceAccuracy, targetCoordinates, targetAccuracy ); lat = targetCoordinates.latitude(); lon = targetCoordinates.longitude(); height = targetCoordinates.height(); } extern "C"__declspec(dllexport) void __stdcall convertGeocentricToUTM(const double x, const double y, const double z, long& zone, char& hemisphere, double& easting, double& northing) { MSP::CCS::CoordinateSystemParameters geocentricParameters(MSP::CCS::CoordinateType::geocentric); MSP::CCS::UTMParameters utmParameters(MSP::CCS::CoordinateType::universalTransverseMercator, 1, 0); MSP::CCS::CoordinateConversionService ccs( "WGE", &geocentricParameters, "WGE", &utmParameters ); MSP::CCS::Accuracy sourceAccuracy; MSP::CCS::Accuracy targetAccuracy; MSP::CCS::CartesianCoordinates sourceCoordinates(MSP::CCS::CoordinateType::geocentric, x, y, z); MSP::CCS::UTMCoordinates targetCoordinates; ccs.convertSourceToTarget( &sourceCoordinates, &sourceAccuracy, targetCoordinates, targetAccuracy ); zone = targetCoordinates.zone(); hemisphere = targetCoordinates.hemisphere(); easting = targetCoordinates.easting(); northing = targetCoordinates.northing(); } extern "C"__declspec(dllexport) void __stdcall convertGeocentricToMGRS(const double x, const double y, const double z, char*& mgrsString, Precision::Enum& precision) { MSP::CCS::CoordinateSystemParameters geocentricParameters(MSP::CCS::CoordinateType::geocentric); MSP::CCS::CoordinateSystemParameters mgrsParameters(MSP::CCS::CoordinateType::militaryGridReferenceSystem); MSP::CCS::CoordinateConversionService ccs( "WGE", &geocentricParameters, "WGE", &mgrsParameters ); MSP::CCS::Accuracy sourceAccuracy; MSP::CCS::Accuracy targetAccuracy; MSP::CCS::CartesianCoordinates sourceCoordinates(MSP::CCS::CoordinateType::geocentric, x, y, z); MSP::CCS::MGRSorUSNGCoordinates targetCoordinates; ccs.convertSourceToTarget( &sourceCoordinates, &sourceAccuracy, targetCoordinates, targetAccuracy ); mgrsString = targetCoordinates.MGRSString(); precision = targetCoordinates.precision(); } 

Then I have the following p / invoke calls in my C # class.

 [DllImport("CoordinateConversionWrapper.dll")] private static extern void convertGeodeticToGeocentric(double lat, double lon, double height, ref double x, ref double y, ref double z); [DllImport("CoordinateConversionWrapper.dll")] private static extern void convertGeocentricToMGRS(double x, double y, double z, ref char[] mgrsString, Precision precision); 

Calling any of the above p / invoke methods throws a NullReferenceException. It looks like the problem is in the C ++ code itself, but, not being an expert in C ++, I'm not sure what the problem is ...

Please, help!!

+4
source share
2 answers

I suggest debugging your code. You can enable debugging unmanaged code in the settings ("Debugging" tab) of your C # project.

Send also a call code.

Note: string output parameters for p / invoke are usually done using the StringBuilder class.

[EDIT] Problem with row exit - who provides storage for the row and who frees it (if necessary)? The only useful solution is to pass the storage (like char *) and its length to a C ++ function. Use StringBuilder from the C # side as indicated.

+2
source

I used the code that you posted with the latest version of the library and it works great for me. One thing you can consider is wrapping like a C ++ / CLI instead of using P / Invoke, but this is another topic.

I work under the assumption that you are using Visual Studio 2010 (well, good to start somewhere :-)).

One thing that is clearly out of order:

Native:

extern "C" __ declspec (dllexport) void __stdcall convertGeocentricToMGRS (const double x, const double y, const double z, char * & mgrsString, Precision :: Enum & precision)

and C #:

[DllImport ("CoordinateConversionWrapper.dll")] private static extern void convertGeocentricToMGRS (double x, double y, double z, ref char [] mgrsString, precision accuracy);

make:

 extern "C"__declspec(dllexport) void __stdcall convertGeocentricToMGRS(const double x, const double y, const double z, char** mgrsString, Precision::Enum& precision) { MSP::CCS::CoordinateSystemParameters geocentricParameters(MSP::CCS::CoordinateType::geocentric); MSP::CCS::CoordinateSystemParameters mgrsParameters(MSP::CCS::CoordinateType::militaryGridReferenceSystem); MSP::CCS::CoordinateConversionService ccs( "WGE", &geocentricParameters, "WGE", &mgrsParameters ); MSP::CCS::Accuracy sourceAccuracy; MSP::CCS::Accuracy targetAccuracy; MSP::CCS::CartesianCoordinates sourceCoordinates(MSP::CCS::CoordinateType::geocentric, x, y, z); MSP::CCS::MGRSorUSNGCoordinates targetCoordinates; ccs.convertSourceToTarget( &sourceCoordinates, &sourceAccuracy, targetCoordinates, targetAccuracy ); int nMGRSLen = strlen( targetCoordinates.MGRSString() ); ::CoTaskMemFree(*mgrsString); *mgrsString = (char *)::CoTaskMemAlloc(nMGRSLen + 1); strcpy( *mgrsString, targetCoordinates.MGRSString() ); precision = targetCoordinates.precision(); } 

Note that char is passed as a pointer to a pointer and used by CoTaskMemFree / CoTaskMemAlloc / strcpy (including Objbase.h for CoTaskMemAlloc)

And in C # code you can:

  [DllImport("MSPGeotransTest.dll", CharSet= CharSet.Ansi))] public static extern void convertGeocentricToMGRS(double x, double y, double z, ref string mgrsString, ref PrecisionEnum precision); 

Where:

  public enum PrecisionEnum : uint { degree = 0, tenMinute = 1, minute = 2, tenSecond = 3, second = 4, tenthOfSecond = 5, hundrethOfSecond = 6, thousandthOfSecond = 7, tenThousandthOfSecond = 8 } 

Perhaps there are other possibilities for this ...

Some other useful things:

To be able to debug, make sure that:

In the menu "Tools"> "Options"> "Debug"> "General", "Enable only my code" is not installed.

In the project> Properties> Debug tab, the option "Enable unmanaged code debugging" is checked.

Put a breakpoint in the C # method and when the breakpoint is reached, you can go to F11 and get the C ++ code ...

I compiled C ++ Dll by selecting "Use multibyte character set" (Configuration Properties \ General \ Character Set)

It would also seem that the CoordinateConversionService constructor throws a CoordinateConversionException if it cannot load the configuration files (it searches for them, it will appear in a path that can be configured through an environment variable named MSPCCS_DATA and if the environment variable is not defined it looks for them in. ./../data/ regarding exe path).

Perhaps in C ++ wrapper methods you can catch any exceptions that may be caused by called methods and return an error code ... Ie other situations that cause exceptions are associated with incorrect input coordinates, etc.

As I said, I have a working example, so if you want, I can send it to you ...

+1
source

All Articles