Использование 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.
}