Как сделать Memory-Mapped-File, созданный в Windows-Service, видимым для других программ?(С #) - PullRequest
0 голосов
/ 04 февраля 2019

Использование C # и .NET 4.0 Я пытаюсь использовать MMF (Memory-Mapped-File) между программой и службой Windows.Сначала я проверил свой код между двумя обычными программами для настольных компьютеров, и он отлично работает.Но когда одна из программ («получатель») является службой Windows, другая программа не может открыть MMF, созданную службой Windows.Почему?

Я обнаружил, что начиная с Windows 7 службы Windows выполняются в сеансе 0, а программы для настольных компьютеров - в других сеансах.Таким образом, чтобы эти объекты, такие как MMF и мьютексы, были видны друг другу, вы должны дать им имя, которое начинается с префикса «Global \».Это работало для объекта Mutex, но до сих пор не было (для меня) для MMF.Я застрял!

Ниже приведен соответствующий код, но проблема действительно может быть просто сформулирована: как программа для настольных компьютеров «видит» MMF, создаваемую службой Windows.Большая часть следующего кода является просто обработкой протокола для службы журналов, которую я пытаюсь создать.

Любая помощь или совет здесь очень ценятся - спасибо.

Вот имена, которые яИспользую:

const string MmfName = @"Global\LognutServiceIpcMmf1";
const string MutexNameLockMmf = @"Global\LognutSvcMutexLockMmf";
const string EventNameLogAvail = @"Global\LognutSvcEventLogAvail";
const string EventNameResponding = @"Global\LognutSvcEventResponding";

Вот программа (Windows Service), которая изначально создает MMF, с некоторыми пропущенными регистрациями и обработкой безопасности:

void ReceiveStuff()
{
    using (MemoryMappedFile mmf = MemoryMappedFile.CreateOrOpen( MmfProtocol.MmfName, 1024, MemoryMappedFileAccess.ReadWriteExecute ))
    {
        using (MemoryMappedViewStream mmvStream = mmf.CreateViewStream( 0, 1024, MemoryMappedFileAccess.Read ))
        {
            using (Mutex mutexLockMmf = new Mutex( initiallyOwned: false, name: MutexNameLockMmf, createdNew: out bool isMutexCreated ))
            {
                Say( "mutexSvcReady begot. isMutexCreated = " + isMutexCreated );
                // The initial state of eventwhLogAvail is to be nonsignaled, such that this thread will block upon calling WaitOne.
                using (EventWaitHandle eventwhLogAvail = new EventWaitHandle( initialState: false, mode: EventResetMode.AutoReset, name: EventNameLogAvail, createdNew: out bool isCreatedNewEventwhLogAvail ))
                {
                    Say( "isCreatedNewEventwhLogAvail = " + isCreatedNewEventwhLogAvail );
                    // The initial state of eventwhLogAvail is to be non-signaled, such that the Xmt thread will block upon calling WaitOne.
                    using (EventWaitHandle eventwhResponding = new EventWaitHandle( initialState: false, mode: EventResetMode.AutoReset, name: EventNameResponding, createdNew: out bool isCreatedNewEventwhResponding ))
                    {
                        // Keep checking for the existence of the mutexLogAvail...
                        // Until that exists, there are no logs incoming.
                        while (!_isClosing)
                        {
                            // Wait until someone has sent us logs (which they will signal to us by opening this semaphore and calling Release..
                            // We have to include a timeout, so that we can come out periodically to check for cancellation.

                            // Wait on eventwhLogAvail, looping periodically only to check on cancellation requests.
                            bool isSignalReceived = eventwhLogAvail.WaitOne(timeout: TimeSpan.FromSeconds( 5 ));
                            if (isSignalReceived)
                            {
                                Say( "eventwhLogAvail acquired. Log message is avail." );

                                //  When eventwhLogAvail unblocks, set eventwhResponding to signal Xmt that I have got it.
                                //  Because this is AutoReset, it immediately Resets (goes back to blocking) as soon as Xmt proceeds through it.
                                eventwhResponding.Set();

                                // Wait on mutexLockMmf
                                mutexLockMmf.WaitOne();
                                Say( "mutexLockMmf acquired" );

                                // Now that mutexLockMmf unblocked, get the log from the MMF.
                                IpcMessage message1;
                                // Use protobuf-net to deserialize the buffer bytes.
                                message1 = Serializer.DeserializeWithLengthPrefix<IpcMessage>( mmvStream, style: PrefixStyle.Base128 );
                                ProcessReceivedMessage( message1 );

                                // Now that we are done with the MMF, release ownership of the Mutex to indicate that Rcv may add new logs to the MMF now.
                                mutexLockMmf.ReleaseMutex();
                            }
                            else // timed-out. No logs avail yet.
                            {
                                Say( "Timed-out waiting for eventwhLogAvail." );
                            }
                        }
                    }
                }
            }
        }
    }
}

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

void SendStuff()
{
    // Create the memory-mapped file which allows 'Reading' and 'Writing'
    using (MemoryMappedFile mmf = MemoryMappedFile.OpenExisting( mapName: MmfName, desiredAccessRights: MemoryMappedFileRights.Read ))
    {
        // Create a view-stream for this process, which allows us to write data from offset 0 to 1024 (whole memory)
        using (MemoryMappedViewStream mmvStream = mmf.CreateViewStream( offset: 0, size: 1024 ))
        {
            EventWaitHandle eventwhLogAvail = EventWaitHandle.OpenExisting( name: EventNameLogAvail );
            EventWaitHandle eventwhResponding = EventWaitHandle.OpenExisting( name: EventNameResponding );

            // Loop waiting for the Mutex to appear...
            while (!_isClosing)
            {
                Mutex mutexLockMmf = null;
                bool doesMutexExist = false;

                // Attempt to open the Mutex..
                try
                {
                    mutexLockMmf = Mutex.OpenExisting( name: MutexNameLockMmf );
                    doesMutexExist = true;
                    Logger.LogTrace( "I see the mutexLockMmf now!" );
                }
                catch (WaitHandleCannotBeOpenedException x)
                {
                    Logger.LogTrace( "x.Message is " + x.Message );
                    doesMutexExist = false;
                }
                catch (UnauthorizedAccessException x)
                {
                    Logger.LogTrace( "mutexLockMmf - Unauthorized access: " + x.Message );
                    doesMutexExist = false;
                }

                if (doesMutexExist)
                {
                    // Since this Mutex already exists, that indicates the LognutWindowsService is running.
                    bool doWeHaveMutex = false;
                    // Wait for the LogSvc to become ready..
                    // If the LognutWindowsService is not up, then this will return immediately.
                    try
                    {
                        // I need to wait upon mutexLockMmf to know that the previous message has been processed.
                        doWeHaveMutex = mutexLockMmf.WaitOne( timeout: TimeSpan.FromSeconds( 5 ) );
                    }
                    catch (AbandonedMutexException x)
                    {
                        Logger.LogTrace( "Abandoned Mutex: " + x.Message );
                        break;
                    }

                    if (doWeHaveMutex)
                    {
                        Logger.LogTrace( "Acquired mutex. Sending message..." );

                        // this is what we want to write to the memory mapped file
                        IpcMessage message1 = new IpcMessage();
                        message1.Id = 69;
                        message1.Content = "hello world";

                        // Use protobuf-net to serialize the variable 'message1' and write it to the memory mapped file.
                        Serializer.SerializeWithLengthPrefix( destination: mmvStream, instance: message1, style: PrefixStyle.Base128 );

                        // Release the eventwhLogAvail that allows Rcv to proceed to process it's log
                        // Since this is an AutoReset event, it Resets after the Rcv thread has proceeded through.
                        eventwhLogAvail.Set();

                        // Wait for eventwhResponding signal from Rcv that tells me it is ready for me to block
                        // Because I have not released mutexLockMmf yet, I know that Xmt cannot speed around the re-process the MMF too soon.
                        eventwhResponding.WaitOne();

                        // Release mutexLockMmf (the mutex that protects the MMF) so that now Xmt may do it's work.
                        mutexLockMmf.ReleaseMutex();
                    }
                    else // don't have the Mutex
                    {
                        Logger.LogTrace( "mutex timeout." );
                    }
                }
                else // Mutex does not exist yet.
                {
                    Thread.Sleep( 5000 );
                }
            } // end while loop.
        } // end using mmvStream.
    } // end using mmf.
}
...