
Для обмена данными между библиотекой DLL, написанной на языке C/C++ и приложением, написанном на языке C#, удобно и эффективно использовать Отображаемые в Памяти Файлы (MemoryMappedFile). По сути, это выделенный участок оперативной памяти компьютера (скорость!), который имеет свое уникальное имя и размер в байтах. Оба эти параметра задаются программистом. В дальнейшем можно, как читать из этой памяти, так и писать в нее, подключившись к ней в библиотеке DLL и в приложении C#.
Был проведен тест скорости обмена сообщениями, в тесте принимали участие следующие технологии: MemoryMappedFile, NamedPipes и Socket. Создавались по два отдельных приложения на C#, сервер и клиент, которые должны были обменяться друг с другом текстовыми сообщениями размером 120 символов 500 000 раз. На все это у них ушло следующее количество времени:
MemoryMappedFile: 1,5 секунды
NamedPipes: 12,5 секунд
Socket: 14 секунд
При этом, количество требуемого кода, так же, было меньше всего у MemoryMappedFile, по моему, выбор очевиден!
Пример создания и использования именованной памяти с именем "MyMemory" и размером 256 байт. В примере реализован следующий алгоритм:
QLua(Lua)
- Подключает библиотеку DLL
- Запускает функцию отправки сообщений в C#
- Останавливает функцию отправки сообщений в C#
Код скрипта QLua:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
| -- Подключает библиотеку DLL
require("QluaCSharpConnector");
-- Флаг для поддержания работы функции main
IsRun = true;
function main()
-- Запускает функцию отправки сообщений в C#
QluaCSharpConnector.StartSendHi();
-- Обеспечивает работу скрипта и библиотеки до остановки скрипта пользователем
while IsRun do
sleep(1000);
end;
end;
function OnStop()
-- Останавливает функцию отправки сообщений в C#
QluaCSharpConnector.StopSendHi();
-- Останавливает цикл в функции main
IsRun = false;
end; |
C/C++
- Библиотека DLL создает/подключается к именованной памяти.
- Отправляет (записывает в память) текстовое сообщение: "Привет из C/C++".
- Читает память с периодичностью в 1 секунду, если память стала чиста, сообщение отправляется вновь.
Код библиотеки DLL (C/C++):
(Если Вы не знаете как создавать библиотеки DLL, которые можно использовать в скриптах QLua(Lua), ознакомьтесь, пожалуйста, с
данной статьей).
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
| #include <windows.h>
//=== Необходимые для Lua константы ============================================================================//
#define LUA_LIB
#define LUA_BUILD_AS_DLL
//=== Заголовочные файлы LUA ===================================================================================//
extern "C" {
#include "Lua\lauxlib.h"
#include "Lua\lua.h"
}
//=== Получает указатель на выделенную именованную память =====================================================//
// Имя для выделенной памяти
TCHAR Name[] = TEXT("MyMemory");
// Создаст, или подключится к уже созданной памяти с таким именем
HANDLE hFileMapMyMemory = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 256, Name);
//=== Стандартная точка входа для DLL ==========================================================================//
BOOL APIENTRY DllMain(HANDLE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
{
return TRUE;
}
// Флаг необходимости отправлять сообщение
bool Run = true;
//=== Реализация функций, вызываемых из LUA ====================================================================//
static int forLua_StartSendHi(lua_State *L) // Отправляет сообщения для C#
{
// Если указатель на память получен
if (hFileMapMyMemory)
{
// Получает доступ (представление) непосредственно к чтению/записи байт
PBYTE pb = (PBYTE)(MapViewOfFile(hFileMapMyMemory, FILE_MAP_READ | FILE_MAP_WRITE, 0, 0, 256));
// Если доступ получен
if (pb != NULL)
{
// Очищает память при первом обращении
for (int i = 0; i < 256; i++)pb[i] = '\0';
// Бесконечный цикл пока Run == true
while (Run)
{
// Если запись пустая (либо первая отправка, либо C# очистил память, подтвердив получение)
if (pb[0] == 0)
{
// Записывает текст сообщения в память
char *Str = "Привет из C/C++";
memcpy(pb, Str, strlen(Str));
}
// Пауза в 1 секунду
Sleep(1000);
}
// Закрывает представление
UnmapViewOfFile(pb);
// Закрывает указатель на память
CloseHandle(hFileMapMyMemory);
}
}
return(0);
}
static int forLua_StopSendHi(lua_State *L) // Прекращает отправку сообщений для C#
{
Run = false; // Выключает флаг
return(0);
}
//=== Регистрация реализованных в dll функций, чтобы они стали "видимы" для Lua ================================//
static struct luaL_reg ls_lib[] = {
{ "StartSendHi", forLua_StartSendHi }, // из скрипта Lua эту функцию можно будет вызывать так: QluaCSharpConnector.StartSendHi(); здесь можно указать любое другое название
{ "StopSendHi", forLua_StopSendHi }, // соответственно
{ NULL, NULL }
};
//=== Регистрация названия библиотеки, видимого в скрипте Lua ==================================================//
extern "C" LUALIB_API int luaopen_QluaCSharpConnector(lua_State *L) {
luaL_openlib(L, "QluaCSharpConnector", ls_lib, 0);
return 0;
} |
C#
- Приложение на C# создает/подключается к именованной памяти.
- Читает память с периодичностью в 1 секунду, если в памяти появилось текстовое сообщение: "Привет из C/C++", выводит его в текстовое поле и очищает память, сообщая тем самым DLL что сообщение получено.
Код C#:
Создайте проект
C# Windows Forms, если Вы не знаете как это сделать, посмотрите
здесь.
Разместите на форме один
TextBox (из вкладки
"Панель элементов"), установите его свойство
"Multiline" в
"True", а свойство
"ScrollBars" в
"Vertical" и растяните его по форме, чтобы получилось примерно следующее:

Кликните по шапке формы, перейдите в окне
"Свойства" на вкладку
"События" и сделайте двойной клик в пустой строке напротив поля
"FormClosing". Таким образом в файл
"Form1.cs" добавится новая функция
"Form1_FormClosing", которая будет вызываться после того, как Вы нажмете на кнопку закрытия формы запущенного приложения.
Снова кликните по шапке формы, перейдите в окне "Свойства" на вкладку "События" и сделайте двойной клик в пустой строке напротив поля "Shown". Таким образом в файл "Form1.cs" добавится новая функция "Form1_Shown", которая будет вызываться при первом показе формы.
Сейчас полностью замените код в файле "Form1.cs" на этот:
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
| using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.IO;
using System.IO.MemoryMappedFiles;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace Test
{
public partial class Form1 : Form
{
// Выделенная именованная память
public MemoryMappedFile Memory;
// Объект для чтения из памяти
StreamReader SR_Memory;
// Объект для записи в память
StreamWriter SW_Memory;
// Флаг необходимости получать сообщения
bool Run = true;
public Form1()
{
InitializeComponent();
// Создаст, или подключится к уже созданной памяти с таким именем
Memory = MemoryMappedFile.CreateOrOpen("MyMemory", 256, MemoryMappedFileAccess.ReadWrite);
// Создает поток для чтения
SR_Memory = new StreamReader(Memory.CreateViewStream(), System.Text.Encoding.Default);
// Создает поток для записи
SW_Memory = new StreamWriter(Memory.CreateViewStream(), System.Text.Encoding.Default);
}
// Делегат нужен для того, чтобы безопасно обратиться к TextBox из другого потока
private delegate void TB(string Msg);
private void AppText(string Msg)
{
// Добавляет к сообщению символ перехода на новую строку
textBox1.AppendText(Msg + Environment.NewLine);
}
private void GetMessage()// Получает сообщения от DLL, выводит их в текстовое поле, очищает память
{
string Msg = "";
// Цикл работает пока Run == true
while(Run)
{
// Встает в начало потока для чтения
SR_Memory.BaseStream.Seek(0, SeekOrigin.Begin);
// Считывает данные из потока памяти, обрезая ненужные байты
Msg = SR_Memory.ReadToEnd().Trim('\0', '\r', '\n');
// Если в потоке нужное сообщение ("Привет из C/C++")
if (Msg == "Привет из C/C++")
{
// Потокобезопасно выводит сообщение в текстовое поле
BeginInvoke(new TB(AppText), Msg);
// Встает в начало потока для записи
SW_Memory.BaseStream.Seek(0, SeekOrigin.Begin);
// Очищает память, заполняя "нулевыми байтами"
for (int i = 0; i < 256; i++) SW_Memory.Write("\0");
// Очищает все буферы для SW_Memory и вызывает запись всех данных буфера в основной поток
SW_Memory.Flush();
}
//Пауза в 1 секунду
Thread.Sleep(1000);
}
// По завершению цикла, закрывает все потоки и освобождает именованную память
SR_Memory.Close();
SW_Memory.Close();
Memory.Dispose();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
Run = false;// Выключает флаг
}
private void Form1_Shown(object sender, EventArgs e)
{
// Запускает функцию чтения и вывода сообщений в отдельном потоке, чтобы форма отвечала на действия пользователя
new Thread(() =>
{
GetMessage();
}).Start();
}
}
} |
Скомпилируйте проект.
Теперь, после добавления библиотеки DLL в каталог терминала QUIK (туда, где файл "info.exe"), запуска скрипта QLua и запуска приложения C#, Вы увидите как на форме C#, с периодичностью в 1 секунду, появляются сообщения "Привет из C/C++":
Если у Вас появились какие-то вопросы, задайте их в комментариях под статьей !!!
Здравствуйте.
Постараюсь коротко изложить проблему, но думаю стоит описать с самого начала.
Решил создавать Именованную память для каждой бумаги "class_sec", например "SPBFUT_SiM3".
Чтобы постоянно не подключаться к памяти, а только 1 раз, решил хранить HANDLE, выбрал std::map где ключ "class_sec", а значение HANDLE. Когда вызываю функцию записи в Именованную память передаю "class_sec", беру HANDLE из map, а если нету, тогда создаю. И все работает, но я обнаружил что открывается очень много HANDLE для одной и той же бумаги. Я стал выяснять причину и обнаружил что map "разрушается", то есть ключ "class_sec" превращается через время в мусор типа "x_{+1=.. " и соответственно создается новый HANDLE и помещается снова в map. При чем разрушаются только ключи, значения HANDLE остаются целые. При остановке Lua скрипта я вызываю другую функцию, которая перебирает весь map и закрывает HANDLE. Все HANDLE, в том числе и с поврежденными ключами, закрываются. Откртых HANDLE не остается.
Для выяснения причины удалил все из функции и оказалось что разрушается std::map, std::unordered_map и обычные массивы со значениями const char* и просто char* тоже разрушаются. Поэтому я приведу упрощенный код, только с map где ключ "class_sec" а значение INT случайное число весто HANDLE. И так map тоже разрушается.
Смотрю в DebugView и через время вижу что ключи испорчены
Сначала все хорошо:
HANDLE SPBFUT_SiM3 41
HANDLE SPBFUT_GZM3 18467
...
но потом становится так:
HANDLE < .1[23.Vs 41 HANDLE SPBFUT_GZM3 18467 HANDLE SPBFUT_SiM3 6334 то есть SPBFUT_SiM3 ключ испортился и был создан новый. Что касается Lua, возможно это происходит только если вызываю функцию из OnAllTrade, так как я пробовал вызывать просто из main функцию в цикле с миллионом итераций и случайно передавал разные "class_sec" и разрушения не заметил, с map все хорошо.
Так же я пробовал проверять наличие в map, но это не решило проблему.
Мои небольшие знания в СИ закончились, больше не знаю в каком направлении искать причину и почему такое происходит, возможно кто-то что-то подскажет...
Вообще на работу моего алгоритма это ни как не влияет, я даже не знал что открываются тысячи HANDLE, случайно заглянул в Process Explorer и увидел. Можно конечно игнорировать, но я предпочитаю избавиться от этой утечки, если ее так можно назвать. Даже думал создать еще один алгоритм, который будет закрывать HANDLE с испорченными ключали в map и удалять эти записи в map. Но это вообще бред... Хочу понять причину и устранить.
Здравствуйте.
1. std::map HANDLES; - не заданы тип ключа и тип поля. Как выполняется ваша программа?
----
2. const char* name это указатель на константную строку. По смыслу в качестве ключа и поля map надо использовать типы-значения (которые нельзя изменить "сбоку"), например, std::string.
Попробуйте проверить следующий вариант:
1) Вместо std::map HANDLES; -> std::map HANDLES;
2) Вместо const char* name = lua_tostring(L, 1); -> std::string name = lua_tostring(L, 1);
Исправление:
1) Вместо std::map HANDLES; -> std::map HANDLES;
Проблема с угловыми скобками.
Благодарю за поясниения.
1. Да. Я не досмотрел, когда писал на сайте код, конечно в программе у меня указаны типы, в тестовой я писал несколько вариантов:
А в полной, для чего собственно и задумаывается, было прописано
2. Да, потом, когда уже написал сюда вопрос, я побробовал использовать тип string, сразу как то не пришло в голову, но хотя я особо и не верил что это изменит ситуацию. Как оказалось это действительно полностью решило проблему. map не разрушается, лишние HANDLE не создаются, все хорошо.
- "По смыслу в качестве ключа и поля map надо использовать типы-значения (которые нельзя изменить "сбоку")" - Вот об этом мне ничего небыло известно, так как я не изучал основы языка СИ, а лишь пишу код используя найденные примеры в Интернете и описания функций. Когда испольовал тип string, что решило проблему, я стал догадываться что у типов char* есть какие то особенности, которые мне не известны, но какие именно не понятно, все же нужно изучать основы.
Еще раз благодарю за помощь.
Ну да, понятно. Видать это не я не досмотрел в прошлый раз, это сайт обрезал типы. В сообщении их не видно, но они есть )))
Интересно, почему вы стали использовать C++?
Таблица в Lua это map, но ключами и полями могут быть данные любого Lua-типа. При этом таблицы реализованы в Lua на C и сделано это на хорошем уровне, скорее всего, сравнимом с реализацией map в C++.
Мне нужно создать Именованную память для каждой бумаги и записывать в нее данные после получения каждой сделки. Если передать из LUA даные в СИ, открыть доступ к памяти, получить указатель, записать данные и закрыть, на это уходит много времени. Поэтому я решил что на первой сделке открываю доступ, сохраняю HANDLE в map и так же с указателем, его тоже сохраняю в map, записываю данные в память и ничего не закрываю. Следующая сделка, теперь беру данные из map и пишу данные. Если сделка по другой бумаге, открывается или создается новая память и так же не закрывается. А при остановке скрипта всё закрываю.
То есть мне нужно было где то хранить HANDLE, если в lua таблицах, то я не знаю как, поэтому решил хранить в СИ, первое что нашел, где можно использовать текстовые ключи, это map.
Доброго времени суток.
Такая проблема, при переходе через ночь или остановке скрипта, квик наглухо повисает и крашится. Далее листинг скрипта. Помогите пожалуйста разобраться в чем может быть проблема...
и сразу извиняюсь, дабы улетели спец символы....
Здравствуйте, попробуйте поразрывать и повосстанавливать соединение терминала с сервером просто днем, не дожидаясь ночи, возможно эта ситуация вызывает те проблемы, о которых Вы пишите, чтобы найти ошибку, которая Вас интересует нужно и код dll смотреть и еще что у Вас там работает, а на это много времени нужно, которого нет в свободном доступе.
Библиотека используется для отправки команд, свечки передаются с помощью файла.
Но вас понял, попробую. Спасибо))
Пожалуйста 🙂
Разобрался, в том куске кода где я сомневался и правда ошибка, там постоянно исполнялось какое то действие при подключении или при отсутствии подключения к серверу, а этого по хорошему надо избегать, чтобы избежать всякого рода зависаний и дальнейших крашей.
То есть было :
Стало:
Таким образом действия совершаются единоразово при надобности, а не постоянно
Рад, что проблема решилась!
"там можно более хорошей скорости достичь." На самом в деле, в ДЛЛ тожеминимум и только подготовка данных. Там сложная математика привязана, которая выполняется вообще в сторонних программах, таких как - http://www.scilab.org/. Это свободно распространяемый аналог МатЛаба.
Буду рад, если заинтересует. У него тоже С-шное API.
Спасибо, буду иметь в виду, хотя сейчас нет необходимости в каких-то расчетах.
"Дак а что мешает из луа с C# напрямую общаться через сокеты, без всяких DLL ?"
Замысел архитектора.))
Есть мысль сделать из ДЛЛ полноценное приложение. Почти полноценное. Получается достточно просто, но надо проверить. Однако все сразу невозможно. Спязь первична.)
Если мысль заинтересовала и есть время на попробовать, то приватно могу изложить. Хотя, может оказаться, что вы и сами уже так делаете.
Я пробовал в длл создавать окно с кнопочкой при помощи WinAPI, все просто оказалось, но дальше этого не пошел, т.к. не было необходимости. На C# пользовательский интерфейс как-то интереснее разрабатывать, по моему мнению 🙂 А, вообще, я последнее время пришел к выводу, что для робота вполне достаточно чистого QLua, а во всех этих коннекторах есть смысл только если нужно сделать какое-то самостоятельное приложение типа скальперского стакана, а робот, в традиционном понимании, должен сам торговать без участия пользователя, а если требуется участие, то это уже не робот 🙂
Добрый день. Я немного не о том, и не говорю, что ДЛЛ перестанет быть ДЛЛ. Хотя, разумеется, можно и окно с кнопочкой, если сильно хочется.)) ДЛЛ будет выполнять, в общем, те же самые функции, т.е. конечная цель останется без изменения, но все сильно упрощается и возможности и быстродействиие ДЛЛ серьезно увеличиваются. При минимальных затратах.
Что касается - "все делать в КЛуа", то это не для моих стратегий. Полсекунды - это уже вечность.))
По мне, по возможности, как можно меньше Луа и самые примитивные операции, а лучше и вообще без Луа.)) Я до этого работал с терминалом с полным API, но брокер вывел его из эксплуатации, заменив на другой. Надеюсь Квик не исчезнет в одночасье, хотя мне он не особо нравится, но других вариантов - раз, и все.
Да, если много расчетов, то в длл их, действительно, разумнее выполнять, там можно более хорошей скорости достичь.
"Я пробовал в длл создавать окно с кнопочкой при помощи WinAPI, все просто оказалось, но дальше этого не пошел, т.к. не было необходимости."
Ох, Дмитрий, какую хорошую вещь вы пробовали, а здесь ее не выложили!
Есть люди, которые очень хотели бы ее увидеть т.к. сами они ее полгода изобретать будут, а ваш этот вышупомянутый эксперимент их бы спас!
Просто как-то нестабильно все работало, не захотелось допиливать. Хорошо, посмотрю что я там навоял тогда, если найду. Если решу, что это можно выложить, то опубликую, хотя, я, обычно, не выкладываю то, что не работает нормально.
Что-то я сегодня начитался много на тему прямого использования WinAPI - все, как один, говорят что это очень плохая идея, использовать в 21 веке WinAPI ))) Так что, наверное, уже не нужно. У меня своего мнения нет на этот счет, могу только полагаться на мнение сообщества. Наверное, действительно, не нужно))
Хорошо
YUBA, возможно с вами пообщаться на тему взаимодействия стороннего приложения с Квиком через сокеты?
Конечно, но наверное лучше в другом месте. Все таки не наша тема. Пусть Хозяин (Дмитрий) скажет где лучше.
Кстати, уже реализовал. Осталось причесать и в ДЛЛ-ку впихнуть.
Да где удобно, там и общайтесь, может быть кому-то еще полезно будет почитать Вашу переписку, я, как бы, без претензий на царствование на данном сайте 🙂
А, Дмитрий, извиняюсь - ответили
Рад стараться 🙂
Благодарю за поддержку!
Отдельное спасибо за уважительное отношение!
Спасибо, оперативно. Я думаю Дмитрий занят, спросил как с вами связаться и даже донатсов на развите проекта забросил, пока мне ничего не ответили. Моя почта yotra-global@yandex.ru Напишите, если не трудно на нее: КВИК-Луа-Сокет
Провел тестирования клиент-сервера на сокетах. И клиент и сервер реализованы на С# как консольные приложения. Клиент и сервер обменивались строками длиной 120 символов, 500000 циклов. Продолжительность теста составила 35 с, что составляет 14285 циклов/с.
Так-то нормальный результат, ждем статью с примером 🙂
Дайте, пожалуйста, ссылку на какой-нибудь самый простой примерчик, интересно стало.
Здравствуйте, Дмитрий. Большая статья с экземплами -https://professorweb.ru/my/csharp/web/level3/3_1.php Экземплы рабочие, но для непосредственного применения непригодны. Но можно поэкспериментировать и взять за основу.
Что-то не пускают меня по этому адресу 🙂

