SHAllocShared fixes

Francois Gouget fgouget at free.fr
Mon Aug 30 13:36:37 CDT 2004


So I finally did write a conformance test for the SHAllocShared shell32
APIs. Compiling and running it on Windows shows that the definitions in
the Platform SDK's shlobj.h have the wrong call convention. These APIs
are truely stdcall APIs as they were defined in Wine.

So I left the function definitions in undocshell.h since I don't see how
the ones in the Platform SDK's could be used anyway.

Running the conformance test on Wine showed major bugs in our
implementation. For instance SHFreeShared() did:

   return (BOOL)GlobalFree(hMem);

But GlobalFree() returns 0 when successful (one must check
GetLastError()), while SHFreeShared() is supposed to return TRUE!

Similarly SHUnlockShared() passed a pointer to the memory region to
GlobalUnlock() which expects that memory region's handle! I fixed this
particular issue by storing the memory region handle in the first 4
bytes of the allocated memory. This actually very much looks like what
Windows is doing: on Windows an SHLockShared() call will return a
pointer which is on a 64KB boundary + 4 bytes.

This actually calls for two more notes:
 * This looks very much what one would get by calling the corresponding
functions in shlwapi (which seems much more complete). However in
shell32 there is talk of sending messages around (WM_USER+2) so maybe
it's the same concept but a different implementation?
 * Our SHAllocShared documentation also talks about a pidl being put in
the allocated memory. I really don't see where that pidl would be
stored.

Finally, as far as I can see (greping the dumpbin logs of Windows
versions from 95 to XP) these APIs are not exported by name. So I
removed the name export from shell32.spec.


Changelog:

 * dlls/shell32/undocshell.h
   dlls/shell32/shell32.spec
   dlls/shell32/shellord.c
   dlls/shell32/tests/Makefile.in
   dlls/shell32/tests/alloc.c

   Don't export the SHAllocShared functions by name.
   Add a conformance test.
   Fix their implementation and add some documentation.


-- 
Francois Gouget         fgouget at free.fr        http://fgouget.free.fr/
                          "Utilisateur" (nom commun) :
       Mot utilis\xE9 par les informaticiens en lieu et place d'"idiot".
-------------- next part --------------
Index: dlls/shell32/undocshell.h
===================================================================
RCS file: /var/cvs/wine/dlls/shell32/undocshell.h,v
retrieving revision 1.25
diff -u -r1.25 undocshell.h
--- dlls/shell32/undocshell.h	22 Aug 2004 22:27:31 -0000	1.25
+++ dlls/shell32/undocshell.h	30 Aug 2004 14:35:57 -0000
@@ -154,6 +154,11 @@
  * Memory Routines
  */
 
+/* The Platform SDK's shlobj.h header defines similar functions with a
+ * leading underscore. However those are unusable because of the leading
+ * underscore, because they have an incorrect calling convention, and
+ * because these functions are not exported by name anyway.
+ */
 HANDLE WINAPI SHAllocShared(
 	LPVOID pv,
 	ULONG cb,
Index: dlls/shell32/shell32.spec
===================================================================
RCS file: /var/cvs/wine/dlls/shell32/shell32.spec,v
retrieving revision 1.85
diff -u -r1.85 shell32.spec
--- dlls/shell32/shell32.spec	9 Jul 2004 22:51:19 -0000	1.85
+++ dlls/shell32/shell32.spec	30 Aug 2004 14:25:14 -0000
@@ -278,10 +278,10 @@
  511 stdcall SHRegQueryValueExW (long wstr ptr ptr ptr ptr)
  512 stdcall SHRegDeleteKeyW (long wstr)
 
- 520 stdcall SHAllocShared (long long long)
- 521 stdcall SHLockShared (long long)
- 522 stdcall SHUnlockShared (long)
- 523 stdcall SHFreeShared (long long)
+ 520 stdcall @(long long long) SHAllocShared
+ 521 stdcall @(long long) SHLockShared
+ 522 stdcall @(long) SHUnlockShared
+ 523 stdcall @(long long) SHFreeShared
  524 stdcall RealDriveType (long long)
  525 stub RealDriveTypeFlags
 
Index: dlls/shell32/shellord.c
===================================================================
RCS file: /var/cvs/wine/dlls/shell32/shellord.c,v
retrieving revision 1.124
diff -u -r1.124 shellord.c
--- dlls/shell32/shellord.c	22 Aug 2004 22:27:31 -0000	1.124
+++ dlls/shell32/shellord.c	30 Aug 2004 17:56:08 -0000
@@ -1187,72 +1187,125 @@
 }
 
 /*************************************************************************
- * SHAllocShared				[SHELL32.520]
+ * @ [SHELL32.520]
+ *
+ * PARAMS
+ *  psrc   [I] Block of memory to be copied to the shared memory area.
+ *             If NULL, then no data is copied
+ *  size   [I] Size of the shared memory block to allocate
+ *  procID [I] ID of the process with which to share the memory
+ *
+ * RETURNS
+ *  Success: A handle to the block of shared memory
+ *  Failure: NULL
+ *
+ * FIXME
+ *  We don't actually handle sharing the memory with another process
  *
  * NOTES
- *  parameter1 is return value from HeapAlloc
- *  parameter2 is equal to the size allocated with HeapAlloc
- *  parameter3 is return value from GetCurrentProcessId
- *
- *  the return value is posted as lParam with 0x402 (WM_USER+2) to somewhere
- *  WM_USER+2 could be the undocumented CWM_SETPATH
- *  the allocated memory contains a pidl
+ *  SHAllocShared may be identical to shlwapi.SHAllocShared.
+ *  The return value is posted as lParam with 0x402 (WM_USER+2) to
+ *  somewhere WM_USER+2 could be the undocumented CWM_SETPATH.
+ *  The allocated memory contains a pidl (???).
  */
 HGLOBAL WINAPI SHAllocShared(LPVOID psrc, DWORD size, DWORD procID)
