Форум программистов, компьютерный форум, киберфорум
C# .NET
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
288 / 251 / 107
Регистрация: 26.10.2012
Сообщений: 797

Вызов неуправляемого метода крашит программу

22.04.2025, 13:39. Показов 2371. Ответов 22
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Здравствуйте!

Есть внешняя библиотека загружаемая через DllImport. Она работает нестабильно: при определенных входных данных приложение зависает или прекращает работу. Иногда она то зависает, то работает нормально на одних и тех же данных.
Но мне все равно очень хочется использовать эту библиотеку.
Я пробовал вызывать этот метод в асинхронном Task. Пробовал создавать отдельный поток с этим методом, ждать 5 секунд и потом вызывать Thread.Kill в случае зависания. Не помогает: иногда случаются зависания метода, и это убивает все приложение.
Есть идея написать отдельное консольное приложение и вызывать его через сторонний Process, но это звучит как-то совсем жестко.
Есть идеи как работать с проблемной зависающей внешней библиотекой без вылета всего приложения?

На всякий случай мой код.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
public static OutputData Compute(InputData input)
{
    var output = OutputData.Empty();
    SetDllDirectory(Path.Combine(Directory.GetCurrentDirectory(), "DLL"));
    var libraryHandle = LoadLibrary("ErgDll");
    var pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "newObjectC");
    var newObjectCHandler = Marshal.GetDelegateForFunctionPointer<NewObjectCHandler>(pAddressOfFunctionToCall);
    pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "getArgsAddresC");
    var getArgsAddresCHandler = Marshal.GetDelegateForFunctionPointer<GetArgsAddresCHandler>(pAddressOfFunctionToCall);
    pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "DoErgC");
    var doErgCHandler = Marshal.GetDelegateForFunctionPointer<DoErgCHandler>(pAddressOfFunctionToCall);
    pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "getResultsAddresC");
    var getResultsAddresCHandler = Marshal.GetDelegateForFunctionPointer<GetResultsAddresCHandler>(pAddressOfFunctionToCall);
    pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "delObjectC");
    var delObjectCHandler = Marshal.GetDelegateForFunctionPointer<DelObjectCHandler>(pAddressOfFunctionToCall);
 
    var pI = IntPtr.Zero;
    try
    {
        pI = newObjectCHandler();
        if (pI == IntPtr.Zero)
        {
            output.error = "Error in newObject()";
            return output;
        }
 
        var pAdd = getArgsAddresCHandler(pI);
        if (pAdd == IntPtr.Zero)
        {
            output.error = "Error in getArgsAddresC(...)";
            return output;
        }
 
        input.WriteInput(pAdd);
 
        Debug.WriteLine("Enter method");
        //собственно вот этот злосчастный метод типа Func<int, IntPtr> на котором все висит
        var result = doErgCHandler(pI);
        Debug.WriteLine("Exit Method");
 
        const int TMIN_FLAG = 1947000000;
        if (result == 0) // O'key
        {
            var resAddress = getResultsAddresCHandler(pI);
            if (resAddress == IntPtr.Zero)
            {
                output.error = "Некорректный getArgsAddresC";
                return output;
            }
            output = new OutputData(input, resAddress);
            return output;
        }
        else if (TMIN_FLAG < result) // Заданное время хода T меньше минимального Tmin
        {
            result -= TMIN_FLAG;
            output.error = $"Время хода T меньше минимального Tmin на {result} сек";
            return output;
        }
        else
        {
            output.error = $"Неизвестный результат: {result}";
            return output;
        }
    }
    catch (Exception e)
    {
        output.error = e.ToString();
        return output;
    }
    finally
    {
        if (pI != IntPtr.Zero)
        {
            delObjectCHandler(pI);
            FreeLibrary(libraryHandle);
        }
    }
}
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
22.04.2025, 13:39
Ответы с готовыми решениями:

Вызов метода(делегата) из неуправляемого кода
Есть способ передать адрес делегата в неуправляемый код как адрес функции, кто нибудь решил эту...

Вызов неуправляемого кода из длл
Замучался с маршилингом! Есть функция в длл: RetCode = HMM_Path_Init( char *path ); RetCode –...

