GeoTrans p / invoke包装器NullReferenceException

时间:2011-08-13 17:27:59

标签: .net c++ c#-4.0

我正在尝试公开我需要从GeoTrans c ++库调用的方法,但是我遇到了问题。任何帮助都会很棒!

我有以下c ++文件,我正在运行nmake反编译成一个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();
}

然后我在c#class中进行了以下p / invoke调用..

[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);

调用上述任何p / invoke方法会导致NullReferenceException。似乎这个问题在c ++代码本身内部,但是,不是c ++专家,我不确定问题是什么......

请帮助!!

2 个答案:

答案 0 :(得分:2)

我建议您调试代码。您可以在C#项目的设置(调试选项卡)中启用调试非托管代码。

请同时发布主叫代码。

备注:p / invoke的字符串输出参数通常使用StringBuilder类完成。

[编辑]字符串输出有问题 - 谁为字符串提供存储空间以及谁将其释放(如果需要)?唯一有用的解决方案是将存储(作为char *)及其长度传递给C ++函数。如上所述,在C#端使用StringBuilder。

答案 1 :(得分:1)

我使用了你发布的代码和最新版本的库,它对我来说很好用。您可以考虑的一件事是包装为C ++ / CLI而不是使用P / Invoke,但这是另一个主题。

我正在假设你使用的是Visual Studio 2010(哦,一个人必须从某个地方开始:-))。

显然不好的一件事是:

<强>本地

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

和C#:

[的DllImport( “CoordinateConversionWrapper.dll”)] private static extern void convertGeocentricToMGRS(double x,double y,double z,ref char [] mgrsString,Precision precision);

执行:

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();
}

请注意,char作为指针传入,并且使用了CoTaskMemFree / CoTaskMemAlloc / strcpy(包括用于CoTaskMemAlloc的Objbase.h)

在C#代码中,您可以:

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

其中:

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

可能存在其他可能性......

其他一些有用的东西:

为了能够调试,请确保:

在工具&gt;选项&gt;调试&gt;一般情况下,“启用我的代码”未选中。

在项目&gt;属性&gt;选中“调试”选项卡“启用非托管代码调试”。

在C#方法中放置一个断点,当到达断点时,您可以进入F11并到达C ++代码......

我通过选择“使用多字节字符集”(配置属性\常规\字符集)编译了C ++ Dll

如果CoordinateConversionService构造函数无法加载配置文件(它会搜索它们看起来似乎在可以通过名为MSPCCS_DATA的环境变量配置的路径中并且未定义环境变量),它似乎会抛出一个CoordinateConversionException。它在相对于exe路径的../../data/中查找它们。

可能在C ++包装器方法中,您可能希望捕获被调用方法可能引发的任何异常并返回错误代码... I.e。触发异常的其他情况是由于输入坐标无效等原因造成的。

正如我所说,我有一个有效的例子,如果你愿意,我可以发给你......