Изображение:
У меня все ОК. На всяк случай.
https://professorweb.ru/my/csharp/web/level3/3_1.php
Не, меня, что-то, так и не пускает, ладно 🙂
Жаль. Проверил, ссылка не битая, работает.
Или можете прямо решение VS дать.
В принципе, могу. Но относительно кода в статье там, в общем, ничего нового. Применение NET- сокетов везде одинаковое. Я только вынес сами сокеты в отдельные классы и консоли клиента и сервера работают только с их методами.
Ладно, не озадачивайтесь, я уже сам тут ковыряюсь 🙂
Или Вы вот эти примеры использовали?
https://msdn.microsoft.com/ru-ru/library/6y0e13d3(v=vs.110).aspx
https://msdn.microsoft.com/ru-ru/library/kb5kfec7(v=vs.110).aspx
Сейчас много читаю по взаимодействию между процессами. Получается, что Pipes и MemoryMappedFiles - это прошлый век (Ни в коем случае не утверждаю, что их не надо применять.)). Они пошли от старых версий Unix. Сегодня основной технологией являются сокеты (Sockets) - уже и Windows. В общем, сокеты пришли тоже из Unix, а в Linux все взаимодействие между процессами уже изначально строилось на сокетах. Когда-то курсы по Linux(Unix) заканчивал, так там 2 недели только о сокетах и говорили.))
Кстати, не потому-ли в стандартной поставке Framework Sockets есть, а pipes & MMF отсутствуют, и грузятся по необходимости.
Не говорю, что буду на них делать, но тема интересная и в инете много инфы.
Я когда искал решение, почему-то отказался от сокетов, не помню уже сейчас почему, возможно из-за того, что они по скорости проигрывают MMF.
Смотрю сокеты на С++. Быстродействие, говорите.) Простенький клиенти сервер, тест - > 2000 запросов/с. Это 0.5 мс/запрос. На Шарпах наверно побольше. Только надо Unix- сокеты, они локальные, по определению. Еще и могут размножаться как тараканы и идентифицировать клиента.
Не агитирую.)) Но, чем больше читаю, тем больше нравится.
Сокеты можно напрямую из луа использовать, кстати, это Вам еще в копилку плюсов 🙂
Да, даже где-то экземпл видел. Сокеты в Луа - думаю, что это все таки нейтральная инфа. Не для наших задач.
Дак а что мешает из луа с C# напрямую общаться через сокеты, без всяких DLL ?
И MMF у меня сразу была, недавно Windows переустанавливал и студию с нуля ставил, все работает 🙂
Возможно потому, что я неполную установку делал - только С++ и C#. Не исключене, что где-то вспомогательную галочку не поставил.
Да, скорее всего, т.к. я все значения по умолчанию оставлял.
Для тех, кто соберется реализовывать на NET обмен через Pipes или MMF.
В заводской поставке VS 2015 Framework, System.IO.Pipes и System.IO.MemoryMappedFiles отсутствуют.
Ищите на NuGet. Инсталируется в конкретный открытый проект.
Нашел только для Framework 4.6. для более ранних версий - не знаю, хотя д.б. уже с версии 4.0.
Странно, я ничего не подгружал специально, VS обычно сама выделяет ошибку, навожу на нее мышкой, появляется желтый значек с выпадающим списком, раскрываю его, выбираю первый пункт и в uses добавляется нужный компонент.
Подгрузив Pipes из GitHub, в этом открывающемся списке появились заодно и MemoryMappedFiles.))
🙂
Что-то в этом роде и имелось в виду под протоколами обмена. С MMF знаком только в принципе, применять не приходилось. В одном из приложений делал обмен обычными файлами, с записью на небольшой RAM-Disk. Чтобы избежать совместного доступа запись-чтение, создавал дополнительно пустой файл, как флаг окончания записи, который затирался приемником, что использовалось как флаг разрешения следующей записи. В общем, структура обмена практически аналогичная.
Сейчас буду разбираться, как в MMF организована (и есть ли вообще) организация доступа.
Я так понял, что вы обмениваетесь текст файлами в формате, типа CSV?
Я, вообще, строку просто формирую и отправляю, разделитель выбираю в зависимости от данных, универсальных форматов не ищу, т.к. сделки одним способом удобнее отправлять, команды другим, стакан третьим и т.д. Посмотрите раздел меню "QLua C/C++ C#", там много примеров, хотя, сейчас, когда я реализовываю подобные задачи, я немного по другому делаю некоторые вещи, чем там представлено, но, по большому счету, суть та же осталась. А проблему совместного использования я тоже просто решил: DLL, например записала нужные данные в память, после чего установила в первые 4 байта константу _CR_, которая означает, что память ожидает чтения C#, после этого DLL берет новые данные для отправки и читает постоянно первые 4 байта памяти, как видит, что в памяти установлена константа _QW_, означающая, что память готова к записи QLua, пишет туда эти данные и устанавливает константу _CR_. На стороне C# происходит все то же самое, только, вместо записи - чтение. C#, видя константу _CR_, читает то, что прислала DLL, потом затирает память нулевыми байтами и устанавливает константу _QW_, после этого продолжает постоянно читать первые 4 байта, как видит в них _CR_, все повторяется. И никаким конфликтам там просто нет места.
Спасибо, понял. Флаги первые байты. Не учел, что в MMF есть произвольный доступ.
Всегда пожалуйста.