Question on memory mapping under windows 2000, SP4. This is more of a generic programming question, but I am doing the development in MASM to try and learn a thing or two :)

I'm attempting to use a memory mapped file as IPC between a windows service (running ring3 under local system account) and a client interface (also ring3, from a normal unprivileged "user" account).

From the client side, I've been able to create a memory map with a well-known known name successfully, however using the same name, the service cannot seem to open it. When I make the call to OpenFileMapping from the windows service, it fails. If I try opening it from the service using CreateFileMapping, and test for ERROR_ALREADY_EXISTS, I see it's creating a new file map.

Is file mapping limited to "in-session" processes? Assuming the windows service is running in its own session, that would explain the failure.
Or perhaps is this a security issue I have to somehow grant access to the memory map by modifying some kind of a process security descriptor?

Maybe I should be using a different form of IPC?? :)


------- client code
	.if reason==DLL_PROCESS_ATTACH
invoke CreateFileMapping, INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1, offset mmapOutName? ; whopping 1 byte map
.IF eax == NULL
invoke GetLastError
mov ebx, eax
invoke FormatMessage, FORMAT_MESSAGE_FROM_SYSTEM+FORMAT_MESSAGE_MAX_WIDTH_MASK, \
NULL, ebx, NULL, addr tempBuf, SIZEOF tempBuf, NULL
invoke MessageBox, 0, addr tempBuf, $CTA0("CreateFileMapping Error"), 0
.ELSE
mov hMemoryMap, eax

invoke GetLastError
.IF eax==ERROR_ALREADY_EXISTS
invoke MessageBox,0,$CTA0("FYI: a memory map of same name already exists"),0,0
.ELSE
invoke MessageBox,0,$CTA0("FYI: a memory map doesnt exist with this name"),0,0
.ENDIF

invoke MapViewOfFile, hMemoryMap, FILE_MAP_WRITE, 0, 0, 0 ; map whole thing into the view window, starting at 0
.IF eax!= NULL
mov pOutgoingMessageMapView, eax
invoke MessageBox, 0, $CTA0("map successful"), 0, 0
.ELSE
invoke MessageBox, 0, $CTA0("cant map view of file"), 0, 0
.ENDIF

.ENDIF
.elseif reason==DLL_PROCESS_DETACH
invoke UnmapViewOfFile, pOutgoingMessageMapView
invoke CloseHandle, hMemoryMap
.endif




----------------- server code:
	;invoke OpenFileMapping, FILE_MAP_READ, 0, addr mmapOutName? ? ? ? ; tried this, but failed
invoke CreateFileMapping, INVALID_HANDLE_VALUE, NULL, PAGE_READ, 0, 1, addr mmapOutName? ? ; seems to create new map... mmapOutName is same!
.IF eax == NULL
invoke OutputDebugString, $CTA0("OpenFileMapping failure")
.ELSE
mov hMmapFromFromPlugins, eax
invoke GetLastError
.IF eax==ERROR_ALREADY_EXISTS
invoke OutputDebugString, $CTA0("Existing memory map used")
.ELSE
invoke OutputDebugString, $CTA0("New memory map used")
.ENDIF
.ENDIF

invoke MapViewOfFile, hMmapFromFromPlugins, FILE_MAP_READ, 0, 0, 0 ; map the whole thing, starting from 0
.IF eax == NULL
invoke OutputDebugString, $CTA0("Can't map view of mmap for plugin")
.ELSE
mov pMsgFromPluginsBuf, eax
.ENDIF



Sorry if this is an easy question, but I wan't able to find anything searching the forums, or via google :p

Thanks,

Jerome
Posted on 2005-07-08 15:27:45 by jackal651
Hmmm, are you creating the filemapping in the client, then trying to access it from the server? It should be the other way around. And you probably need to play with security descriptors, since you want communication between privileged and unprivileged processes.

I would use sockets or named pipes instead, since they're properly synchronized, and probably a bit easier to deal with correctly.
Posted on 2005-07-08 15:57:27 by f0dder
Thanks for the quick reply f0dder!

I did try creating the memory map from the service first, but then the client ends up creating its own memory map as well. So it doesn't seem like it's seeing across the "current user" and "local system" windows sessions. Humm.

I've also tried experimenting with prefixing the memory map name with "Global\" and "Local\" but it didn't seem to change the behaviour.

Your advice for looking into using sockets will probably be tried next, because it seems that using memory mapping in this way is a dead end. The point of this project is to build a client-server interface that lets a user do some "empowered" tasks as a non-administrator, such as add printers, etc. I've already been able to do this with scripts, but coding a more "elegant" solution is not as easy as I'd hoped!

damn memory mapping!? ;)

-Jerome

Posted on 2005-07-08 16:28:55 by jackal651
Well, it ought to be possible to create a filemap in the service (probably requiring some playing around with lpAttributes), and then use OpenFileMapping in the client... I almost feel like playing around a bit myself, because it's weird if it's not possible :)

But well, consider sockets or pipes - detecting when a command has been "sent", and proper synchronization, would be somewhat hard with filemapping.

Perhaps using mailslots is an even better option? I've never played with them myself, but the advantage seems to be that, client-side, mailslots work even on 9x systems.
Posted on 2005-07-08 16:39:31 by f0dder
what is the error when you try to OpenFileMapping?

I have successfully used memory mapped files in conjunction with events for inter-process communication, but both processes were running under the same user. Maybe you really do have to adjust security tokens on the object
Posted on 2005-07-08 16:42:09 by comrade
When creating the memory map from the client with readwrite, then trying openfilemapping as read only from the service, FormatMessage returns a "The system cannot find the file specified" error.

