Главная
Новости
Программы
Проекты
Статьи
Ссылки
Контакты
Гостевая
Выпуск №5. Майлслоты

Здравствуйте, уважаемые читатели.

Сначала хочу поздравить вас всех, пусть и с опозданием, с прошедшими праздниками: С Новым годом, С Рождеством и со Старым новым годом. Хочу пожелать счастья и успехов во всем!

Последнее время у меня мало времени, к тому же надвинулась сессия, как всегда внезапно Так что пока рассылкой заниматься почти нет времени, но в будущем я планирую все-таки ее оживить.

Как и обещал, сегодня я решил поговорить о майлслотах (Mailslots). Они относятся к механизмам межпроцессного взаимодействия таким, как Pipes (каналы), Mutexes (Мьютексы), Semaphores (Семафоры) и т .д. Неплохая вводная статейка по синхронизации есть на RSDN, называется она "Многозадачность в Windows". Однако майлслоты еще вызывают интерес вот в каком ракурсе: именно через них, а точнее через майлслот под именем messngr передают свои сообщения WinPopup в Win9x и "Служба сообщений" в NT, W2k, WinXP. В принципе, для передачи сообщений по сети, можно найти массу других способов, зачастую намного более элегантных, НО: "Служба сообщений" уже установлена и по умолчанию включена на компьютерах с операционками на базе NT и притом пользователь не может ее отключить. Это дает нам возможность рассылать какие-либо важные информационные сообщения по сети, например о том, что на сервере запущена архивация или что сисадмин режется в кваку и ему не надо звонить и отвлекать Главное – при этом не требуется установка каких-либо клиентских программ на компьютерах-получателях.

Но не все так радужно. Майлслоты построены на передаче датаграмм (datagrams). То есть, говоря другими словами, сообщения могут не дойти до адресата. Что поделать, доставка не гарантированная. Если нужна гарантированная доставка, то можно использовать именованные каналы (Named pipes). Кстати, у негарантированной доставки есть один очевидный плюс – это возможность посылки широковещательных сообщений, например на всю рабочую группу или домен.

Вот как выглядит общий механизм работы майлслотов. Владелец (Owner) создает свой майлслот и периодически проверяет, не поступило ли чего в него. Другие компьютеры открывают файл, именем которого служит имя майлслота и пишут туда информацию. Владелец, увидев что ему прислали что-то, читает из своего майлслота информацию как из файла и парсит ее. Все довольно примитивно.

Осталось лишь разобраться с форматом сообщений, посылаемых "Службой сообщений". Он тоже довольно прост (0 – это ноль):

Имя отправителя
0
Имя получателя
0
Сообщение
0

Самое интересное, что "Имя отправителя" и "Имя получателя" чисто номинальные поля и могут быть любыми или вообще отсутствовать, например вот так

0
0
Сообщение
0

Отправка

Теперь посмотрим, как можно послать сообщение. Я набросал небольшую программку, которая это делает, вот ее код:

#include "stdafx.h"

#include <memory>

bool NetSend(TCHAR* pszDest, TCHAR* pszMessage)

{

if (

(pszDest == 0) ||

(*pszDest == 0) ||

(pszMessage == 0) ||

(*pszMessage == 0)

)

return false;

std::auto_ptr<TCHAR> pszFileName (new TCHAR[MAX_PATH]);

::lstrcpy(pszFileName.get(), "\\\\");

::lstrcat(pszFileName.get(), pszDest);

::lstrcat(pszFileName.get(), "\\mailslot\\messngr");

HANDLE hMailSlot = ::CreateFile(

pszFileName.get(),

GENERIC_WRITE,

FILE_SHARE_READ,

0,

OPEN_EXISTING,

FILE_ATTRIBUTE_NORMAL,

0

);

if (hMailSlot != INVALID_HANDLE_VALUE)

{

TCHAR szCompName [MAX_COMPUTERNAME_LENGTH + 1];

TCHAR szUserName [65];

DWORD dwCompNameLen  = sizeof(szCompName);

DWORD dwUserNameLen  = sizeof(szUserName);

DWORD dwDestLen   = ::lstrlen(pszDest);

DWORD dwMessageLen   = ::lstrlen(pszMessage);

DWORD dwSendMsgLen   = 0;

DWORD dwBytesWritten = 0;

::GetComputerName(szCompName, &dwCompNameLen);

::GetUserName(szUserName, &dwUserNameLen);

dwSendMsgLen = dwUserNameLen + dwCompNameLen + dwDestLen + dwMessageLen + 5;

std::auto_ptr<TCHAR> pSendBuffer(new TCHAR[dwSendMsgLen]);

::lstrcpy(pSendBuffer.get(), szUserName);

::lstrcat(pSendBuffer.get(), "@");

::lstrcat(pSendBuffer.get(), szCompName);

LPTSTR pszDestSubStr = pSendBuffer.get() + ::lstrlen(pSendBuffer.get()) + 1;

::lstrcpy(pszDestSubStr, pszDest);

LPTSTR pszMsgSubStr = ::lstrcpy(pszDestSubStr + dwDestLen + 1, pszMessage);

::CharToOem(pszMsgSubStr, pszMsgSubStr);

::WriteFile(hMailSlot, pSendBuffer.get(), dwSendMsgLen, &dwBytesWritten, 0);

::CloseHandle(hMailSlot);

return (dwSendMsgLen == dwBytesWritten);

}

return false;

};

