Задача: есть некий векторных площадной объект, который я хочу сохранить в tif или bmp формат
Использую GIS ToolKit Active v.12.0.1. При использовании v.12.0.0 программа не "вылетала", но и не производила создания изображения.
Проблема с выполнением процедуры LoadMapToPicture в результате чего возникает исключение System.AccessViolationException: 'Попытка чтения или записи в защищенную память. Это часто свидетельствует о том, что другая память повреждена.'
//инициализируем структуру фрагмента сохраняемой карты DFrame frame = new DFrame(); //устанавливаем координаты фрагмента равными координатам выбранной области frame.X1 = xPointMin; frame.Y1 = yPointMin; frame.X2 = xPointMax; frame.Y2 = yPointMax;
//кол-во бит на пиксел сохраняемого изображения (1, 8, 24-рекомендуемое значение) int bitcount = 24; //определяем разрешение(т/м) и масштаб карты int scale = MapScreenMain.MapScale; //определяем разрешение(т/д) double resolutionDPI = 508; //в одном метре 39,37 дюймов double sizeInchInMeter = 0.0254; //определяем разрешение(т/м) double resolutionDPM = resolutionDPI / sizeInchInMeter; //размер пикселя формируемого изображения в метрах на местности double sizePixelInMeter = scale / resolutionDPM; //размер формируемого изображения в метрах на местности по оси Y double deltaY = Math.Abs(frame.Y2 - frame.Y1); //ширина формируемого изображения в пикселях int width = (int)Math.Round( deltaY / sizePixelInMeter); //размер формируемого изображения в метрах на местности по оси X double deltaX = Math.Abs(frame.X2 - frame.X1); //высота формируемого изображения в пикселях int height = (int)Math.Round( deltaX / sizePixelInMeter);
// Сохранить карту в формате BMP, Tiff, RSW // map - карта,содержащая векторные, растровые и др. данные; // handle - диалог сопровождения процесса обработки; // dframe - фрагмент сохраняемой карты(в метрах на местности) // bitcount - кол-во бит на пиксел сохраняемого изображения (1, 8, 24-рекомендуемое значение) // scale - масштаб сохраняемого изображения // resolution - разрешающая способность сохраняемого изображения(т/м) // filename - имя файла сохраняемого изображения (*.bmp, *.tif); // handleMainWin - должен быть равен нулю
// При ошибке функция возвращает ноль // В СЛЕДУЮЩЕЙ СТРОЧКЕ И ВОЗНИКАЕТ ИСКЛЮЧЕНИЕ if (RstAPI.LoadMapToPicture(MapScreenMain.MapHandle, (int)this.Handle, ref frame, bitcount, (int)scale, (int)resolutionDPM, filename, 0) == 0) { MessageBox.Show("Произошла ошибка создания BMP файла!"); }
------
Метод LoadMapToPicture импортируется из библиотеки gispicex.dll [DllImport(GisLibrary3, CharSet = CharSet.Ansi)] public static extern int LoadMapToPicture(int map, int handle, ref DFrame dframe, int bitcount, int scale, int resolution, IntPtr filename, int handleMainWin);
Может кто сталкивался с этой проблемой, в чём может быть причина?
Алексей, уточните, для какой платформы разрабатываете приложение - для x32, или x64?
В объявлении LoadMapToPicture у Вас неточность при описании параметров функции:
Цитата
Метод LoadMapToPicture импортируется из библиотеки gispicex.dll [DllImport(GisLibrary3, CharSet = CharSet.Ansi)] public static extern int LoadMapToPicture(int map, int handle, ref DFrame dframe, int bitcount, int scale, int resolution, IntPtr filename, int handleMainWin);
Обратите внимание на объявление функции LoadMapToPicture в файле MAPPICEX.H из состава GIS ToolKit Active 12-ой версии:
Код
_PICIMP long int _PICAPI LoadMapToPicture(HMAP map,HMESSAGE handle,
DFRAME * dframe, long bitcount,
long scale, long resolution,
const char* filename,
HMESSAGE handleMainWin);
Параметр функции map имеет тип HMAP (maptype.h):
Код
#if defined(_M_X64) || defined(BUILD_DLL64)
typedef __int64 HMAP; // ИДЕНТИФИКАТОР ОТКРЫТОЙ ВЕКТОРНОЙ КАРТЫ
// (УКАЗАТЕЛЬ НА TMapAccess)
#else
typedef long int HMAP; // ИДЕНТИФИКАТОР ОТКРЫТОЙ ВЕКТОРНОЙ КАРТЫ
// (УКАЗАТЕЛЬ НА TMapAccess)
#endif
Параметры функции handle и handleMainWin имеют тип HMESSAGE (mapsyst.h):
Код
#define HMESSAGE HWND
Для платформы x64 переменные типа HMAP и HWND не поместятся в int.
В примерах для языка C#, которые поставляются вместе с установочным дистрибутивом, например, в "Mapedit" используется именно int map [DllImport("gisu64acces.dll", EntryPoint = "mapGetNodeView")] private static extern int mapGetNodeView(int map);
в то время как в .H _MAPIMP long int _MAPAPI mapGetNodeView(HMAP hMap);
Примеры исправим, int вместо HMAP - это пережиток прошлого. В GTK версии 12 это ошибка. Все так, как Вам объяснил Dmitry_: типы HMAP, HSITE, HOBJ и другие типы GIS ToolKit, начинающиеся с H, являются типом HANDLE, то есть их размер равен размеру указателя. Если такой параметр в функции описан как int, то это ошибка. Параметры идентификатора экземпляра класса работы с картой (HMAP), класса экземпляра работы с объектом (HOBJ) и т.п. должны быть описаны именно через типы (HMAP и HOBJ соответственно), а они уже в свою очередь должны быть определены исходя из разрядности системы.
Если я правильно понял, то должно получиться что-то следующее (C#)
Код
#if _WIN64
using HMAP = System.Int64;
#else
using HMAP = System.Int32;
#endif
using HWND = IntPtr;
using HMESSAGE = IntPtr;
public class RstAPI
{
public const string GisLibrary3 = "C:\\Windows\\SysWOW64\\gispicex.dll";
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DFrame
{
public double X1;
public double Y1;
public double X2;
public double Y2;
public bool Empty
{
get { return (X1.Equals(0.0) && X2.Equals(0.0) && Y1.Equals(0.0) && Y2.Equals(0.0)); }
}
}
//сохранить карту в формат BMP, TIFF, RSW
[DllImport(GisLibrary3, CharSet = CharSet.Ansi)]
public static extern long LoadMapToPicture(HMAP map, HMESSAGE handle, DFrame dframe, long bitcount, long scale, long resolution, IntPtr filename, HMESSAGE handleMainWin);
}
При вызове LoadMapToPicture
Код
//инициализируем структуру фрагмента сохраняемой карты
DFrame frame = new DFrame();
frame.X1 = minXX;
frame.Y1 = minYY;
frame.X2 = maxXX;
frame.Y2 = maxYY;
//переводим имя сохраняемого растра/карты в формат IntPtr
IntPtr filename = Marshal.StringToHGlobalAnsi(PathOrderFolder + "export.rsw");
if (RstAPI.LoadMapToPicture(mainMapView.MapHandle, this.Handle, frame, bitcount, scale, resolutionDPMInt, filename, this.Handle) == 0)
{
MessageBox.Show("Произошла ошибка");
}
срабатывает исключение System.AccessViolationException: 'Попытка чтения или записи в защищенную память. Это часто свидетельствует о том, что другая память повреждена.'
Алексей написал: //сохранить карту в формат BMP, TIFF, RSW [DllImport(GisLibrary3, CharSet = CharSet.Ansi)] public static extern long LoadMapToPicture(HMAP map, HMESSAGE handle, DFrame dframe, long bitcount, long scale, long resolution, IntPtr filename, HMESSAGE handleMainWin);
Параметр типа DFRAME в функцию LoadMapToPicture должен передаваться по указателю. Скорее всего, не хватает слова ref.
Цитата
//================================================-======================== // Сохранить карту в формате BMP, Tiff, RSW // hmap - идентификатор открытых данных // handle - диалог сопровождения процесса обработки; // dframe - фрагмент сохраняемой карты(в метрах на местности) // bitcount - кол-во бит на пиксел сохраняемого изображения (16, 24-рекомендуемое значение, 32) // scale - масштаб сохраняемого изображения // resolution - разрешающая способность сохраняемого изображения(т/м) // filename - имя файла сохраняемого изображения (*.bmp, *.tif); // handleMainWin - должен быть равен нулю // При ошибке функция возвращает ноль // // Диалогу визуального сопровождения процесса обработки посылаются // сообщения: // - (WM_PROGRESSBAR) Извещение об изменении состояния процесса // WPARAM - текущее состояние процесса в процентах (0% - 100%) // Если функция-отклик возвращает WM_PROGRESSBAR, то процесс завершается. // // - (WM_ERROR) Извещение об ошибке // LPARAM - указатель на структуру ERRORINFORMATION // Структура ERRORINFORMATION описана в picexprm.h, // WM_PROGRESSBAR и WM_ERROR - в maptype.h //================================================-======================== _PICIMP long int _PICAPI LoadMapToPicture(HMAP hmap,HMESSAGE handle, DFRAME * dframe, long bitcount, long scale, long resolution, const char* filename, HMESSAGE handleMainWin);
_PICIMP long int _PICAPI LoadMapToPictureUn(HMAP map,HMESSAGE handle, DFRAME * dframe, long bitcount, long scale, long resolution, const WCHAR* filename, HMESSAGE handleMainWin);
Справочно: при вызове MAPAPI объявить типы HMAP, HSITE, HOBJ и другие типы GIS ToolKit, начинающиеся с H, являются типом HANDLE, можно размером указателя (также как Вы объявили HWND через IntPtr).
public static extern long LoadMapToPicture(HMAP map, HMESSAGE handle, ref DFrame dframe, long bitcount, long scale, long resolution, IntPtr filename, HMESSAGE handleMainWin)
первый параметр MapHandle (идентификатор открытой основной карты) имеет тип int, преобразуем в IntPtr и получаем вызов вида
в результате исключение LoadMapToPicture разбалансировал стек. Вероятно, это вызвано тем, что управляемая сигнатура PInvoke не совпадает с неуправляемой целевой сигнатурой.
Алексей написал: первый параметр MapHandle (идентификатор открытой основной карты) имеет тип int, преобразуем в IntPtr и получаем вызов вида
Да, прошу прощения, я не очень точно выразился:
Цитата
Денис Вицко написал: при вызове MAPAPI объявить типы HMAP, HSITE, HOBJ и другие типы GIS ToolKit, начинающиеся с H, являются типом HANDLE, можно размером указателя (также как Вы объявили HWND через IntPtr).
имелось в виду только MAPAPI, без использования компонент (в x32 можно преобразовать в IntPtr, а в x64 вообще компоненты использовать не стоит).
К сожалению, компоненты GIS ToolKit Active имеют проблемы работоспособности в x64 приложениях. В рамках одной *.ocx библиотеки не удается корректно реализовать методы с параметрами переменного размера в зависимости от разрядности. Вы уже и сами заметили, что MapView.MapHandle возвращает int, что x64 приведет к ошибке при выходе приложения из области "нижней" адресации памяти. Пока однозначного решения данной проблемы у нас нет. Гарантированно работать и в x32-, и в x64- разрядных приложениях может только код, основанный на вызовах MAPAPI. Компоненты - только в x32.
Цитата
Алексей написал: в результате исключение LoadMapToPicture разбалансировал стек. Вероятно, это вызвано тем, что управляемая сигнатура PInvoke не совпадает с неуправляемой целевой сигнатурой.
Это связано не с объявлением HMAP как IntPtr. Проблема в описании параметров типа long. В C# не зависимо от разрядности проекта этот тип имеет длину 8 байт и реально является типом long long, вопреки собственному определению типов данных от Microsoft. В MAPAPI (как и в WINAPI) long - это integer (4 байта).
Кроме того, надо внимательно следить за платформой, под которую выполняется сборка проекта. В C# установка активной целевой платформы сделана чрезвычайно неудобно и не всегда понятно, что собралось. А платформу "AnyCPU" вообще лучше сразу удалить из опций проекта и решения, чтобы избежать путаницы. Особенно, когда речь идет о вызовах неуправляемого кода и когда программа использует платформозависимые типы данных.
Вот рабочий код вызова LoadMapToPicture с HMAP=IntPtr:
Скрытый текст
Код
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Diagnostics;
using System.IO;
namespace testapi
{
using HWND = IntPtr;
using HMESSAGE = IntPtr;
using HMAP = IntPtr;
class Program
{
#if _WIN64
public const string DLLMapAcces = "gisu64acces.dll";
public const string DLLMapPicEx = "gisu64picex.dll";
#else
public const string DLLMapAcces = "gisuacces.dll";
public const string DLLMapPicEx = "gisupicex.dll";
#endif
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DFrame
{
public double X1;
public double Y1;
public double X2;
public double Y2;
public bool Empty
{
get { return (X1.Equals(0.0) && X2.Equals(0.0) && Y1.Equals(0.0) && Y2.Equals(0.0)); }
}
}
//открыть карту
[DllImport(DLLMapAcces, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern HMAP mapOpenData(string name, int mode);
//закрыть карту
[DllImport(DLLMapAcces, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern void mapCloseData(HMAP map);
// габариты карты
[DllImport(DLLMapAcces, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern double mapGetMapX1(HMAP hMap);
[DllImport(DLLMapAcces, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern double mapGetMapX2(HMAP hMap);
[DllImport(DLLMapAcces, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern double mapGetMapY1(HMAP hMap);
[DllImport(DLLMapAcces, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern double mapGetMapY2(HMAP hMap);
//сохранить карту в формат BMP, TIFF, RSW
[DllImport(DLLMapPicEx, CharSet = CharSet.Ansi, CallingConvention = CallingConvention.StdCall)]
public static extern long LoadMapToPicture(HMAP map, HMESSAGE handle, ref DFrame dframe, int bitcount, int scale, int resolution, string filename, HMESSAGE handleMainWin);
static void Main(string[] args)
{
Console.WriteLine("Начало выполнения...");
string mappath = @"C:\Users\Public\Documents\Panorama\Panorama12\Data\noginsk\noginsk.sitx";
Console.WriteLine("Открытие карты: " + mappath);
HMAP aMap = mapOpenData(mappath, 0);
if (aMap != null)
{
Console.WriteLine("... успешно");
DFrame frame;
Console.WriteLine("Запрос габаритов...");
frame.X1 = mapGetMapX1(aMap);
frame.Y1 = mapGetMapY1(aMap);
frame.X2 = mapGetMapX2(aMap);
frame.Y2 = mapGetMapY2(aMap);
if (!frame.Empty)
Console.WriteLine("... успешно");
Console.WriteLine("Экспорт графики...");
string filename = @"C:\Users\Public\Documents\Panorama\Panorama12\Data\noginsk\noginsk.bmp";
//кол-во бит на пиксел сохраняемого изображения (1, 8, 24-рекомендуемое значение)
int bitcount = 24;
//определяем разрешение(т/м) и масштаб карты
int scale = 100000;
//определяем разрешение(т/д)
int resolutionDPI = 508;
//в одном метре 39,37 дюймов
double sizeInchInMeter = 0.0254;
//определяем разрешение(т/м)
double resolutionDPM = resolutionDPI / sizeInchInMeter;
//размер пикселя формируемого изображения в метрах на местности
double sizePixelInMeter = scale / resolutionDPM;
long result = LoadMapToPicture(aMap, (HMESSAGE)null, ref frame, bitcount, scale, (int)sizePixelInMeter, filename, (HMESSAGE)null);
if (result != 0)
Console.WriteLine("... успешно");
else
Console.WriteLine("... Ошибка!");
Console.WriteLine("Закрытие карты.");
mapCloseData(aMap);
}
else
Console.WriteLine("... Ошибка!!!");
Console.WriteLine("Программа завершена. Нажмите любую клавишу.");
Console.ReadKey();
}
}
}