GeoTrans p/invoke 包装器 NullReferenceException

发布于 2024-11-29 07:56:09 字数 5195 浏览 0 评论 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# 类中有以下 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++ 专家,我不确定问题是什么......

请帮助!

I'm trying to expose methods I need to call from the GeoTrans c++ library but, am running into issues. Any help would be great!

I have the following c++ file that i'm running nmake against to compile into a 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();
}

I then 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 results in a NullReferenceException. It seems like the issue is within the c++ code itself but, not being a c++ expert I'm not sure what the issue is...

Please Help!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

终遇你 2024-12-06 07:56:09

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

请同时发布调用代码。

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

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

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

Please post also the calling code.

Remark: string output parameters for p/invoke are typically done with the StringBuilder class.

[EDIT] There is a problem with the string output - who provides the storage for the string and who is freeing it (if needed)? The only useful solution is to pass the storage (as char*) and the length of it into the C++ function. Use StringBuilder on the C# side as mentioned.

水波映月 2024-12-06 07:56:09

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

我假设您正在使用 Visual Studio 2010(哦,好吧,必须从某个地方开始:-))。

显然不行的一件事是:

Native:

extern "C"__declspec(dllexport) void __stdcall ConvertGeocentricToMGRS(const double x, const double y, const double z, char*& mgrsString, Precision: :Enum& 精度)

和 C#:

[DllImport("CooperativeConversionWrapper.dll")]
私有静态外部无效convertGeocentricToMGRS(双x,双y,双z,ref char [] mgrsString,精度精度);

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
    }

可能存在其他可能性来执行此操作...

其他一些有用的东西:

为了能够调试,请确保:

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

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

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

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

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

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

正如我所说,我有一个工作示例,所以如果您愿意,我可以将其发送给您......

I used the code you posted together with the latest version of the library and it works fine for me. One thing you might consider is wrapping as C++/CLI instead of using P/Invoke but this is another topic.

I am working under the assumption you are using Visual Studio 2010 (oh well one has to start somewhere :-) ).

One thing that is clearly not ok is:

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

Do:

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 the char is passed in as pointer to pointer, and that CoTaskMemFree/CoTaskMemAlloc/strcpy are used (include 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
    }

Other possibilities probably exist to do this...

Some other useful things:

In order to be able to debug make sure that:

In Tools > Options > Debugging > General, "Enable Just My Code" is unchecked.

In Project > Properties > Debug tab, "Enable unmanaged code debugging" is checked.

Place a breakpoint in the C# method and when the breakpoint is reached you can step into F11 and reach the C++ code...

I compiled the C++ Dll by choosing "Use Multi-Byte Character Set" (Configuration Properties\General\Character Set)

Also it would seem the CoordinateConversionService constructor throws a CoordinateConversionException if it is unable to load the configuration files (it searches them it would seem 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/ relative to the exe path).

Probably in the C++ wrapper methods maybe you would like to catch any exceptions that can be thrown by the called methods and return an error code... I.e. other situations that trigger exceptions is due to invalid input coordinates and so on.

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文