MemoryMappedFile и приложение в виде сервиса
От: Nikolaz Германия www.nikeware.com
Дата: 21.10.15 07:48
Оценка: 18 (1)
Добрый день!

Есть 64-битное С# приложение (что-то типа сервера).
Основная его задача — создание MemoryMappedFile объектов и предоставление доступа к ним со стороны клиента(ов), которые представляют собой обычное приложение (не обязательно С#).
Клиент и сервер общаются посредством текстовых сообщений через pipes. Клиент в результате получает уникальное имя для MemoryMappedFile-объекта и уже своими средствами открывает его и работает с ним.
Сервер можно запускать как обычное приложение, а можно как сервис.

Проблема в том, что если сервер запущен в режиме "как сервис", то при попытке открыть MemoryMappedFile-объект клиентом, получаем "кукишь" . "Кукишь" этот называется PermissionDenied. Понятно, что проблема в том, что сервер, запущенный как сервис, работает на другом уровне привелегий и решением было бы создание MemoryMappedFile-объекта с определенными правами.

На просторах интернета есть на эту тему немного, но решение, что я нашел, увы не работает:
guid_name = Guid.NewGuid().ToString();

if (asService)
    guid_name = "Global\\" + guid_name;

if (asService)
{
    // create a SecurityIdentifier object for "everyone".
    var security = new MemoryMappedFileSecurity();
    SecurityIdentifier everyoneSid = new SecurityIdentifier(WellKnownSidType.WorldSid, null);
    security.AddAccessRule(new AccessRule<MemoryMappedFileRights>(everyoneSid, MemoryMappedFileRights.FullControl, AccessControlType.Allow));

    mmf = MemoryMappedFile.CreateNew(guid_name, total, MemoryMappedFileAccess.ReadWrite, MemoryMappedFileOptions.None, security, HandleInheritability.Inheritable);
}
else
    mmf = MemoryMappedFile.CreateNew(guid_name, total);



27.10.15 10:19: Перенесено из '.NET'
C# service MemoryMappedFile
Re: MemoryMappedFile и приложение в виде сервиса
От: Sinix  
Дата: 21.10.15 08:17
Оценка: 9 (1)
Здравствуйте, Nikolaz, Вы писали:

N>На просторах интернета есть на эту тему немного, но решение, что я нашел, увы не работает:


Вот тут клянутся, что похожий код работает. Сервис точно первый срабатывает?
Для очистки совести я бы скопировал код из ссылки выше и запустил бы клиента под админскими правами. Если сработало — уже понятно, куда копать дальше. Не сработало — тоже понятно, что ничего не понятно.

Да, клиент — не win10 app?
Re: MemoryMappedFile и приложение в виде сервиса
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 21.10.15 08:20
Оценка:
Здравствуйте, Nikolaz, Вы писали:
Кстати а почему не используешь WCF и netNamedPipeBinding?
Я сейчас кстати использую только в качестве консольного приложения
https://msdn.microsoft.com/ru-ru/library/vstudio/ms752253(v=vs.100).aspx
и солнце б утром не вставало, когда бы не было меня
Отредактировано 21.10.2015 9:57 Serginio1 . Предыдущая версия . Еще …
Отредактировано 21.10.2015 9:50 Serginio1 . Предыдущая версия .
Re[2]: MemoryMappedFile и приложение в виде сервиса
От: Nikolaz Германия www.nikeware.com
Дата: 21.10.15 15:37
Оценка: 36 (1)
Здравствуйте, Sinix, Вы писали:

Спасибо.

S>Вот тут клянутся, что похожий код работает. Сервис точно первый срабатывает?

И похоже они правы
Создал тестового клиента как C# приложение — всё Ок.

S>Да, клиент — не win10 app?

Клиент Qt Проблема кажется именно в этом.
Вот здесь что-то про это есть. Буду разбираться ...
Re[3]: MemoryMappedFile и приложение в виде сервиса
От: Pavel Dvorkin Россия  
Дата: 22.10.15 03:22
Оценка:
Здравствуйте, Nikolaz, Вы писали:

N>Клиент Qt Проблема кажется именно в этом.


Если проблема именно в этом, то решается она довольно просто — отказаться от QSharedMemory и написать все на чистом Win API, это несложно.
With best regards
Pavel Dvorkin
Re[4]: MemoryMappedFile и приложение в виде сервиса
От: Nikolaz Германия www.nikeware.com
Дата: 22.10.15 09:14
Оценка: 8 (1)
Здравствуйте, Pavel Dvorkin, Вы писали:

PD>Если проблема именно в этом, то решается она довольно просто — отказаться от QSharedMemory и написать все на чистом Win API, это несложно.

Как временное решение, да. Можно и так. Но баг всё же на стороне Qt и править надо именно там. Судя по исходникам, они путают понятия и считают, что (FILE_MAP_READ | FILE_MAP_WRITE) и FILE_MAP_ALL_ACCESS есть одно и то же.

Ситуация выглядит следующим образом.

Правильный и работающий код Win32 на стороне клиента, который открывает SharedMemory, которая была создана в приложении, запущеном как сервис:

int win32_openSharedMemory(const QString& name)
{
    #define BUF_SIZE (256*1024)

    HANDLE hMapFile;
    LPCTSTR pBuf;

    std::wstring wname;
    wname = name.toStdWString();

    hMapFile = OpenFileMapping(
        FILE_MAP_READ | FILE_MAP_WRITE, // read/write access        
        FALSE,                 // do not inherit the name
        wname.c_str());               // name of mapping object

    if (hMapFile == NULL)
    {
        ilog->trace(ts("Could not open file mapping object (%d).\n", GetLastError()));
        return 1;
    }

    pBuf = (LPTSTR) MapViewOfFile(hMapFile, // handle to map object
        FILE_MAP_READ | FILE_MAP_WRITE,  // read/write permission
        0,
        0,
        BUF_SIZE);

    if (pBuf == NULL)
    {
        ilog->trace(ts("Could not map view of file (%d).\n", GetLastError()));

        CloseHandle(hMapFile);

        return 1;
    }

    ilog->trace("win32_openSharedMemory() OK!");

    UnmapViewOfFile(pBuf);

    CloseHandle(hMapFile);

    return 0;
}


Теперь взглянем на исходники Qt:

bool QSharedMemory::attach(AccessMode mode)
{
    ...

    return d->attach(mode);
}


Где mode до безобразия примитивен:
enum AccessMode
{
   ReadOnly,
   ReadWrite
};


Пойдем дальше (посмотрим, что такое d->attach(mode)):
bool QSharedMemoryPrivate::attach(QSharedMemory::AccessMode mode)
{
    // Grab a pointer to the memory block
    int permissions = (mode == QSharedMemory::ReadOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS);
    memory = (void *)MapViewOfFile(handle(), permissions, 0, 0, 0);
    if (0 == memory) {
        setErrorString(QLatin1String("QSharedMemory::attach"));
        cleanHandle();
        return false;
    }

    ...

    return true;
}


Во-первых локальная переменная permissions определяется очень оригинально: если не readonly, то "ХОЧУ ПОЛНЫЙ ДОСТУП". Наверное Qt считает, что если можно писать, то это уже full-access . Но тогда как мне быть, если я хочу создать SharedMemory блок, только с возможностью записи (Ну вот захотелось мне вдруг ?.

Но самая "засада" в функции handle(), которая вызывается в attach() для MapViewOfFile (см. выше).

HANDLE QSharedMemoryPrivate::handle()
{
    ...
#ifndef Q_OS_WINCE
        hand = OpenFileMapping(FILE_MAP_ALL_ACCESS, false, (wchar_t*)nativeKey.utf16());
#else
    ...
#endif

        if (!hand)
            setErrorString(QLatin1String("QSharedMemory::handle"));

    ...

    return hand;
}


Ну вот просто так: OpenFileMapping(FILE_MAP_ALL_ACCESS, ... и всё!!! Хочу полный доступ и всё тут!

Теперь вернёмся к win32_openSharedMemory() и смоделируем эту ситуацию. Для этого нам нужно всего лишь заменить:
    hMapFile = OpenFileMapping(
        FILE_MAP_READ | FILE_MAP_WRITE, // read/write access        
        FALSE,                 // do not inherit the name
        wname.c_str());               // name of mapping object


на:
    hMapFile = OpenFileMapping(
        FILE_MAP_ALL_ACCESS, // full access
        FALSE,                 // do not inherit the name
        wname.c_str());               // name of mapping object


И мы получим неработающий код, точно такой же как и у Qt.
Ошибка присутствует как в Qt 4.8.6 так и вплоть до 5.5.0.

Вроде всё.

p.s. просьба модераторов перенести в раздел "Qt".
Отредактировано 22.10.2015 9:22 Nikolaz . Предыдущая версия . Еще …
Отредактировано 22.10.2015 9:15 Nikolaz . Предыдущая версия .
Qt QSharedMemory bug
Re[5]: MemoryMappedFile и приложение в виде сервиса
От: Pavel Dvorkin Россия  
Дата: 22.10.15 10:59
Оценка: 4 (1)
Здравствуйте, Nikolaz, Вы писали:

N>Здравствуйте, Pavel Dvorkin, Вы писали:


PD>>Если проблема именно в этом, то решается она довольно просто — отказаться от QSharedMemory и написать все на чистом Win API, это несложно.

N> Как временное решение, да. Можно и так. Но баг всё же на стороне Qt и править надо именно там. Судя по исходникам, они путают понятия и считают, что (FILE_MAP_READ | FILE_MAP_WRITE) и FILE_MAP_ALL_ACCESS есть одно и то же.

N>Ситуация выглядит следующим образом.


<skipped>

Ничего особенного. Библиотеки то и дело не реализуют все возможности подлежащего API
Примеро полно

1. На дотнете нельзя открыть логический или физический диск как файл, хотя CreateFile это позволяет
2. fopen и т.д. не дает возможности ставить share mode
3. ну и твой : либо read, либо full access

Решение — перейти на базовый уровень. Я не вижу, почему в твоем случае это годится только как временное решение. По-моему, просто годится. MMF — это не контролы от QT и не маршрутизация сообщений, где лезть на уровень ниже надо с большой осторожнотью. Для MMF все эти классы — лишь некая простая обертка над API, так что что ее использовать, что API — примерно одинаково по трудоемкости. Даже как бы не наоборот — сколько ты времени потратил на копание в этом классе и поиск этого бага ? Писал бы на чистом API — скорее всего, этого постинга вообще бы не было.
With best regards
Pavel Dvorkin
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.