Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[18.221.187.121] |
|
Сообщ.
#1
,
|
|
|
Приветствую!
Вот уже неделю (а то и больше) не могу разрешить следующую проблему: есть библиотека, написанная в Visual C++ .NET и проэкт, использующий ее, написанный в Visual Basic .NET. Библиотека предназначена для построения стрктуры данных в памяти (дву-связный список) по переданному ей массиву и работы с ним (перестановки и т.п.). Когда я вызываю процедуру построения этого списка, все вроде бы нормально и никаких ошибок не происходит. Но стоит мне вызвать хоть одну функцию работы со списком и вылетает ексепшн: NullReferenceException. Как будто указатель на начало списка укаывает в нуль, но из приведенного мной кода (см. ниже), явно видно, что такого быть не может. Тем более, когда я проверял все функции этой библиотеки в программе (без вызова их из DLL, они были описаны в той же программе) они работали и не происходило ошиок. Или я совсем ничего не понимаю, или все таки у DLL свой сегмент данных, и в нем должно все сохраняться. Вот код библиотеки (не весь, я указал лишь одну функцию работы со списком - Perform2OPT): Цитата #include <windows.h> typedef struct City { unsigned int Data; struct City *Next, *Prev; } TCity; // common functions void SetDistancesMatrix(double *M); void SetData(unsigned int *CitiesData, unsigned int Pos, unsigned int N); int GetData(unsigned int *CitiesData); void DestroyList(); // heuristics bool Perform2OPT(); ... ... ... //!!!!!!! These pointers raises exception ??? TCity *Head=NULL, *Tail=NULL, *Last; // Pointer to the head and tail of list double *Distance; // Pointer to distances matrix int Num; // Number of cities void DestroyList() { TCity *C=Head, *T; while (C!=Tail) { T=C; C=C->Next; delete T; } delete C; } void SetDistancesMatrix(double *M) { Distance=M; } // Main DLL function. We do not use any initializatoins BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { return TRUE; } void SetData(unsigned int *CitiesData, unsigned int Pos, unsigned int N) { TCity *C; Num=N; Head=new TCity; Tail=new TCity; Last=Head; Head->Data=0; Tail->Data=0; for (unsigned int i=Pos; i<Pos+Num; i++) { C=new TCity; C->Data=CitiesData[i]; C->Prev=Last; Last->Next=C; Last=C; } Tail->Prev=Last; Last->Next=Tail; } int GetData(unsigned int *CitiesData) { int Idx=0; TCity *C=Head->Next; while (C!=Tail) { CitiesData[Idx++]=C->Data; C=C->Next; } return Idx-1; } // Performs 2-OPT herustics bool Perform2OPT() { TCity *I1, *I2, *J1, *J2; unsigned int TempData; bool Result=false; int Cnt1=0, Cnt2=0, Cnt3=0; Next: I1=Head->Next; do { I2=I1->Next; J1=I2->Next; do { J2=J1->Next; if ((Distance[I1->Data*Num+J1->Data]+Distance[I2->Data*Num+J2->Data])< (Distance[I1->Data*Num+I2->Data]+Distance[J1->Data*Num+J2->Data])) { while (I2!=J1) { TempData=I2->Data; I2->Data=J1->Data; J1->Data=TempData; I2=I2->Next; J1=J1->Prev; if (J1->Next==I2) break; } Result=true; goto Next; } J1=J2; } while ((J1!=Tail->Prev)&&J1->Next); I1=I2; } while (I1!=Tail->Prev->Prev); return Result; } Так выглядит модуль объявлений функций в библиотеке (VB.NET): Цитата Module heuristics Declare Sub SetDistancesMatrix Lib "heuristics32.dll" (ByRef M As Double(,)) Declare Sub SetData Lib "heuristics32.dll" (ByRef CitiesData As Integer(), ByVal Pos As Integer, ByVal N As Integer) Declare Function GetData Lib "heuristics32.dll" (ByRef CitiesData As Integer()) As Integer Declare Sub DestroyList Lib "heuristics32.dll" () Declare Function Perform2OPT Lib "heuristics32.dll" () As Byte ... ... ... End Module А это класс-интерфейс для работы с библиотекой (собственно проэкт VB.NET): Цитата Module TSPClass Public Class HeuristicsClass Private FCitiesNum As Integer 'Number of cities Private FCities As Integer() 'Array of cities indexes Private DistancesMatrix As Double(,) 'Matrix with distances between cities Private FDBName As String 'File name of database with cities data Private FTableName As String 'Name of table in database Private FOptimized As Boolean 'Optimized flag Public ReadOnly Property CitiesCount() [COLOR=blue]As Integer Public Property DBName() As String Public Property TableName() As String Public ReadOnly Property Optimized() As Boolean Public Function GetTourPoints(ByRef Data As Short()) As Boolean Public Function SetTourPoints(ByRef Data As Short()) As Boolean 'This method reads table (specifyed by TableName property) from source database (name specifyed by DBName property) 'and loads data from it for optimization. Fields MUST BE named as follows: "Index", "X", "Y". 'If function succeeds, returned value is true, otherwise - false Public Function ReadSource() As Boolean Dim Result As Boolean = True 'Return value 'Всякие объявления Try 'Здесь я считываю массив из базы данных (как раз то, что я спрашивал в соседнем топике) 'А вот тут пошли вызовы из DLL: Call SetDistancesMatrix(DistancesMatrix) 'Set pointer to distances matrix Call SetData(FCities, 0, N) 'Set current sequence for optimization Catch DllEx As DllNotFoundException MsgBox(DllEx.Message, MsgBoxStyle.Critical, "Error") FCitiesNum = 0 Result = False Catch ex As Exception 'If error occured ... FCitiesNum = 0 'Clear number of cities Result = False 'Return result - false (failed) Finally DataReader.Close() Connection.Close() End Try Return Result End Function 'А это, господа, и виновник всех неудач (как я с себя все свалил) Function Perform_2OPT() As Boolean Try FOptimized = Perform2OPT() Catch DllEx As DllNotFoundException MsgBox(DllEx.Message, MsgBoxStyle.Critical, "Error") Catch e As NullReferenceException MsgBox("Che za hren'???"+e.Message) End Try Return FOptimized End Function ... ... ... Public Sub New(ByVal DBN As String, ByVal TN As String) Dim Response As MsgBoxResult = MsgBoxResult.Retry FOptimized = False FCitiesNum = 0 FDBName = DBN FTableName = TN While (Not ReadSource()) And Response = MsgBoxResult.Retry Response = MsgBox("Error reading database", MsgBoxStyle.RetryCancel, "ERROR") End While End Sub Protected Overrides Sub Finalize() MyBase.Finalize() End Sub End Class P.S. Извините за такой объём, но иначе нельзя объяснить проблему. Спасибо за вимание. |
Сообщ.
#2
,
|
|
|
А почему нельзя использовать System.Collections.ArrayList? Скорее всего он использует оптимальные алгоритмы, и не особо уступает в скорости написанному.
Или почему бы не сделать библиотеку managed dll? А гонять библиотеку под отладчиком пробовал? Именно библиотеку, а не программу. |
Сообщ.
#3
,
|
|
|
Здравствуйте!
Дело в том, что стандартный класс работает медленно. Я тестировал и стандартный и мною написанный в среде VB.NET класс, хороших результатов это не дало. А на С++ я программу отлаживал и она работает. Неужели указатели в библиотеке не сохраняются? Как это может быть? |
Сообщ.
#4
,
|
|
|
1. Если используешь VS2003 - имеет смысл переписать на MC++ и не париться. А то можешь на интеропе потерять больше.
2. Указатели маршалятся как IntPtr а не как Integer. Этот тип специально для этого и создан. Может баг еще где, извини, это просто в глаза бросилось.. времени нет |
Сообщ.
#5
,
|
|
|
То есть ты хочешь сказать, что необходимо поменять:
Declare Sub SetData Lib "heuristics32.dll" (ByRef CitiesData As Integer(), ByVal Pos As Integer, ByVal N As Integer) Declare Function GetData Lib "heuristics32.dll" (ByRef CitiesData As Integer()) As Integer На: Declare Sub SetData Lib "heuristics32.dll" (ByVal CitiesData As IntPtr, ByVal Pos As Integer, ByVal N As Integer) Declare Function GetData Lib "heuristics32.dll" (ByVal CitiesData As IntPtr) As Integer ??? |
Сообщ.
#6
,
|
|
|
Угу
|
Сообщ.
#7
,
|
|
|
Спасибо за ответ, все понятно. Но:
- Как мне послылать тогда массив 32-х битных значений? - А в самой библиотеке тоже надо получать как IntPtr? - И как его там конвертить в массив интов? Большое спасибо. С уважением, neutrino. |
Сообщ.
#8
,
|
|
|
Цитата neutrino, 22.07.03, 20:06:15 Спасибо за ответ, все понятно. Но: - Как мне послылать тогда массив 32-х битных значений? - А в самой библиотеке тоже надо получать как IntPtr? - И как его там конвертить в массив интов? Большое спасибо. С уважением, neutrino. В библиотеке у тебя обычные указатели. А про передачу массивов прочитай здесь. (последний ответ) |
Сообщ.
#9
,
|
|
|
Привет еще раз. Надеюсь я вас не замучил...
Ссылка оказалась на самом деле полезной. После того, как я ее прочитал, нашел и у себя в МСДН подходящий топик. Назревает вопрос: а надо ли мне маршалить массив, если библиотеку я тоже написал в .NET Framework? Странно получается... Или язык С++ по определению у них "Unsafe" из-за указателей или еще чего... Кстати, из того, что я прочитал в МСДН следует, что определение должно выглядеть так: Declare Sub SetData Lib "heuristics32.dll" (ByRef <MarshalAs(UnmanagedType.LPArray, SizeParamIndex=0)> CitiesData As Integer(), ByVal Pos As Integer, ByVal N As Integer) ??? ??? ??? |
Сообщ.
#10
,
|
|
|
Так понятно, прочитай-ка про C++ with managed extensions. Особенно обрати внимание на __gc arrays. Возможно у тебя отпадет куча вопросов и надобность в интеропе заодно.
|
Сообщ.
#11
,
|
|
|
Большое спасибо господину kl за исчерпывающий ответ и помощь.
|
Сообщ.
#12
,
|
|
|
Я немного поторопился с выводами...
У меня ничего не вышло с этими __gc. Все время синтаксические ошибки. Вот так я писал: double Distance __gc[,]; void SetDistancesMatrix(double M __gc[,]) { Distance=M; } Да и такое определение не прокатывает: void SetData(unsigned int CitiesData __gc[], unsigned int Pos, unsigned int N) Как же нужно правильно писать? |
Сообщ.
#13
,
|
|
|
Приведи ошибки-то, нет времени компилить.
Я надеюсь, ты с опцией /clr компилил? |
Сообщ.
#14
,
|
|
|
Ну Ё! Ты хоть МСДН почитай что-ли!
__gc это storage класс, как static или register. Соответственно его надо указывать там где ДОЛЖЕН появляться storage-класс. Или, для __gc arrays ПЕРЕД словом new при выделении памяти: <br>double array[]= [b]__gc[/b] new double[100]<br> Ссылки в MSDN: ms-help://MS.VSCC.2003/MS.MSDNQTR.2003APR.1033/vcmxspec/html/vcManagedExtensionsSpec_4_5.htm - это __gc arrays ms-help://MS.VSCC.2003/MS.MSDNQTR.2003APR.1033/vcmxspec/html/vcManagedExtensionsSpec_4.htm - это по поводу __gc вообще ms-help://MS.VSCC.2003/MS.MSDNQTR.2003APR.1033/vcmex/html/vcconMCOverview.htm - это всё про managed extensions for C++ |
Сообщ.
#15
,
|
|
|
Цитата andrey, 30.07.03, 23:26:44 Ну Ё! Ты хоть МСДН почитай что-ли! __gc это storage класс, как static или register. Соответственно его надо указывать там где ДОЛЖЕН появляться storage-класс. Или, для __gc arrays ПЕРЕД словом new при выделении памяти: <br>double array[]= __gc new double[100]<br> __gc - это ключевое слово, говорящее о том, что объект будет размещен в управляемой куче (куче CLR). И синтаксис должен выглядеть вот так: <br>double array __gc[] = new double __gc[100];<br> А причем тут например static я так и не понял |
Сообщ.
#16
,
|
|
|
static это просто аналогия.
Вот статья из MSDN: A __gc array is a dynamic array that is allocated on the common language runtime heap. The number of elements of the array is not part of the type. A single array variable may refer to arrays of different sizes. Example // __gc_arrays.cpp // compile with: /clr #using <mscorlib.dll> using namespace System; int main() { Int32 ia[] = __gc new Int32[100]; ia = new Int32[200]; } The indices of a __gc array are zero-based, as in standard C++. A __gc array is subscripted using ordinary C++ array brackets. Unlike standard C++, subscripting is not a synonym for pointer arithmetic, and is not commutative. Characteristics A __gc array shall be allocated using the managed operator __gc new. Using new to allocate a managed array defaults to __gc new. Constraints The type of an element of a __gc array shall only be a __value class, or a __gc pointer to a __gc class. __nogc new shall not be used to allocate an array of managed type. A __gc array variable definition shall not specify a size. The size of the array is given in the call to the managed operator __gc new. A __gc array is itself a __gc object. It is actually a pointer into the common language runtime heap. The indirection of the pointer has been factored into the subscripting operator. As a __gc object, it has the same restrictions as a __gc class (Section 4). Most notably, the element type cannot be an unmanaged C++ class that is not a POD type. All __gc arrays inherit from System::Array. Any method or property of System::Array can be applied directly to the array variable. Example // __gc_arrays2.cpp // compile with: /clr #using <mscorlib.dll> using namespace System; int main() { Int32 ia[] = __gc new Int32[100]; Console::WriteLine(ia->Length); } Output 100 |
Сообщ.
#17
,
|
|
|
Кстати я в своём примере ошибся - нельзя использовать встроенный C++ класс double, нужно исползовать FCL класс Double
|
Сообщ.
#18
,
|
|
|
Синтаксис допускает оба варианта
Главное не забыть про /clr и разумеется не пытаться создавать __gc массивы на стеке |
Сообщ.
#19
,
|
|
|
Уважаемый господин Андрей, читал я МСДН и ничего там не понял.
А для ключа /clr есть аналог в Options? |
Сообщ.
#20
,
|
|
|
Цитата andrey, 30.07.03, 23:26:44 ... __gc это storage класс, как static или register. Соответственно его надо указывать там где ДОЛЖЕН появляться storage-класс. Или, для __gc arrays ПЕРЕД словом new при выделении памяти ... Мне не нужно выделять память для массива. Прочитайте мои вопрос внимательнее. Мне необходимо получить его в качестве параметра функции в библиотеке, которую вызывают из VB.NET. Именно поэтому существует проблема несоответствия типов данных. |
Сообщ.
#21
,
|
|
|
Цитата neutrino, 03.08.03, 20:32:17 Уважаемый господин Андрей, читал я МСДН и ничего там не понял. А для ключа /clr есть аналог в Options? Есть там что-то типа Compile managed code. И приведи ты в конце-концов ошибки-то. |
Сообщ.
#22
,
|
|
|
Я никак не возьму в толк, тебе нужен ответ на вопрос или нет? Тогда напиши то что тебя просит модератор - то есть ошибки.
Я попробовал так : C++ managed code class library: <br>class sss{<br>...<br> public : void PTest(Double arr[]){<br> Console::WriteLine(arr->ToString());<br> }<br> VB.NET windows forms application: <br> Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load<br> Dim ss As tst.sss<br> ss = New tst.sss<br> Dim mm() As Double<br> mm = New Double(2) {2.2999999999999998, 5.5999999999999996, 5.2999999999999998}<br> ss.PTest(mm)<br> End Sub<br><br> У меня всё это работает |
Сообщ.
#23
,
|
|
|
Доброго времени суток.
Вот вывод компилятора (команда вызова в самом начале): Цитата C:\Documents and Settings\neutrino\My Documents\Heuristics32>cl /clr Heuristics32.cpp Microsoft ® 32-bit C/C++ Optimizing Compiler Version 12.00.8168 for 80x86 Copyright © Microsoft Corp 1984-1998. All rights reserved. Command line warning D4002 : ignoring unknown option '/clr' Heuristics32.cpp Heuristics32.cpp(37) : fatal error C1021: invalid preprocessor command 'using' C:\Documents and Settings\neutrino\My Documents\Heuristics32> То есть ни параметр '/clr' ни директиву препроцессора '#using' он не понял. Саму эту директиву, как написано в MSDN, надо указывать всегда при использовании Managed C++: Цитата #using <mscorlib.dll> Для полноты картины приведу релевантный кусок кода, который я пытался компилировать: Цитата #include <windows.h> #using <mscorlib.dll> typedef struct City { unsigned int Data; struct City *Next, *Prev; } TCity; TCity *Head=NULL, *Tail=NULL, *Last; // Pointer to the head and tail of list double Distance __gc[,]; // Pointer to distances matrix int Num; // Number of cities BOOL APIENTRY DllMain( HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved ) { switch (ul_reason_for_call) { case DLL_PROCESS_ATTACH: case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: case DLL_PROCESS_DETACH: break; } return TRUE; } void DestroyList() { TCity *C=Head, *T; while (C!=Tail) { T=C; C=C->Next; delete T; } delete C; } void SetDistancesMatrix(double M __gc[,]) { Distance=M; } void SetData(unsigned int CitiesData __gc[], unsigned int Pos, unsigned int N) { TCity *C; Num=N; Head=new TCity; Tail=new TCity; Last=Head; Head->Data=0; Tail->Data=0; for (unsigned int i=Pos; i<Pos+Num; i++) { C=new TCity; C->Data=CitiesData[i]; C->Prev=Last; Last->Next=C; Last=C; } Tail->Prev=Last; Last->Next=Tail; } int GetData(unsigned int CitiesData __gc[]) { int Idx=0; TCity *C=Head->Next; while (C!=Tail) { CitiesData[Idx++]=C->Data; C=C->Next; } return Idx-1; } . . . |
Сообщ.
#24
,
|
|
|
Версия компилера не та, должна быть (у меня такая) 13.0.9466 for .NET Framework.
Это MC++ компилятор из MSVS.NET 2003. Если у тебя стоит MSVS.NET, то проверь PATH, может вызывается не тот компилер который надо. |
Сообщ.
#25
,
|
|
|
Я понимаю, что это наглость, но не мог бы кто-нибудь откомпилировать мой проэкт библиотеки? Пожалуйста. Мне больше ничего не остается делать...
Зазипинный проэкт библиотеки: Heuristics.zip (362Kb) |
Сообщ.
#26
,
|
|
|
Не качается.... Кинь мне на мыло andreyaz ____ @ ____ rol.ru (_ удалить)
|
Сообщ.
#27
,
|
|
|
Отправил.
|
Сообщ.
#28
,
|
|
|
Ээээ, размер файла =0.
О, зато теперь качается по ссылке! |
Сообщ.
#29
,
|
|
|
Блин, там что сервак на модеме сидит?
Миллион ошибок. Я отключил все RTC, Debug info, PCH Всё вынесено в класс statics неймспейса ____Heuristics. Ко всему применён модификатор static. DllMain убит нафиг. Количество размерностей массива Distance теперь 1 (при объявлении обявлялось 2, а потом в коде использовалась только 1, соответственно SetDistancesMatrix принимает только 1 мерный массив) Вобщем работать может быть будет. Самое главное: теперь это managed dll и LoadLibrary не катит! |
Сообщ.
#30
,
|
|
|
Качай http://www.ve-2-00.ru/xxxx.rar
|
Сообщ.
#31
,
|
|
|
Странно, мне почему-то не удалось прочитать таблицу экспорта функций...
|
Сообщ.
#32
,
|
|
|
Это managed-dll, там нет экспортируемых функций. RTFM, как уже неоднократно советовали!!!
|
Сообщ.
#33
,
|
|
|
Цитата andrey, 25.08.03, 00:56:43 Это managed-dll, там нет экспортируемых функций. RTFM, как уже неоднократно советовали!!! Тут согласен на 100\%. Это основы. Читай что такое метаданные в .NET |