Лабораторные работы / Windows лаб 1-4 / lab2_var3_kod
.pdfВариант 3
Задание
Написать программу для обмена сообщениями через механизм Windows Mailstots, которая действует следующим образом:
1.Запрашивает у пользователя наименование почтового ящика и пытается создать его функцией CreateMailslot(), а если почтовый ящик уже существует, получает его описатель функцией CreateFile().
Указание 1. Пользователь должен вводить полное наименование почтового ящика, например, \\.\mailslot\test. Если оно не начинается с \\.\, такой ящик нельзя создать, к нему можно только подключиться (он расположен на удаленной машине). Указание 2. Можно диагностировать, что почтовый ящик существует, если функция CreateMailslot() завершилась ошибкой с соответствующим кодом (который можно получить функцией GetLastError()). Узнать нужный код ошибки можно по MSDN.
2.Запрашивает у пользователя, какое действие следует выполнить:
2.1.Получить информацию о почтовом ящике: количество сообщений, размер последнего сообщения (которое будет извлечено следующим), наибольший допустимый размер сообщения для данного ящика. Сведения необходимо вывести на экран, получив их функцией GetMailslotInfo().
2.2.(Только для процессов-клиентов.) Поместить сообщение в почтовый ящик. Необходимо запросить у пользователя текст сообщения, который может быть многострочным и завершается пустой строкой, а затем записать сообщение в почтовый ящик функцией WriteFile().
2.3.(Только для процесса-сервера.) Получить сообщение из почтового ящика.
Необходимо считать очередное сообщение функцией ReadFile() и отобразить его на экране. 2.4. Завершить работу. Следует отключиться от почтового ящика, закрыв его
описатель функцией CloseHandle().
Указание. По умолчанию созданный функцией CreateMailslot() почтовый ящик
доступен для чтения и записи только ОС (учетной записи LOCAL SYSTEM ). Параметром lpSecurityAttributes можно разрешить доступ всем процессам; код для заполнения структуры SECURITY_ATRIBUTES приведен в приложении.
Проверку работы программ следует выполнять, запуская несколько экземпляров приложения, например, из «Проводника».
Выполнение
#define WINVER 0x0501 #include <windows.h> #include <sddl.h> #include <iostream> #include <memory> #include <string> #include <cstring> #include <cstdlib> #include <clocale>
#define MAX_MSG_SIZE 512
1
//Запрос наименования почтового ящика static std::string GetMailslotPath()
{
static const char name_prefix[] = "\\\\.\\mailslot\\"; std::string result;
bool valid_name = false; do
{
std::cout << "Enter full mailslot path: "; std::getline(std::cin, result);
//Проверка корректности введенного имени if (result.size() < sizeof(name_prefix) ||
strncmp(name_prefix, result.c_str(), sizeof(name_prefix) - 1) != 0)
{
std::cout << "Error. Name must be: " << name_prefix << "name.\n";
}
else
{
valid_name = true;
}
} while (!valid_name); return result;
}
//Разрешение доступа всем процессам
static PSECURITY_DESCRIPTOR CreateSecurityDescriptor()
{
const char* sddl = "D:(A;OICI;GRGW;;;AU)(A;OICI;GA;;;BA)"; PSECURITY_DESCRIPTOR security_descriptor = nullptr; ConvertStringSecurityDescriptorToSecurityDescriptor(sddl, SDDL_REVISION_1,
&security_descriptor, nullptr); return security_descriptor;
}
static SECURITY_ATTRIBUTES CreateSecurityAttributes()
{
SECURITY_ATTRIBUTES attributes; attributes.nLength = sizeof(attributes);
attributes.lpSecurityDescriptor = CreateSecurityDescriptor(); attributes.bInheritHandle = FALSE;
return attributes;
}
//Обработка команд пользователя
void Execute(HANDLE handle, bool is_server)
{
//Пояснения пользователю std::string hint = "Type "
+std::string(is_server ? "'read' to read the next one,\n" : "'write' to write,\n")//Сервер читает сообщения, клиент пишет сообщения
+std::string(is_server ? " 'check' to check for messages,\n" : "")//Просмотр информации доступен только серверу
2
+ " 'quit' to terminate.\n"; std::cout << hint;
std::string cmd; while (true)
{
std::cout << "> "; std::getline(std::cin, cmd);
//Выполнение запроса на завершение работы if (cmd == "quit")
{
break;
}
if (is_server)//Если приложение - сервер
{
//Получение информации о ящике: кол-ва сообщений, размера посл-го сообщения и макс. допустимого размера
DWORD msg_count; DWORD next_size; DWORD max_size;
if (!GetMailslotInfo(handle, &max_size, &next_size, &msg_count, nullptr))
{
std::cout << "Error. Error code: " << GetLastError()<<'\n'; break;
}
//Чтение и отображение очередного сообщения if (cmd == "read")
{
if (msg_count)
{
auto buf = std::make_unique<char[]>(next_size); DWORD read;
if (!ReadFile(handle, buf.get(), next_size, &read, nullptr))
{
std::cout << "Error. Error code: " << GetLastError()<<'\n'; break;
}
std::cout.write(buf.get(), read); std::cout << '\n';
}
else
{
std::cout << "[ OK ] No messages for you, Commander.\n";
}
}
//Отображение информации о входящих сообщениях else if (cmd == "check")
{
3
if (msg_count == 0)
{
std::cout << "[ OK ] No messages for you, Commander\n";
}
else
{
std::cout << "[ OK ] Commander, you have " << msg_count << " new messages on your private terminal.\n";
std::cout << "The size of next message is " << next_size << "B.\n"; std::cout << "The maximum size of a message is " << max_size << "B.\n";
}
}
else
std::cout << "How may I help you, Commander?\n" << hint;
}
else //Если приложение - клиент
{
//Запрос и запись сообщения пользователя if (cmd == "write")
{
std::string message; std::string line;
std::cout << "Enter message text. Terminate with an empty line.\n"; do
{
std::getline(std::cin, line);
if (!line.empty() && !message.empty()) message.append("\r\n");
message.append(line); } while (!line.empty());
DWORD written;
if (!WriteFile(handle, message.c_str(), message.size(), &written, nullptr))
{
std::cout << "Error. Error code: " << GetLastError()<<'\n'; break;
}
else
std::cout << "[ OK ] Message sent.\n";
}
else
std::cout << "How may I help you, Commander?\n" << hint;
}
}
}
int main()
{
setlocale(LC_ALL, "");
bool is_server = true;//Флаг: 1 - сервер, 0 - клиент; после первого запуска - сервер, при
4
следующих запусках - клиент
auto path = GetMailslotPath();
auto sec_attr = CreateSecurityAttributes();
DWORD err = ERROR_SUCCESS;
auto mailslot = CreateMailslot(path.c_str(), MAX_MSG_SIZE, MAILSLOT_WAIT_FOREVER, &sec_attr);//Попытка создания нового почтового ящика
if (mailslot == INVALID_HANDLE_VALUE)
{
err = GetLastError();
//Если ящик уже существует
if (err == ERROR_ALREADY_EXISTS)
{
is_server = false;//Приложение - клиент
mailslot = CreateFile(path.c_str(), GENERIC_WRITE, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);//Получение описателя существующего почтового ящика
err = GetLastError();
}
}
if (err != ERROR_SUCCESS)
{
std::cout << "Error. Error code: " << err <<'\n'; return EXIT_FAILURE;
}
Execute(mailslot, is_server);
LocalFree(sec_attr.lpSecurityDescriptor);
CloseHandle(mailslot);//Закрытие дескриптора почтового ящика return EXIT_SUCCESS;
}
5