进程间MemoryMappedFile同步异常?C#与VBA跨语言IPC技术问询
Great question—memory mapped files (MMFs) are awesome for high-speed IPC, but they don’t come with built-in synchronization out of the box. That’s exactly why you’re seeing inconsistencies between your C# and VBA processes. Let’s break this down and fix it.
MMFs just provide a shared block of memory between processes—they don’t enforce any rules around who can read/write when. If your C# process is writing to the MMF at the same time your VBA process is reading from it, you’ll get partial or corrupted data (race conditions). Without a way to "lock" access to the shared memory, you can’t guarantee consistency.
The solution is to use Windows kernel-level synchronization objects (like a Mutex) alongside your MMF. Both C# and VBA can interact with these primitives via P/Invoke (or .NET’s built-in classes for C#), ensuring only one process accesses the shared memory at a time.
C# Implementation With Mutex
In C#, you can use the built-in Mutex class to wrap your MMF operations:
// Create or open a named mutex to sync access to the MMF using (var syncMutex = new Mutex(false, "TestMap_SyncMutex")) using (var mmf = MemoryMappedFile.CreateOrOpen("TestMap", 10, MemoryMappedFileAccess.ReadWrite)) { var viewAccessor = mmf.CreateViewAccessor(); // Wait to acquire the mutex (blocks until it's available) syncMutex.WaitOne(); try { // SAFE: Only one process can execute this code at a time // Example write operation viewAccessor.Write(0, (int)12345); // Example read operation int readValue = viewAccessor.ReadInt32(0); Console.WriteLine($"C# Read: {readValue}"); } finally { // Always release the mutex, even if an error occurs syncMutex.ReleaseMutex(); } }
VBA Implementation With Mutex
In VBA, you’ll need to declare the necessary kernel32 functions via P/Invoke to work with the same mutex:
First, add these declarations at the top of your module (use PtrSafe for 64-bit Office):
' Mutex-related kernel32 declarations Declare PtrSafe Function CreateMutex Lib "kernel32" Alias "CreateMutexA" ( _ ByRef lpMutexAttributes As Any, _ ByVal bInitialOwner As Long, _ ByVal lpName As String) As LongPtr Declare PtrSafe Function WaitForSingleObject Lib "kernel32" ( _ ByVal hHandle As LongPtr, _ ByVal dwMilliseconds As Long) As Long Declare PtrSafe Function ReleaseMutex Lib "kernel32" ( _ ByVal hHandle As LongPtr) As Long Declare PtrSafe Function CloseHandle Lib "kernel32" ( _ ByVal hObject As LongPtr) As Long ' MMF-related declarations (you probably already have these) Declare PtrSafe Function OpenFileMapping Lib "kernel32" Alias "OpenFileMappingA" ( _ ByVal dwDesiredAccess As Long, _ ByVal bInheritHandle As Long, _ ByVal lpName As String) As LongPtr Declare PtrSafe Function MapViewOfFile Lib "kernel32" ( _ ByVal hFileMappingObject As LongPtr, _ ByVal dwDesiredAccess As Long, _ ByVal dwFileOffsetHigh As Long, _ ByVal dwFileOffsetLow As Long, _ ByVal dwNumberOfBytesToMap As Long) As LongPtr Declare PtrSafe Sub CopyMemory Lib "kernel32" Alias "RtlMoveMemory" ( _ ByRef Destination As Any, _ ByRef Source As Any, _ ByVal Length As Long) ' Constants Const INFINITE = &HFFFFFFFF Const WAIT_OBJECT_0 = 0 Const GENERIC_READ = &H80000000 Const GENERIC_WRITE = &H40000000 Const FILE_MAP_WRITE = &H2
Then use the mutex to wrap your MMF access:
Sub AccessMMFSafely() Dim hMutex As LongPtr Dim hFileMap As LongPtr Dim hMMView As LongPtr Dim readValue As Long ' Open or create the same mutex used in C# hMutex = CreateMutex(ByVal 0&, False, "TestMap_SyncMutex") If hMutex = 0 Then MsgBox "Failed to initialize synchronization mutex", vbCritical Exit Sub End If ' Wait to acquire the mutex (blocks until available) If WaitForSingleObject(hMutex, INFINITE) <> WAIT_OBJECT_0 Then MsgBox "Could not acquire mutex", vbCritical CloseHandle hMutex Exit Sub End If ' Now safely access the MMF On Error GoTo Cleanup hFileMap = OpenFileMapping(GENERIC_READ Or GENERIC_WRITE, 0, "TestMap") If hFileMap = 0 Then MsgBox "Failed to open MMF", vbCritical GoTo Cleanup End If hMMView = MapViewOfFile(hFileMap, FILE_MAP_WRITE, 0, 0, 0) If hMMView = 0 Then MsgBox "Failed to map MMF view", vbCritical GoTo Cleanup End If ' Example read operation CopyMemory readValue, ByVal hMMView, 4 MsgBox "VBA Read: " & readValue Cleanup: ' Always release the mutex to avoid deadlocks ReleaseMutex hMutex ' Clean up MMF handles If hMMView <> 0 Then CloseHandle hMMView If hFileMap <> 0 Then CloseHandle hFileMap ' Close mutex handle CloseHandle hMutex End Sub
- Match Names: Make sure the mutex name (e.g.,
TestMap_SyncMutex) is identical in both C# and VBA—this is how the processes find the same synchronization object. - Always Release: Use
finally(C#) or error handling (VBA) to ensure the mutex is released even if an error occurs. A stuck mutex will cause deadlocks. - Choose the Right Primitive: If you need more complex sync (e.g., signaling when data is ready), use an
Eventinstead of a Mutex. The pattern is similar—just useCreateEvent,SetEvent, andWaitForSingleObject. - Office Bitness: For VBA, ensure your declarations use
PtrSafeandLongPtrif you’re running 64-bit Office. For 32-bit, you can useLonginstead ofLongPtr.
内容的提问来源于stack exchange,提问作者Bogey

