Implement and test ScCopyProps/ScRelocProps.
diff --git a/dlls/mapi32/mapi32.spec b/dlls/mapi32/mapi32.spec
index 8b606a6..28d70fb 100644
--- a/dlls/mapi32/mapi32.spec
+++ b/dlls/mapi32/mapi32.spec
@@ -108,8 +108,8 @@
165 stub ScCopyNotifications@16
166 stub ScRelocNotifications@20
170 stdcall ScCountProps@12(long ptr ptr) ScCountProps
-171 stub ScCopyProps@16
-172 stub ScRelocProps@20
+171 stdcall ScCopyProps@16(long ptr ptr ptr) ScCopyProps
+172 stdcall ScRelocProps@20(long ptr ptr ptr ptr) ScRelocProps
173 stdcall LpValFindProp@12(long long ptr) LpValFindProp
174 stub ScDupPropset@16
175 stdcall FBadRglpszA@8(ptr long) FBadRglpszA
diff --git a/dlls/mapi32/prop.c b/dlls/mapi32/prop.c
index 186e979..f7572c8 100644
--- a/dlls/mapi32/prop.c
+++ b/dlls/mapi32/prop.c
@@ -160,7 +160,7 @@
ULONG ulStrLen = strlenW(lpSrc->Value.MVszW.lppszW[i]) + 1u;
lpDest->Value.MVszW.lppszW[i] = lpNextStr;
- memcpy(lpNextStr, lpSrc->Value.MVszA.lppszA[i], ulStrLen * sizeof(WCHAR));
+ memcpy(lpNextStr, lpSrc->Value.MVszW.lppszW[i], ulStrLen * sizeof(WCHAR));
lpNextStr += ulStrLen;
}
break;
@@ -628,6 +628,281 @@
}
/*************************************************************************
+ * ScCopyProps@16 (MAPI32.171)
+ *
+ * Copy an array of property values into a buffer suited for serialisation.
+ *
+ * PARAMS
+ * cValues [I] Number of properties in lpProps
+ * lpProps [I] Property array to copy
+ * lpDst [O] Destination for the serialised data
+ * lpCount [O] If non-NULL, destination for the number of bytes of data written to lpDst
+ *
+ * RETURNS
+ * Success: S_OK. lpDst contains the serialised data from lpProps.
+ * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
+ *
+ * NOTES
+ * The resulting property value array is stored in a contigous block starting at lpDst.
+ */
+SCODE WINAPI ScCopyProps(int cValues, LPSPropValue lpProps, LPVOID lpDst, ULONG *lpCount)
+{
+ LPSPropValue lpDest = (LPSPropValue)lpDst;
+ char *lpDataDest = (char *)(lpDest + cValues);
+ ULONG ulLen, i;
+
+ TRACE("(%d,%p,%p,%p)\n", cValues, lpProps, lpDst, lpCount);
+
+ if (!lpProps || cValues < 0 || !lpDest)
+ return MAPI_E_INVALID_PARAMETER;
+
+ memcpy(lpDst, lpProps, cValues * sizeof(SPropValue));
+
+ for (i = 0; i < cValues; i++)
+ {
+ switch (PROP_TYPE(lpProps->ulPropTag))
+ {
+ case PT_CLSID:
+ lpDest->Value.lpguid = (LPGUID)lpDataDest;
+ memcpy(lpDest->Value.lpguid, lpProps->Value.lpguid, sizeof(GUID));
+ lpDataDest += sizeof(GUID);
+ break;
+ case PT_STRING8:
+ ulLen = lstrlenA(lpProps->Value.lpszA) + 1u;
+ lpDest->Value.lpszA = lpDataDest;
+ memcpy(lpDest->Value.lpszA, lpProps->Value.lpszA, ulLen);
+ lpDataDest += ulLen;
+ break;
+ case PT_UNICODE:
+ ulLen = (strlenW(lpProps->Value.lpszW) + 1u) * sizeof(WCHAR);
+ lpDest->Value.lpszW = (LPWSTR)lpDataDest;
+ memcpy(lpDest->Value.lpszW, lpProps->Value.lpszW, ulLen);
+ lpDataDest += ulLen;
+ break;
+ case PT_BINARY:
+ lpDest->Value.bin.lpb = (LPBYTE)lpDataDest;
+ memcpy(lpDest->Value.bin.lpb, lpProps->Value.bin.lpb, lpProps->Value.bin.cb);
+ lpDataDest += lpProps->Value.bin.cb;
+ break;
+ default:
+ if (lpProps->ulPropTag & MV_FLAG)
+ {
+ lpDest->Value.MVi.cValues = lpProps->Value.MVi.cValues;
+ /* Note: Assignment uses lppszA but covers all cases by union aliasing */
+ lpDest->Value.MVszA.lppszA = (char**)lpDataDest;
+
+ switch (PROP_TYPE(lpProps->ulPropTag))
+ {
+ case PT_MV_STRING8:
+ {
+ lpDataDest += lpProps->Value.MVszA.cValues * sizeof(char *);
+
+ for (i = 0; i < lpProps->Value.MVszA.cValues; i++)
+ {
+ ULONG ulStrLen = lstrlenA(lpProps->Value.MVszA.lppszA[i]) + 1u;
+
+ lpDest->Value.MVszA.lppszA[i] = lpDataDest;
+ memcpy(lpDataDest, lpProps->Value.MVszA.lppszA[i], ulStrLen);
+ lpDataDest += ulStrLen;
+ }
+ break;
+ }
+ case PT_MV_UNICODE:
+ {
+ lpDataDest += lpProps->Value.MVszW.cValues * sizeof(WCHAR *);
+
+ for (i = 0; i < lpProps->Value.MVszW.cValues; i++)
+ {
+ ULONG ulStrLen = (strlenW(lpProps->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
+
+ lpDest->Value.MVszW.lppszW[i] = (LPWSTR)lpDataDest;
+ memcpy(lpDataDest, lpProps->Value.MVszW.lppszW[i], ulStrLen);
+ lpDataDest += ulStrLen;
+ }
+ break;
+ }
+ case PT_MV_BINARY:
+ {
+ lpDataDest += lpProps->Value.MVszW.cValues * sizeof(SBinary);
+
+ for (i = 0; i < lpProps->Value.MVszW.cValues; i++)
+ {
+ lpDest->Value.MVbin.lpbin[i].cb = lpProps->Value.MVbin.lpbin[i].cb;
+ lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)lpDataDest;
+ memcpy(lpDataDest, lpProps->Value.MVbin.lpbin[i].lpb, lpDest->Value.MVbin.lpbin[i].cb);
+ lpDataDest += lpDest->Value.MVbin.lpbin[i].cb;
+ }
+ break;
+ }
+ default:
+ /* No embedded pointers, just copy the data over */
+ ulLen = UlPropSize(lpProps);
+ memcpy(lpDest->Value.MVi.lpi, lpProps->Value.MVi.lpi, ulLen);
+ lpDataDest += ulLen;
+ break;
+ }
+ break;
+ }
+ }
+ lpDest++;
+ lpProps++;
+ }
+ if (lpCount)
+ *lpCount = lpDataDest - (char *)lpDst;
+
+ return S_OK;
+}
+
+/*************************************************************************
+ * ScRelocProps@20 (MAPI32.172)
+ *
+ * Relocate the pointers in an array of property values after it has been copied.
+ *
+ * PARAMS
+ * cValues [I] Number of properties in lpProps
+ * lpProps [O] Property array to relocate the pointers in.
+ * lpOld [I] Position where the data was copied from
+ * lpNew [I] Position where the data was copied to
+ * lpCount [O] If non-NULL, destination for the number of bytes of data at lpDst
+ *
+ * RETURNS
+ * Success: S_OK. Any pointers in lpProps are relocated.
+ * Failure: MAPI_E_INVALID_PARAMETER, if any parameter is invalid.
+ *
+ * NOTES
+ * MSDN states that this function can be used for serialisation by passing
+ * NULL as either lpOld or lpNew, thus converting any pointers in lpProps
+ * between offsets and pointers. This does not work in native (it crashes),
+ * and cannot be made to work in Wine because the original interface design
+ * is deficient. The only use left for this function is to remap pointers
+ * in a contigous property array that has been copied with memcpy() to another
+ * memory location.
+ */
+SCODE WINAPI ScRelocProps(int cValues, LPSPropValue lpProps, LPVOID lpOld,
+ LPVOID lpNew, ULONG *lpCount)
+{
+ static const BOOL bBadPtr = TRUE; /* Windows bug - Assumes source is bad */
+ LPSPropValue lpDest = (LPSPropValue)lpProps;
+ ULONG ulCount = cValues * sizeof(SPropValue);
+ ULONG ulLen, i;
+
+ TRACE("(%d,%p,%p,%p,%p)\n", cValues, lpProps, lpOld, lpNew, lpCount);
+
+ if (!lpProps || cValues < 0 || !lpOld || !lpNew)
+ return MAPI_E_INVALID_PARAMETER;
+
+ /* The reason native doesn't work as MSDN states is that it assumes that
+ * the lpProps pointer contains valid pointers. This is obviously not
+ * true if the array is being read back from serialisation (the pointers
+ * are just offsets). Native can't actually work converting the pointers to
+ * offsets either, because it converts any array pointers to offsets then
+ * _dereferences the offset_ in order to convert the array elements!
+ *
+ * The code below would handle both cases except that the design of this
+ * function makes it impossible to know when the pointers in lpProps are
+ * valid. If both lpOld and lpNew are non-NULL, native reads the pointers
+ * after converting them, so we must do the same. Its seems this
+ * functionality was never tested by MS.
+ */
+
+#define RELOC_PTR(p) (((char*)(p)) - (char*)lpOld + (char*)lpNew)
+
+ for (i = 0; i < cValues; i++)
+ {
+ switch (PROP_TYPE(lpDest->ulPropTag))
+ {
+ case PT_CLSID:
+ lpDest->Value.lpguid = (LPGUID)RELOC_PTR(lpDest->Value.lpguid);
+ ulCount += sizeof(GUID);
+ break;
+ case PT_STRING8:
+ ulLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.lpszA) + 1u;
+ lpDest->Value.lpszA = (LPSTR)RELOC_PTR(lpDest->Value.lpszA);
+ if (bBadPtr)
+ ulLen = lstrlenA(lpDest->Value.lpszA) + 1u;
+ ulCount += ulLen;
+ break;
+ case PT_UNICODE:
+ ulLen = bBadPtr ? 0 : (lstrlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR);
+ lpDest->Value.lpszW = (LPWSTR)RELOC_PTR(lpDest->Value.lpszW);
+ if (bBadPtr)
+ ulLen = (strlenW(lpDest->Value.lpszW) + 1u) * sizeof(WCHAR);
+ ulCount += ulLen;
+ break;
+ case PT_BINARY:
+ lpDest->Value.bin.lpb = (LPBYTE)RELOC_PTR(lpDest->Value.bin.lpb);
+ ulCount += lpDest->Value.bin.cb;
+ break;
+ default:
+ if (lpDest->ulPropTag & MV_FLAG)
+ {
+ /* Since we have to access the array elements, don't map the
+ * array unless it is invalid (otherwise, map it at the end)
+ */
+ if (bBadPtr)
+ lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA);
+
+ switch (PROP_TYPE(lpProps->ulPropTag))
+ {
+ case PT_MV_STRING8:
+ {
+ ulCount += lpDest->Value.MVszA.cValues * sizeof(char *);
+
+ for (i = 0; i < lpDest->Value.MVszA.cValues; i++)
+ {
+ ULONG ulStrLen = bBadPtr ? 0 : lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u;
+
+ lpDest->Value.MVszA.lppszA[i] = (LPSTR)RELOC_PTR(lpDest->Value.MVszA.lppszA[i]);
+ if (bBadPtr)
+ ulStrLen = lstrlenA(lpDest->Value.MVszA.lppszA[i]) + 1u;
+ ulCount += ulStrLen;
+ }
+ break;
+ }
+ case PT_MV_UNICODE:
+ {
+ ulCount += lpDest->Value.MVszW.cValues * sizeof(WCHAR *);
+
+ for (i = 0; i < lpDest->Value.MVszW.cValues; i++)
+ {
+ ULONG ulStrLen = bBadPtr ? 0 : (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
+
+ lpDest->Value.MVszW.lppszW[i] = (LPWSTR)RELOC_PTR(lpDest->Value.MVszW.lppszW[i]);
+ if (bBadPtr)
+ ulStrLen = (strlenW(lpDest->Value.MVszW.lppszW[i]) + 1u) * sizeof(WCHAR);
+ ulCount += ulStrLen;
+ }
+ break;
+ }
+ case PT_MV_BINARY:
+ {
+ ulCount += lpDest->Value.MVszW.cValues * sizeof(SBinary);
+
+ for (i = 0; i < lpDest->Value.MVszW.cValues; i++)
+ {
+ lpDest->Value.MVbin.lpbin[i].lpb = (LPBYTE)RELOC_PTR(lpDest->Value.MVbin.lpbin[i].lpb);
+ ulCount += lpDest->Value.MVbin.lpbin[i].cb;
+ }
+ break;
+ }
+ default:
+ ulCount += UlPropSize(lpDest);
+ break;
+ }
+ if (!bBadPtr)
+ lpDest->Value.MVszA.lppszA = (LPSTR*)RELOC_PTR(lpDest->Value.MVszA.lppszA);
+ break;
+ }
+ }
+ lpDest++;
+ }
+ if (lpCount)
+ *lpCount = ulCount;
+
+ return S_OK;
+}
+
+/*************************************************************************
* LpValFindProp@12 (MAPI32.173)
*
* Find a property with a given property id in a property array.
diff --git a/dlls/mapi32/tests/prop.c b/dlls/mapi32/tests/prop.c
index f223c6c..a04740a 100644
--- a/dlls/mapi32/tests/prop.c
+++ b/dlls/mapi32/tests/prop.c
@@ -41,6 +41,8 @@
static LONG (WINAPI *pLPropCompareProp)(LPSPropValue,LPSPropValue);
static LPSPropValue (WINAPI *pPpropFindProp)(LPSPropValue,ULONG,ULONG);
static SCODE (WINAPI *pScCountProps)(INT,LPSPropValue,ULONG*);
+static SCODE (WINAPI *pScCopyProps)(int,LPSPropValue,LPVOID,ULONG*);
+static SCODE (WINAPI *pScRelocProps)(int,LPSPropValue,LPVOID,LPVOID,ULONG*);
static LPSPropValue (WINAPI *pLpValFindProp)(ULONG,ULONG,LPSPropValue);
static BOOL (WINAPI *pFBadRglpszA)(LPSTR*,ULONG);
static BOOL (WINAPI *pFBadRglpszW)(LPWSTR*,ULONG);
@@ -779,6 +781,68 @@
}
+static void test_ScCopyRelocProps(void)
+{
+ static const char* szTestA = "Test";
+ char buffer[512], buffer2[512], *lppszA[1];
+ SPropValue pvProp, *lpResProp = (LPSPropValue)buffer;
+ ULONG ulCount;
+ SCODE sc;
+
+ pScCopyProps = (void*)GetProcAddress(hMapi32, "ScCopyProps@16");
+ pScRelocProps = (void*)GetProcAddress(hMapi32, "ScRelocProps@20");
+
+ if (!pScCopyProps || !pScRelocProps)
+ return;
+
+ pvProp.ulPropTag = PROP_TAG(PT_MV_STRING8, 1u);
+
+ lppszA[0] = (char *)szTestA;
+ pvProp.Value.MVszA.cValues = 1;
+ pvProp.Value.MVszA.lppszA = lppszA;
+ ulCount = 0;
+
+ sc = pScCopyProps(1, &pvProp, buffer, &ulCount);
+ ok(sc == S_OK && lpResProp->ulPropTag == pvProp.ulPropTag &&
+ lpResProp->Value.MVszA.cValues == 1 &&
+ lpResProp->Value.MVszA.lppszA[0] == buffer + sizeof(SPropValue) + sizeof(char*) &&
+ !strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA) &&
+ ulCount == sizeof(SPropValue) + sizeof(char*) + 5,
+ "CopyProps(str): Expected 0 {1,%lx,%p,%s} %d got 0x%08lx {%ld,%lx,%p,%s} %ld\n",
+ pvProp.ulPropTag, buffer + sizeof(SPropValue) + sizeof(char*),
+ szTestA, sizeof(SPropValue) + sizeof(char*) + 5, sc,
+ lpResProp->Value.MVszA.cValues, lpResProp->ulPropTag,
+ lpResProp->Value.MVszA.lppszA[0], lpResProp->Value.MVszA.lppszA[0], ulCount);
+
+ memcpy(buffer2, buffer, sizeof(buffer));
+
+ /* Clear the data in the source buffer. Since pointers in the copied buffer
+ * refer to the source buffer, this proves that native always assumes that
+ * the copied buffers pointers are bad (needing to be relocated first).
+ */
+ memset(buffer, 0, sizeof(buffer));
+ ulCount = 0;
+
+ sc = pScRelocProps(1, (LPSPropValue)buffer2, buffer, buffer2, &ulCount);
+ lpResProp = (LPSPropValue)buffer2;
+ ok(sc == S_OK && lpResProp->ulPropTag == pvProp.ulPropTag &&
+ lpResProp->Value.MVszA.cValues == 1 &&
+ lpResProp->Value.MVszA.lppszA[0] == buffer2 + sizeof(SPropValue) + sizeof(char*) &&
+ !strcmp(lpResProp->Value.MVszA.lppszA[0], szTestA) &&
+ /* Native has a bug whereby it calculates the size correctly when copying
+ * but when relocating does not (presumably it uses UlPropSize() which
+ * ignores multivalue pointers). Wine returns the correct value.
+ */
+ (ulCount == sizeof(SPropValue) + sizeof(char*) + 5 || ulCount == sizeof(SPropValue) + 5),
+ "RelocProps(str): Expected 0 {1,%lx,%p,%s} %d got 0x%08lx {%ld,%lx,%p,%s} %ld\n",
+ pvProp.ulPropTag, buffer2 + sizeof(SPropValue) + sizeof(char*),
+ szTestA, sizeof(SPropValue) + sizeof(char*) + 5, sc,
+ lpResProp->Value.MVszA.cValues, lpResProp->ulPropTag,
+ lpResProp->Value.MVszA.lppszA[0], lpResProp->Value.MVszA.lppszA[0], ulCount);
+
+ /* Native crashes with lpNew or lpOld set to NULL so skip testing this */
+}
+
static void test_LpValFindProp(void)
{
SPropValue pvProp, *pRet;
@@ -1062,6 +1126,7 @@
test_LPropCompareProp();
test_PpropFindProp();
test_ScCountProps();
+ test_ScCopyRelocProps();
test_LpValFindProp();
test_FBadRglpszA();
test_FBadRglpszW();