It's like it doesn't even see the memory map exists from within the service. I dont think it would have anything to do with the fact that the client-side code memory map is being created from from a loaded dll... since it shares the same heap space as my main client program, it should make no difference if it's in a dll or not.

I'll have to play with this more next week... out of time and it's Friday. Thanks for the dialog on this. It's good to keep the old brain moving with a fresh perspective!

-Jerome
Posted on 2005-07-08 17:23:42 by jackal651
Jack,

With memory mapped files set as a memory block from the system paging file, you actually write duplicate code in both server and client apps as the memory mapped file system allows for this. You must make sure the naming is the same between both apps so that both can read and/or write to the memory.

As far as triggering a response from the other ap, either client or server, you have a number of options, there is the techniques that f0dder mentioned and you can also creeate and send private messages using the HWND_BROADCAST handle so that the other app can respond to it.

A memory mapped file is probably faster for data transfer but you would need to experiment to see which communication technique best suited your task when it comes to triggering a response from either app that uses the memory mapped file.
Posted on 2005-07-08 21:28:30 by hutch--
A memory mapped file would be a good approach if you need to communicate a lot of data between client/server, as you avoid a lot of copying-around overhead. I would still use sockets, pipes or perhaps events in the MMF memory (that's what you did, comrade?) to do the sync communication, since HWND_BROADCAST sends traffic to all windows, and the other methods are more "focused".

I'll play around a bit with this stuff later today when the gf leaves, and see what I can come up with.
Posted on 2005-07-09 07:32:51 by f0dder
Interesting update:

I wrote simple client/server exes to open a memory mapped file. I run the server first to create the section as READWRITE. I leave it running with the success messagebox open, then run the client with OpenFileMapping for WRITE access - success!

However if I recompile the client to open it with READ-ONLY access - failure with "Access is Denied."

The reason for this is that I was aiming to create two separate memory maps for IPC. Only the creating process has write access, and any others read... like a one-way conduit.

I can't imagine the security descriptor for the memory map has to be changed if I can successfully open with write access.

Maybe it's not possible to open a memory map as read-only??

Code is attached if any are interested to try this themselves!


Server Code:
.386
.model flat,stdcall
option casemap:none

include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\macros\Strings.mac
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

.data
tempBuf BYTE 1024 dup(?)
hMemoryMap HANDLE ?
pOutgoingMessageMapView DWORD ?
blahblah BYTE "BLAHBLAH",0

.code
start:
invoke CreateFileMapping, INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 1, offset blahblah
.IF eax == NULL
invoke GetLastError
invoke FormatMessage, FORMAT_MESSAGE_FROM_SYSTEM+FORMAT_MESSAGE_MAX_WIDTH_MASK, \
NULL, eax, NULL, offset tempBuf, SIZEOF tempBuf, NULL
invoke MessageBox, 0, offset tempBuf, $CTA0("CreateFileMapping Error"), 0
.ELSE
mov hMemoryMap, eax
invoke GetLastError
.IF eax==ERROR_ALREADY_EXISTS
invoke MessageBox,0,$CTA0("FYI: a memory map of same name already exists"),0,0
.ELSE
invoke MessageBox,0,$CTA0("FYI: a memory map doesnt exist with this name"),0,0
.ENDIF
invoke MapViewOfFile, hMemoryMap, FILE_MAP_WRITE, 0, 0, 0 ; map whole thing into the view window, starting at 0
.IF eax!= NULL
mov pOutgoingMessageMapView, eax
invoke MessageBox, 0, $CTA0("map is open, start client"), 0, 0
.ELSE
invoke MessageBox, 0, $CTA0("cant map view of file"), 0, 0
.ENDIF
.ENDIF

invoke UnmapViewOfFile, pOutgoingMessageMapView
invoke CloseHandle, hMemoryMap

invoke ExitProcess, 0
end start


Client Code:
.386
.model flat,stdcall
option casemap:none
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\user32.inc
include \masm32\macros\Strings.mac
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\user32.lib

.data
tempBuf BYTE 1024 dup(?)
hMemoryMap HANDLE ?
pIncomingMessageMapView DWORD ?
blahblah BYTE "BLAHBLAH",0

.code
start:
invoke OpenFileMapping, FILE_MAP_WRITE, FALSE, offset blahblah ; works ok. equ to 02h.
;invoke OpenFileMapping, FILE_MAP_READ, FALSE, offset blahblah ; read only doesn't work? equ to 04h.
.IF eax == NULL
invoke GetLastError
invoke FormatMessage, FORMAT_MESSAGE_FROM_SYSTEM+FORMAT_MESSAGE_MAX_WIDTH_MASK, \
NULL, eax, NULL, offset tempBuf, SIZEOF tempBuf, NULL
invoke MessageBox, 0, offset tempBuf, $CTA0("CreateFileMapping Error"), 0
.ELSE
mov hMemoryMap, eax
invoke MapViewOfFile, hMemoryMap, FILE_MAP_WRITE, 0, 0, 0 ; map whole thing into the view window, starting at 0
.IF eax!= NULL
mov pIncomingMessageMapView, eax
invoke MessageBox, 0, $CTA0("map successful"), 0, 0
.ELSE
invoke GetLastError
invoke FormatMessage, FORMAT_MESSAGE_FROM_SYSTEM+FORMAT_MESSAGE_MAX_WIDTH_MASK, \
NULL, eax, NULL, offset tempBuf, SIZEOF tempBuf, NULL
invoke MessageBox, 0, offset tempBuf, $CTA0("cant map view of file"), 0
.ENDIF
.ENDIF
invoke UnmapViewOfFile, pIncomingMessageMapView
invoke CloseHandle, hMemoryMap
invoke ExitProcess, 0
end start
Posted on 2005-07-11 12:31:33 by jackal651
How weird :-s
Posted on 2005-07-11 12:40:40 by f0dder