-{	HGLOBAL hmem;
-	LPVOID pmem;
-
-	TRACE("ptr=%p size=0x%04lx procID=0x%04lx\n",psrc,size,procID);
-	hmem = GlobalAlloc(GMEM_FIXED, size);
-	if (!hmem)
-	  return 0;
+{
+    HGLOBAL hmem;
+    LPVOID pmem;
 
-	pmem =  GlobalLock (hmem);
+    TRACE("ptr=%p size=0x%04lx procID=0x%04lx\n",psrc,size,procID);
+    if (procID!=GetCurrentProcessId())
+        FIXME("procID is not the current process\n");
+    hmem = GlobalAlloc(GMEM_FIXED, size+4);
+    if (!hmem)
+        return 0;
 
-	if (! pmem)
-	  return 0;
+    pmem =  GlobalLock(hmem);
+    if (!pmem)
+    {
+        GlobalFree(hmem);
+        return 0;
+    }
 
-	memcpy (pmem, psrc, size);
-	GlobalUnlock(hmem);
-	return hmem;
-}
-/*************************************************************************
- * SHLockShared					[SHELL32.521]
- *
+    *((HANDLE*)pmem)=hmem;
+    pmem=(char*)pmem+4;
+    if (psrc)
+        memcpy(pmem, psrc, size);
+    GlobalUnlock(hmem);
+    TRACE("handle=%p\n",hmem);
+    return hmem;
+}
+/*************************************************************************
+ * @ [SHELL32.521]
+ *
+ * PARAMS
+ *  hmem   [I] A handle returned by SHAllocShared
+ *  procID [I] ID of the process with which to share the memory
+ *
+ * RETURNS
+ *  Success: A pointer to the block of memory
+ *  Failure: NULL
+ *
+ * FIXME
+ *  We don't actually handle sharing the memory with another process
+ *  
  * NOTES
- *  parameter1 is return value from SHAllocShared
- *  parameter2 is return value from GetCurrentProcessId
- *  the receiver of (WM_USER+2) tries to lock the HANDLE (?)
- *  the return value seems to be a memory address
+ *  The receiver of (WM_USER+2) tries to lock the HANDLE (?)
  */
 LPVOID WINAPI SHLockShared(HANDLE hmem, DWORD procID)
-{	TRACE("handle=%p procID=0x%04lx\n",hmem,procID);
-	return GlobalLock(hmem);
+{
+    void* pmem;
+    TRACE("handle=%p procID=0x%04lx\n",hmem,procID);
+    if (procID!=GetCurrentProcessId())
+        FIXME("procID is not the current process\n");
+    pmem=GlobalLock(hmem);
+    if (pmem)
+        pmem=(char*)pmem+4;
+    return pmem;
 }
 /*************************************************************************
- * SHUnlockShared				[SHELL32.522]
+ * @ [SHELL32.522]
  *
- * NOTES
- *  parameter1 is return value from SHLockShared
+ * PARAMS
+ *  pmem [I] A pointer to the shared memory returned by SHLockShared
+ *
+ * RETURNS
+ *  Success: TRUE
+ *  Failure: FALSE
+ *
+ * FIXME
+ *  We don't actually handle sharing the memory with another process
  */
-BOOL WINAPI SHUnlockShared(LPVOID pv)
+BOOL WINAPI SHUnlockShared(LPVOID pmem)
 {
-	TRACE("%p\n",pv);
-	return GlobalUnlock((HANDLE)pv);
+    HANDLE hmem;
+    BOOL rc;
+
+    TRACE("%p\n",pmem);
+    pmem=(char*)pmem-4;
+    hmem=*((HANDLE*)pmem);
+    TRACE("handle=%p\n",hmem);
+    rc=GlobalUnlock(hmem);
+    return (!rc && GetLastError()==NO_ERROR);
 }
 /*************************************************************************
- * SHFreeShared					[SHELL32.523]
+ * @ [SHELL32.523]
  *
- * NOTES
- *  parameter1 is return value from SHAllocShared
- *  parameter2 is return value from GetCurrentProcessId
+ * PARAMS
+ *  hmem   [I] A handle returned by SHAllocShared
+ *  procID [I} ID of the process with which to share the memory
+ *
+ * RETURNS
+ *  Success: TRUE
+ *  Failure: FALSE
+ *
+ * FIXME
+ *  We don't actually handle sharing the memory with another process
  */
-BOOL WINAPI SHFreeShared(
-	HANDLE hMem,
-	DWORD pid)
+BOOL WINAPI SHFreeShared(HANDLE hmem, DWORD pid)
 {
-	TRACE("handle=%p 0x%04lx\n",hMem,pid);
-	return (BOOL)GlobalFree(hMem);
+    TRACE("handle=%p 0x%04lx\n",hmem,pid);
+    return !GlobalFree(hmem);
 }
 
 /*************************************************************************
Index: dlls/shell32/tests/Makefile.in
===================================================================
RCS file: /var/cvs/wine/dlls/shell32/tests/Makefile.in,v
retrieving revision 1.3
diff -u -r1.3 Makefile.in
--- dlls/shell32/tests/Makefile.in	10 Feb 2004 02:19:03 -0000	1.3
+++ dlls/shell32/tests/Makefile.in	30 Aug 2004 12:43:32 -0000
@@ -6,6 +6,7 @@
 IMPORTS   = shell32 ole32
 
 CTESTS = \
+	alloc.c \
 	generated.c \
 	shlfileop.c \
 	string.c
--- /dev/null	2004-08-10 11:44:31.000000000 +0200
+++ dlls/shell32/tests/alloc.c	2004-08-30 16:25:46.000000000 +0200
@@ -0,0 +1,74 @@
+/*
+ * Unit test of the SHFileOperation function.
+ *
+ * Copyright 2004 Francois Gouget
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+ 
+#include "windef.h"
+#include "winbase.h"
+
+#include "wine/test.h"
+
+HANDLE       (WINAPI *pSHAllocShared)(LPVOID,ULONG,DWORD);
+LPVOID       (WINAPI *pSHLockShared)(HANDLE,DWORD);
+BOOL         (WINAPI *pSHUnlockShared)(LPVOID);
+BOOL         (WINAPI *pSHFreeShared)(HANDLE,DWORD);
+
+
+void test_alloc_shared()
+{
+    DWORD procid;
+    HANDLE hmem;
+    int val;
+    int* p;
+
+    procid=GetCurrentProcessId();
+    hmem=pSHAllocShared(NULL,10,procid);
+    ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %ld\n", GetLastError());
+    ok(pSHFreeShared(hmem, procid),
+       "SHFreeShared failed: %ld\n", GetLastError());
+
+    val=0x12345678;
+    hmem=pSHAllocShared(&val,4,procid);
+    ok(hmem!=NULL,"SHAllocShared(NULL...) failed: %ld\n", GetLastError());
+
+    p=(int*)pSHLockShared(hmem,procid);
+    ok(p!=NULL,"SHLockShared failed: %ld\n", GetLastError());
+    if (p!=NULL)
+        ok(*p==val,"Wrong value in shared memory: %d instead of %d\n",*p,val);
+    ok(pSHUnlockShared(p),"SHUnlockShared failed: %ld\n", GetLastError());
+
+    ok(pSHFreeShared(hmem, procid),
+       "SHFreeShared failed: %ld\n", GetLastError());
+}
+
+START_TEST(alloc)
+{
+    HMODULE hdll;
+
+    /* These functions are not exported by name */
+    hdll=GetModuleHandle("shell32");
+    pSHAllocShared=(void*)GetProcAddress(hdll,(char*)520);
+    pSHLockShared=(void*)GetProcAddress(hdll,(char*)521);
+    pSHUnlockShared=(void*)GetProcAddress(hdll,(char*)522);
+    pSHFreeShared=(void*)GetProcAddress(hdll,(char*)523);
+
+    test_alloc_shared();
+}


More information about the wine-patches mailing list