GeoTrans p / invoke wrapper NullReferenceException - PullRequest
4 голосов
/ 13 августа 2011

Я пытаюсь предоставить методы, которые мне нужно вызывать из библиотеки 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();
}

У меня затем следующие вызовы p / invokemy 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);

Вызов любого из перечисленных выше методов p / invoke приводит к исключению NullReferenceException.Кажется, что проблема в самом коде c ++, но, не будучи экспертом в c ++, я не уверен, в чем проблема ...

Пожалуйста, помогите !!

Ответы [ 2 ]

2 голосов
/ 19 августа 2011

Предлагаю отладить ваш код.Вы можете включить отладку неуправляемого кода в настройках (вкладка «Отладка») вашего C # проекта.

Пожалуйста, отправьте также вызывающий код.

Примечание: строковые параметры вывода для p / invoke обычно выполняются с помощью класса StringBuilder.

[EDIT] Существует проблема свывод строки - кто обеспечивает хранение строки и кто ее освобождает (если необходимо)?Единственное полезное решение - передать хранилище (как char *) и его длину в функцию C ++.Используйте StringBuilder на стороне C #, как упомянуто.

1 голос
/ 23 августа 2011

Я использовал код, который вы разместили вместе с последней версией библиотеки, и он отлично работает для меня. Одна вещь, которую вы могли бы рассмотреть - это оборачивать как 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 & precision)

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

Обратите внимание, что символ передается как указатель на указатель, и что используются CoTaskMemFree / CoTaskMemAlloc / strcpy (включая Objbase.h для CoTaskMemAlloc)

А в коде 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
    }

Возможно, существуют другие возможности сделать это ...

Некоторые другие полезные вещи:

Для возможности отладки убедитесь, что:

В меню Инструменты> Параметры> Отладка> Общие флажок «Включить только мой код» не установлен.

В Project> Properties> вкладка Debug установлен флажок «Включить отладку неуправляемого кода».

Поместите точку останова в метод C #, и когда точка останова будет достигнута, вы можете перейти в F11 и достичь кода C ++ ...

Я скомпилировал C ++ Dll, выбрав «Использовать многобайтовый набор символов» (Свойства конфигурации \ Общие \ Набор символов)

Также может показаться, что конструктор CoordinateConversionService выдает исключение CoordinateConversionException, если он не может загрузить файлы конфигурации (при их поиске он выглядит как путь, который можно настроить через переменную среды с именем MSPCCS_DATA, и если переменная среды не определена он ищет их в ../../data/ относительно пути exe).

Вероятно, в методах-оболочках C ++ может быть, вы хотите перехватить любые исключения, которые могут быть вызваны вызванными методами, и вернуть код ошибки ... Т.е. другие ситуации, которые вызывают исключения, вызваны неправильными входными координатами и т. д.

Как я уже сказал, у меня есть рабочий пример, поэтому, если хотите, я могу отправить его вам ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...