int APIENTRY WinMain(HINSTANCE hInstance,

   HINSTANCE hPrevInstance,

   LPSTR  lpCmdLine,

   int nCmdShow)

{

NetSend ("Computer", "Сообщение для пользователя на компьютере Computer");

NetSend ("Workgroup", "Сообщение для рабочей группы Workgroup");

NetSend ("*", "Сообщение для всех в сети");

return 0;

}

 

Хотелось бы отметить преобразование CharToOem. В службе сообщений, видимо, предполагается что сообщения шлются из командной строки командой NET SEND. В результате, чтобы сообщения на языках, кроме английского, были читаемые, внутри функции происходит преобразование OemToChar. Следовательно, если будете передавать текст из командной строки, то CharToOem делать уже не надо.

Получение

Получение почти не отличается от отправки, только что разве тем, что не нужно открывать файл. Вместо открытия, хэндл на файл-майлслот возвращается функцией CreateMailslot. Вот еще один небольшой пример, который я набросал:

#include "stdafx.h"

#include <memory>

const int MAX_MSG_SIZE = 256;

int APIENTRY WinMain(HINSTANCE hInstance,

   HINSTANCE hPrevInstance,

   LPSTR  lpCmdLine,

   int nCmdShow)

{

HANDLE hMailSlot = ::CreateMailslot("\\\\.\\mailslot\\messngr", MAX_MSG_SIZE, 0, 0);

if (hMailSlot == INVALID_HANDLE_VALUE)

{

::MessageBox(0, "Не удалось создать майлслот", "Ошибка", MB_OK | MB_ICONERROR);

return 1;

}

DWORD dwNextMsgSize = 0;

while( ::GetMailslotInfo(hMailSlot, 0, &dwNextMsgSize, 0, 0))

{

if (dwNextMsgSize != MAILSLOT_NO_MESSAGE)

{

  std::auto_ptr<TCHAR> pszMsgBuffer(new TCHAR[dwNextMsgSize + 1]);

  std::auto_ptr<TCHAR> pszResult(new TCHAR[dwNextMsgSize + 64]);

  ::memset(pszMsgBuffer.get(), 1, dwNextMsgSize + 1);

  ::memset(pszResult.get(), 0, dwNextMsgSize + 64);

  DWORD  dwBytesRead = 0;

  ::ReadFile(hMailSlot, pszMsgBuffer.get(), dwNextMsgSize + 1,&dwBytesRead, 0);

  TCHAR* pszSender = pszMsgBuffer.get();

  TCHAR* pszDest   = pszSender + ::lstrlen(pszSender) + 1;

  TCHAR* pszMsg = pszDest + ::lstrlen(pszDest) + 1;

  ::OemToChar(pszMsg, pszMsg);

  ::lstrcpy(pszResult.get(), "Сообщение от ");

  ::lstrcat(pszResult.get(), pszSender);

  ::lstrcat(pszResult.get(), " для ");

  ::lstrcat(pszResult.get(), pszDest);

  ::lstrcat(pszResult.get(), "\n\n");

  ::lstrcat(pszResult.get(), pszMsg);

  ::MessageBox(0, pszResult.get(), "Получено сообщение", MB_OK);

}

::Sleep(1000);

}

::CloseHandle(hMailSlot);

return 0;

}

 

Программа практически ничего не делает. Она только получает переданные сообщения и выводит их в MessageBox и делает это до тех пор, пока ее не прибьют из диспетчера задач. Тут есть одна загвоздка – мне эту программку не удалось заставить принимать сообщения, посылаемые командой NET SEND. То есть сообщения, оправляемые мной же первой программкой, прекрасно доходят, а вот когда посылаю командой NET SEND – кукиш с маслом. Как оказалось после некоторого разбирательства, для корректной работы требуется зарегистрировать псевдоним сообщений (message alias) функцией NetMessageNameAdd. В свою очередь для корректной работы этой функции требуется запущенная служба сообщений. А если служба сообщений запущена, то самому принимать сообщения не удастся. А если остановить службу сообщений... Ну вы поняли

Разное

Давненько мне хотелось сделать так, чтобы Auto Completion, встроенный в VC узнавал конструкции на WTL. А то мало того, что помощи нет, так еще и подсказка не высвечивается. Надо сказать, что реализация этого самого Auto Completion в VC довольно странная – никогда не знаешь, где и как будет работать. Но, в конце концов, я от него добился понимания WTL-ных конструкций. Сделать тоже самое, если еще не сделали, вы можете так:

1.   Качаете файл autowtl.rar

2.   Меняете в файле AutoWtl.reg (он внутри архива) путь к файлу wtl.ncb и импортируете его, либо руками находите параметр AutoCompletionStores по адресу HKLM\Software\Microsoft\DevStudio\6.0\Tools в реестре и добавляете к его значению через ";" путь к файлу wtl.ncb. Наиболее предпочтителен именно второй вариант, так как если у вас уже были установлены библиотеки, то после простого импорта уже в них может пропасть Auto Completion.

На сегодня все.

Всегда буду рад любым вашим отзывам, пожеланиям и не очень рад ругательствам, но все равно пишите

Всего наилучшего,

Используются технологии uCoz