Вызов CLI C++ из неуправляемого кода Native C++
Тема наверное ходовая, но в инете не нашёл рабочего варианта. Суть в том, чтобы из неуправляемого...

22
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,227
22.04.2025, 13:45
Запуск внешнего процесса через Process хорошая идея.
Вам нужно добавить настройки таймаута и эмпирически подобрать эти настройки, чтобы можно было автоматически детектить зависание.
У Task-ов уже есть все необходимое API для этого.
0
Native x86
Эксперт Hardware
 Аватар для quwy
6853 / 3787 / 1024
Регистрация: 13.02.2013
Сообщений: 11,861
22.04.2025, 13:50
DLL точно экспортирует функции с конвенцией stdcall?
0
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,227
22.04.2025, 13:52
Я не работал с Interop-ом, возможно вы что-то неаккуратно сделали, кто-знает, поэтому работает нестабильно.
Нужно смотреть описание функций, которыми пользуетесь.
0
288 / 251 / 107
Регистрация: 26.10.2012
Сообщений: 797
22.04.2025, 13:58  [ТС]
Цитата Сообщение от quwy Посмотреть сообщение
DLL точно экспортирует функции с конвенцией stdcall?
Нет, все с Cdecl
C#
1
2
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate IntPtr GetArgsAddresCHandler(IntPtr ptr);
Подстановка stdcall привела к
"Managed Debugging Assistant 'PInvokeStackImbalance' : 'Вызов функции PInvoke "TrainSimulatorLibrary!Pkbct.TrainSimula torLibrary.Models.Adapters.EnergyOptimal Adapter+GetArgsAddresCHandler::Invoke" разбалансировал стек. Вероятно, это вызвано тем, что управляемая сигнатура PInvoke не совпадает с неуправляемой целевой сигнатурой. Убедитесь, что соглашение о вызовах и параметры сигнатуры PInvoke совпадают с неуправляемой целевой сигнатурой.'"
0
Native x86
Эксперт Hardware
 Аватар для quwy
6853 / 3787 / 1024
Регистрация: 13.02.2013
Сообщений: 11,861
22.04.2025, 14:10
Цитата Сообщение от jetyb Посмотреть сообщение
Подстановка stdcall привела к
Вы просто не привели код декларации, а GetDelegateForFunctionPointer по-умолчанию как раз-таки оперирует конвенцией stdcall.

Похоже код внутри DLL знатно портит память процесса, раз даже ловушка исключений не помогает. Вы уверены, что правильно ее используете?
0
288 / 251 / 107
Регистрация: 26.10.2012
Сообщений: 797
22.04.2025, 14:17  [ТС]
Цитата Сообщение от quwy Посмотреть сообщение
Похоже код внутри DLL знатно портит память процесса, раз даже ловушка исключений не помогает. Вы уверены, что правильно ее используете?
Может там просто бесконечный цикл бегает, и исключений не возникает.
Не уверен что все использую верно.
Возможно я подсовываю программе некорректные входные данные (input.WriteInput(pAdd);), но программа составлена по примеру автора библиотеки и результат похож на правду.
0
Native x86
Эксперт Hardware
 Аватар для quwy
6853 / 3787 / 1024
Регистрация: 13.02.2013
Сообщений: 11,861
22.04.2025, 14:20
Что-то по этому вашему симулятору ничего не гуглится. Есть какая-то документация, или сишные хидеры? Где вы сигнатуры функций смотрите?
1
288 / 251 / 107
Регистрация: 26.10.2012
Сообщений: 797
22.04.2025, 14:37  [ТС]
Не гуглится потому что мы в открытый доступ свои разработки и не выкладываем. Dll-ка c руководством кстати получена давно от такой же закрытой конторы.
Если очень-очень интересна документация - то вот.

Кликните здесь для просмотра всего текста

Sample к документации (слегка подправленный мной).
Самостоятельно сделать аналог к нему муторно, потому что надо составить входные данные.
А так порядок вызова основных функций у меня такой же.
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
 
namespace ConsoleApp2
{
    class Program
    {
        // wrap the unmanaged C++ functions in ErgDll.dll
        [DllImport("ErgDll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr newObjectC();
 
        [DllImport("ErgDll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern void delObjectC(IntPtr pI);
 
        [DllImport("ErgDll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr getArgsAddresC(IntPtr pI);
 
        [DllImport("ErgDll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern IntPtr getResultsAddresC(IntPtr pI);
 
        [DllImport("ErgDll.dll", CallingConvention = CallingConvention.Cdecl)]
        public static extern int DoErgC(IntPtr pI);
 
        //+ BackgroundWorker
        static BackgroundWorker bw;
        static int st_nRet = 0;
        static void bw_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {
            if (e.Error != null)
                Console.WriteLine("Worker exception: " + e.Error);
            else
                Console.WriteLine("Работа закончена успешно"); // Результат - e.Result
            return;
        }
        static void bw_DoWork(object sender, DoWorkEventArgs e)
        {
            st_nRet = DoErgC((IntPtr)e.Argument);
            e.Result = st_nRet;
        }
        //- BackgroundWorker
        static void Main(string[] args)
        {
            const int TMIN_FLAG = 1947000000;
 
            try
            {
                IntPtr pI = newObjectC();
                if (pI == IntPtr.Zero)
                {
                    Console.WriteLine("Error in newObject()");
                    return;
                }
                IntPtr pAdd = getArgsAddresC(pI);
                if (pAdd == IntPtr.Zero)
                {
                    Console.WriteLine("Error in getArgsAddresC(...)");
                    delObjectC(pI); pI = IntPtr.Zero;
                    return;
                }
                // Read data for Erg from text-file
                string textFile = "DatForErg.dat";
                if (args.Length > 0)
                    textFile = args[0];
                if (!File.Exists(textFile))
                {
                    Console.WriteLine("File {0} with data for Erg no founded", textFile);
                    return;
                }
                ReadWriteData RW = new ReadWriteData();
                if (RW.GetDatForErgDll(pAdd, textFile))
                {
                    //int result = DoErgC(pI);
 
                    //+ BackgroundWorker
                    st_nRet = 0;
                    bw = new BackgroundWorker();
                    bw.DoWork += bw_DoWork;
                    bw.RunWorkerCompleted += bw_RunWorkerCompleted;
                    bw.RunWorkerAsync(pI);
                    // Emulation useful work in Main Thread
                    const int SLEEP_AMOUNT = 100;
                    const int SLEEP_TIMEOUT = 90000; // 90 seconds
                    int elapsedTime = 0;
                    while (bw.IsBusy && elapsedTime < SLEEP_TIMEOUT)
                    {
                        elapsedTime += SLEEP_AMOUNT;
                        Thread.Sleep(SLEEP_AMOUNT); // <= Do something useful
                    }
                    int Res = st_nRet; // Without BackgroundWorker: int Res=DoErgC(pI);
                                       //- BackgroundWorker
                    if (0 == Res) // O'key
                    {
                        FileInfo fi = new FileInfo(textFile);
                        RW.ResultsFromERGtoFile(getResultsAddresC(pI), fi.DirectoryName);
                    }
                    else if (TMIN_FLAG < Res) // Заданное время хода T меньше минимального Tmin
                    {
                        Res -= TMIN_FLAG;
                        Console.WriteLine("Время хода T меньше минимального Tmin на {0} сек\n", Res);
                    }
                    else
                    {
                        // ... 
                    }
                }
 
                delObjectC(pI); pI = IntPtr.Zero;
            }
            catch (System.Exception ex)
            {
                Console.WriteLine("An error occurred trying to do Erg-calculation");
            }
        }
 
    }
    class ReadWriteData
    {
        public bool GetDatForErgDll(IntPtr pAdd, string textFile)
        {
            if (pAdd == IntPtr.Zero)
            {
                Console.WriteLine("Address is null");
                return false;
            }
            if (!File.Exists(textFile))
            {
                Console.WriteLine("File {0} with data for Erg no founded", textFile);
                return false;
            }
            IntPtr pTo = pAdd;
            string[] delimiterChars = { " ", "\t", "\r\n" };
            string[] words;
            using (StreamReader sr = new StreamReader(textFile))
            {
                string sAll = sr.ReadToEnd();
                sr.Close();
                words = sAll.Split(delimiterChars, StringSplitOptions.RemoveEmptyEntries);
            }
            int nC = words.Length, i = 0, j = 0, k, iTo = 0, nRows = 0;
            int[] pIntArr = new int[nC];
            double[] pDblArr = new double[256];
            // nLenMeters, VBeg, VEnd, VMax, Tx 
            for (j = 0; j < 5; ++j)
                pIntArr[iTo++] = int.Parse(words[i++]);
            // profil
            nRows = int.Parse(words[i++]); pIntArr[iTo++] = nRows;
            for (j = 0; j < 3 * nRows; ++j)
                pIntArr[iTo++] = int.Parse(words[i++]);
            // VLim
            nRows = int.Parse(words[i++]); pIntArr[iTo++] = nRows;
            for (j = 0; j < 3 * nRows; ++j)
                pIntArr[iTo++] = int.Parse(words[i++]);
            // Mp, Lp, MLok, LLok
            for (j = 0; j < 4; ++j)
                pIntArr[iTo++] = int.Parse(words[i++]);
            Marshal.Copy(pIntArr, 0, pTo, iTo);
            pTo = (IntPtr)(4 * iTo + pTo.ToInt32()); iTo = 0;
            // a1, a2, a3
 
 
            var culture = new CultureInfo("ru-Ru")
                {
                    NumberFormat =
                    {
                        CurrencyDecimalSeparator = "."
                    }
                };
            for (j = 0; j < 3; ++j)
                pDblArr[iTo++] = double.Parse(words[i++], NumberStyles.Any, culture);
            Marshal.Copy(pDblArr, 0, pTo, iTo); ;
            pTo = (IntPtr)(8 * iTo + pTo.ToInt32()); iTo = 0;
            // ntype
            pIntArr[iTo++] = int.Parse(words[i++]);
            // Vagons
            nRows = int.Parse(words[i++]); pIntArr[iTo++] = nRows;
            for (j = 0; j < 3 * nRows; ++j)
                pIntArr[iTo++] = int.Parse(words[i++]);
            Marshal.Copy(pIntArr, 0, pTo, iTo);
            pTo = (IntPtr)(4 * iTo + pTo.ToInt32()); iTo = 0;
            // Tg/Tm
            for (k = 0; k < 2; ++k) // k==0/1 ==> Tg/Tm
            {
                int n = int.Parse(words[i++]),
                    nU = int.Parse(words[i++]);
                pIntArr[iTo++] = n; pIntArr[iTo++] = nU;
                Marshal.Copy(pIntArr, 0, pTo, iTo);
                pTo = (IntPtr)(4 * iTo + pTo.ToInt32()); iTo = 0;
                for (j = 0; j < 2 * n; ++j)
                    pDblArr[iTo++] = double.Parse(words[i++], NumberStyles.Any, culture);
                Marshal.Copy(pDblArr, 0, pTo, iTo);
                pTo = (IntPtr)(8 * iTo + pTo.ToInt32()); iTo = 0;
                if (0 == nU) nU = 1;
                for (j = 0; j < nU; ++j)
                    pIntArr[iTo++] = int.Parse(words[i++]);
                Marshal.Copy(pIntArr, 0, pTo, iTo);
                pTo = (IntPtr)(4 * iTo + pTo.ToInt32()); iTo = 0;
            }
            // Parameters
            for (j = 0; j < 2; ++j)
                pIntArr[iTo++] = int.Parse(words[i++]);
            Marshal.Copy(pIntArr, 0, pTo, iTo);
            pTo = (IntPtr)(4 * iTo + pTo.ToInt32()); iTo = 0;
 
 
            var count = pTo.ToInt32() - pAdd.ToInt32();
            var bytes = new byte[count];
            Marshal.Copy(pAdd,bytes, 0, bytes.Length);
            File.WriteAllBytes("D:/mData.dat", bytes);
            return true;
        }
 
        public bool ResultsFromERGtoFile(IntPtr pAdd, string sPath)
        {
            if (pAdd == IntPtr.Zero)
            {
                Console.WriteLine("Address is null");
                return false;
            }
            IntPtr pFrom = pAdd;
            // to SVWT_Result.dat
            using (StreamWriter sw = new StreamWriter(sPath + "\\SVWT_Result.dat"))
            {
                string sFrm = "{0, 10} {1, 8} {2, 12} {3, 10}";
                int i = 0, nR = Marshal.ReadInt32(pFrom);
                pFrom = (IntPtr)(4 + pFrom.ToInt32());
                sw.WriteLine(nR);
                for (i = 0; i < nR; ++i)
                {
                    int s = 0, v = 0, w = 0, t = 0;
                    s = Marshal.ReadInt32(pFrom); pFrom = (IntPtr)(4 + pFrom.ToInt32());
                    v = Marshal.ReadInt32(pFrom); pFrom = (IntPtr)(4 + pFrom.ToInt32());
                    w = Marshal.ReadInt32(pFrom); pFrom = (IntPtr)(4 + pFrom.ToInt32());
                    t = Marshal.ReadInt32(pFrom); pFrom = (IntPtr)(4 + pFrom.ToInt32());
                    string sRow = string.Format(sFrm, s, v, w, t);
                    sw.WriteLine(sRow);
                }
            }
            // to REGS_Result.dat
            using (StreamWriter sr = new StreamWriter(sPath + "\\REGS_Result.dat"))
            {
                string sFrm = "{0, 10} {1, 8} {2, 8} {3, 10}    {4}";
                int i = 0, nR = Marshal.ReadInt32(pFrom);
                pFrom = (IntPtr)(4 + pFrom.ToInt32());
                sr.WriteLine(nR);
                for (i = 0; i < nR; ++i)
                {
                    int s = 0, v1 = 0, v2 = 0, t = 0;
                    s = Marshal.ReadInt32(pFrom); pFrom = (IntPtr)(4 + pFrom.ToInt32());
                    v1 = Marshal.ReadInt32(pFrom); pFrom = (IntPtr)(4 + pFrom.ToInt32());
                    v2 = Marshal.ReadInt32(pFrom); pFrom = (IntPtr)(4 + pFrom.ToInt32());
                    t = Marshal.ReadInt32(pFrom); pFrom = (IntPtr)(4 + pFrom.ToInt32());
                    byte ch = Marshal.ReadByte(pFrom); pFrom = (IntPtr)(1 + pFrom.ToInt32());
                    string sRow = string.Format(sFrm, s, v1, v2, t, (char)ch);
                    sr.WriteLine(sRow);
                }
            }
 
            return true;
        }
 
    }
}
Вложения
Тип файла: doc AboutErgDll.doc (589.0 Кб, 14 просмотров)
0
Native x86
Эксперт Hardware
 Аватар для quwy
6853 / 3787 / 1024
Регистрация: 13.02.2013
Сообщений: 11,861
22.04.2025, 14:58
Странно:
Во всех экспортируемых функциях используется стандартное для Windows API соглашение, а именно: __stdcall
Хотя вы же проверяли, по факту это не так.

При этом колбеки все:
int (__cdecl*)(int (__cdecl*)(int))
А еще там масса структур, которые на другом языке тоже не так просто правильно задекларировать.

Может проще будет на C/C++ написать?

Добавлено через 1 минуту
P.S. Попахивает документом с грифом "ДСП", вам не настучат за выкладывание подобного?
1
288 / 251 / 107
Регистрация: 26.10.2012
Сообщений: 797
22.04.2025, 15:13  [ТС]
Цитата Сообщение от quwy Посмотреть сообщение
Хотя вы же проверяли, по факту это не так.
При этом колбеки все:
Я сделал все по приложенному примеру на C#. И программа даже иногда работает нормально.
Никакого (int (__cdecl*)(int (__cdecl*)(int)) там нет (которое воспринимаются на моем нынешнем знании C++ как грязное ругательство).

P.S. Ну так я же .dll-ку в сеть не выложил. От голого мануала немного толку, даже пример кода без прилагающегося .txt-шника с данными хрен запустишь.
0
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,227
22.04.2025, 15:20
Немного для размышления:
Цитата Сообщение от jetyb Посмотреть сообщение
var libraryHandle = LoadLibrary("ErgDll");
var pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "newObjectC");
Отсюда:
Also, explicitly calling LoadLibrary/GetProcAddress in the managed code is pretty unusual, the pinvoke marshaller already does this.
- explicitly calling LoadLibrary/GetProcAddress in the managed code is pretty unusual. I agree.
0
288 / 251 / 107
Регистрация: 26.10.2012
Сообщений: 797
22.04.2025, 15:37  [ТС]
IamRain, Изначально я пробовал все делать стандартно через [DllImport]extern. Работало, но только один раз: при повторном вызове моего метода Compute возникало исключение. Помог только такой костыль.
0
288 / 251 / 107
Регистрация: 26.10.2012
Сообщений: 797
23.04.2025, 11:57  [ТС]
Скорее всего все из-за работы сборщика мусора и кривоватой реализации библиотечной API.
Я получаю указатель из API-шного вызова, затем пишу в него не хилый такой динамический блок байт (input.WriteInput(pAdd)), потом опять же вызываю API функцию работающую от записанных данных. А если в это время подсуетится ничего не знающий о размере указателя сборщик мусора, то данные херятся и программа творит дичь.

Добавление GC.Collect в начале вызова метода помогло существенно уменьшить вылеты, но не идеально.
Пока я не знаю как получив от API IntPtr запретить сборщику мусора изменять этот указатель на нужный мне массив байт (размер массива я могу получить).
0
151 / 135 / 29
Регистрация: 02.07.2013
Сообщений: 962
23.04.2025, 15:17
Цитата Сообщение от jetyb Посмотреть сообщение
Есть внешняя библиотека загружаемая через DllImport. Она работает нестабильно:
никак не могу понять из обсуждения: а библиотека вообще сама по себе глючная или глюки идут при попытке использовать ее в шарпе?
0
288 / 251 / 107
Регистрация: 26.10.2012
Сообщений: 797
23.04.2025, 16:00  [ТС]
Глюки идут при использовании в шарпе.
0
Эксперт .NET
 Аватар для Usaga
14090 / 9308 / 1348
Регистрация: 21.01.2016
Сообщений: 34,956
24.04.2025, 17:34
Цитата Сообщение от jetyb Посмотреть сообщение
Я получаю указатель из API-шного вызова, затем пишу в него не хилый такой динамический блок байт (input.WriteInput(pAdd)), потом опять же вызываю API функцию работающую от записанных данных. А если в это время подсуетится ничего не знающий о размере указателя сборщик мусора, то данные херятся и программа творит дичь.
Чтобы не было дичи, надо пинить объекты, которые шаришь с неуправляемым кодом. Причём, это надо делать на короткое время. После этого надо скопировать данные и снять пин.

Это - раз. Два - сама неуправляемая библиотека должна быть рассчитана на работу в таком стиле. Т.е. обмен данными с вызывающей стороной (в обе стороны) тупо через копирование данных. Никакого обмена ссылками\указателями с последующим удержанием до второго прихода Христа.

DLL'ка рассчитана на такое?
0
288 / 251 / 107
Регистрация: 26.10.2012
Сообщений: 797
25.04.2025, 09:31  [ТС]
Usaga, Я думал над этим, но так и не понял как применить fixed к IntPtr полученному из неуправляемого кода и решит ли это проблему.

Попробовал решить проблему через GC.Collect и GCHandle.Alloc(pI, GCHandleType.Pinned) к каждому IntPtr
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
    [HandleProcessCorruptedStateExceptions]
    public static OutputData Compute(InputData input)
    {
        isComputing = true;
        id++;
        messages.Add($"Enter method: {id}");
        input.GetInput(out byte[] inputBuffer, out int inputLength);
        GC.Collect();
        GC.WaitForPendingFinalizers();
        var output = OutputData.Empty();
        SetDllDirectory(Path.Combine(Directory.GetCurrentDirectory(), "DLL"));
        var libraryHandle = LoadLibrary("ErgDll");
        var pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "newObjectC");
        var newObjectCHandler = Marshal.GetDelegateForFunctionPointer<NewObjectCHandler>(pAddressOfFunctionToCall);
        pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "getArgsAddresC");
        var getArgsAddresCHandler = Marshal.GetDelegateForFunctionPointer<GetArgsAddresCHandler>(pAddressOfFunctionToCall);
        pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "DoErgC");
        var doErgCHandler = Marshal.GetDelegateForFunctionPointer<DoErgCHandler>(pAddressOfFunctionToCall);
        pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "getResultsAddresC");
        var getResultsAddresCHandler = Marshal.GetDelegateForFunctionPointer<GetResultsAddresCHandler>(pAddressOfFunctionToCall);
        pAddressOfFunctionToCall = GetProcAddress(libraryHandle, "delObjectC");
        var delObjectCHandler = Marshal.GetDelegateForFunctionPointer<DelObjectCHandler>(pAddressOfFunctionToCall);
        
        var pI = IntPtr.Zero;
        GCHandle? pIHandle = null, pAddHandle = null, pResHandle = null;
        try
        {
            pI = newObjectCHandler();
            if (pI == IntPtr.Zero)
            {
                output.error = "Error in newObject()";
                return output;
            }
            pIHandle = GCHandle.Alloc(pI, GCHandleType.Pinned);
            var pAdd = getArgsAddresCHandler(pI);
            if (pAdd == IntPtr.Zero)
            {
                output.error = "Error in getArgsAddresC(...)";
                return output;
            }
            pAddHandle = GCHandle.Alloc(pAdd, GCHandleType.Pinned);
            Marshal.Copy(inputBuffer, 0, pAdd, inputLength);
            var result = doErgCHandler(pI);
            //Debug.WriteLine("Exit Method");
            const int TMIN_FLAG = 1947000000;
            if (result == 0) // O'key
            {
                var pRes = getResultsAddresCHandler(pI);
                if (pRes == IntPtr.Zero)
                {
                    output.error = "Некорректный getArgsAddresC";
                    return output;
                }
                pResHandle = GCHandle.Alloc(pRes, GCHandleType.Pinned);
                output = new OutputData(input, pRes);
                return output;
            }
            else if (TMIN_FLAG < result) // Заданное время хода T меньше минимального Tmin
            {
                result -= TMIN_FLAG;
                output.error = $"Время хода T меньше минимального Tmin на {result} сек";
                return output;
            }
            else
            {
                output.error = $"Неизвестный результат: {result}";
                return output;
            }
        }
        catch (Exception e)
        {
            messages.Add($"Error: {id} : {e}");
            output.error = e.ToString();
            return output;
        }
        finally
        {
            if (pI != IntPtr.Zero) delObjectCHandler(pI);
            FreeLibrary(libraryHandle);
            pIHandle?.Free();
            pAddHandle?.Free();
            pResHandle?.Free();
            GC.Collect(0);
            messages.Add($"Exit method: {id}");
            isComputing = false;
        }
    }
Вылетов стало поменьше, но остался стабильный сценарий. Я запускаю этот тяжелый метод в отдельном потоке Thread1. Если поток долго не дает результата я применяю Thread.Abort. Thread.Abort дает ThreadAbortedException в Thread1, и в момент пока Thread1 обрабатывает это исключение, но еще не вошел в блок finally, в метод Compute входит новый созданный мною поток Thread2 и на строчке var result = doErgCHandler(pI); дает AccessViolationException.
Тестирую и разбираюсь с распределением доступа к методу из разных потоков.

Цитата Сообщение от Usaga Посмотреть сообщение
DLL'ка рассчитана на такое?
А хрен ее знает
0
 Аватар для IamRain
4693 / 2701 / 734
Регистрация: 02.08.2011
Сообщений: 7,227
25.04.2025, 14:26
Цитата Сообщение от jetyb Посмотреть сообщение
Попробовал решить проблему через GC.Collect и GCHandle.Alloc(pI, GCHandleType.Pinned) к каждому IntPtr
Да причем тут GC.Collect, ты не понимаешь сути проблемы. После сборки мусора, если происходит compaction GC, все ссылки на объекты, которые сменили свои адреса, будут перезаписаны. Но неуправляемый код об этом не знает.
А когда pin-ишь - объекты железно остаются на своих адресах.
0
288 / 251 / 107
Регистрация: 26.10.2012
Сообщений: 797
25.04.2025, 15:10  [ТС]
IamRain, Ну объясните как pin-ить IntPtr, я нигде примеров не видел.
И там неуправляемый код явно выделяет большую область памяти (массив неизвестного мне размера). Как объяснить сборщику мусора не перемешать не только 4(8) байт в памяти куда указывает IntPtr , но и некоторый диапазон памяти за ним?
C#
1
2
byte[] bytes = [1, 2, 3];
fixed (byte* pointerToFirst = bytes){}
В этом-то примере компилятор и так знает, что надо не трогать три байта после указателя.

Как запретить сборщику мусора вмешиваться в это?
C#
1
2
3
IntPtr pAdd = getArgsAddresCHandler(pI); //получение указателя на неуправлямую память через внешний неуправляемый метод
Marshal.Copy(inputBuffer, 0, pAdd, inputLength); //копирование данных в неуправляемую память, inputBuffer is byte[]
IntPtr pRes = getResultsAddresCHandler(pI); //выполнение неуправляемой функции над данными
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
25.04.2025, 15:10
Помогаю со студенческими работами здесь

Полиморфизм: вызов метода базового класса, переопределенного метода и нового метода
В базовом классе метод помечен как virtual. Насколько я понял из книги: override означает, что...

преобразование ref byte в byte[] или в IntPtr без использования неуправляемого кода
есть COM интерфейс с методом: virtual /* */ HRESULT STDMETHODCALLTYPE OnReceivePacket( ...

опасности неуправляемого кода
какие опасности несет в себе неуправляемый код. На ум приходит только назначение переменным...

Запуск неуправляемого кода внутри кода c#
часто требуется при выполнении программы запускать исполняемые файлы (exe) с ключами. Для этого...

Быстродействие управляемого и неуправляемого кода
начал изучать небезопасный код,написал прогу в ней 2 функции одна с управляемым кодом вторая...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
PhpStorm 2025.3: WSL Terminal всегда стартует в ~
and_y87 14.12.2025
PhpStorm 2025. 3: WSL Terminal всегда стартует в ~ (home), игнорируя директорию проекта Симптом: После обновления до PhpStorm 2025. 3 встроенный терминал WSL открывается в домашней директории. . .
Как объединить две одинаковые БД Access с разными данными
VikBal 11.12.2025
Помогите пожалуйста !! Как объединить 2 одинаковые БД Access с разными данными.
Новый ноутбук
volvo 07.12.2025
Всем привет. По скидке в "черную пятницу" взял себе новый ноутбук Lenovo ThinkBook 16 G7 на Амазоне: Ryzen 5 7533HS 64 Gb DDR5 1Tb NVMe 16" Full HD Display Win11 Pro
Музыка, написанная Искусственным Интеллектом
volvo 04.12.2025
Всем привет. Некоторое время назад меня заинтересовало, что уже умеет ИИ в плане написания музыки для песен, и, собственно, исполнения этих самых песен. Стихов у нас много, уже вышли 4 книги, еще 3. . .
От async/await к виртуальным потокам в Python
IndentationError 23.11.2025
Армин Ронахер поставил под сомнение async/ await. Создатель Flask заявляет: цветные функции - провал, виртуальные потоки - решение. Не threading-динозавры, а новое поколение лёгких потоков. Откат?. . .
Поиск "дружественных имён" СОМ портов
Argus19 22.11.2025
Поиск "дружественных имён" СОМ портов На странице: https:/ / norseev. ru/ 2018/ 01/ 04/ comportlist_windows/ нашёл схожую тему. Там приведён код на С++, который показывает только имена СОМ портов, типа,. . .
Сколько Государство потратило денег на меня, обеспечивая инсулином.
Programma_Boinc 20.11.2025
Сколько Государство потратило денег на меня, обеспечивая инсулином. Вот решила сделать интересный приблизительный подсчет, сколько государство потратило на меня денег на покупку инсулинов. . . .
Ломающие изменения в C#.NStar Alpha
Etyuhibosecyu 20.11.2025
Уже можно не только тестировать, но и пользоваться C#. NStar - писать оконные приложения, содержащие надписи, кнопки, текстовые поля и даже изображения, например, моя игра "Три в ряд" написана на этом. . .
Мысли в слух
kumehtar 18.11.2025
Кстати, совсем недавно имел разговор на тему медитаций с людьми. И обнаружил, что они вообще не понимают что такое медитация и зачем она нужна. Самые базовые вещи. Для них это - когда просто люди. . .
Создание Single Page Application на фреймах
krapotkin 16.11.2025
Статья исключительно для начинающих. Подходы оригинальностью не блещут. В век Веб все очень привыкли к дизайну Single-Page-Application . Быстренько разберем подход "на фреймах". Мы делаем одну. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2025, CyberForum.ru