[PATCH 0/3] MR11054: ole32: Implement IStorage::MoveElementTo().
From: Dmitry Timoshkov <dmitry@baikal.ru> Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru> --- dlls/ole32/tests/storage32.c | 115 +++++++++++++++++++++++++++++++++++ 1 file changed, 115 insertions(+) diff --git a/dlls/ole32/tests/storage32.c b/dlls/ole32/tests/storage32.c index cb77b025293..5e8fc396718 100644 --- a/dlls/ole32/tests/storage32.c +++ b/dlls/ole32/tests/storage32.c @@ -3937,6 +3937,120 @@ static void test_custom_lockbytes(void) DeleteTestLockBytes(lockbytes); } +static void test_MoveElementTo(void) +{ + WCHAR temp[MAX_PATH], src_name[MAX_PATH], dst_name[MAX_PATH]; + IStorage *src, *dst, *stg; + IStream *stream; + HRESULT hr; + + GetTempPathW(MAX_PATH, temp); + GetTempFileNameW(temp, L"stg", 0, src_name); + GetTempFileNameW(temp, L"stg", 0, dst_name); + + hr = StgCreateDocfile(src_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &src); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = create_test_file(src); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = StgCreateDocfile(dst_name, STGM_CREATE | STGM_SHARE_EXCLUSIVE | STGM_READWRITE, 0, &dst); + ok(hr == S_OK, "got %#lx\n", hr); + + /* STGTY_STREAM */ + + hr = IStorage_MoveElementTo(src, NULL, NULL, NULL, 0); + todo_wine + ok(hr == STG_E_INVALIDNAME, "got %#lx\n", hr); + if (hr != STG_E_INVALIDNAME) goto next; + + hr = IStorage_MoveElementTo(src, strmA_name, dst, strmC_name, ~0); + ok(hr == STG_E_INVALIDFLAG, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, strmA_name, dst, strmC_name, STGMOVE_COPY); + ok(hr == STG_E_FILENOTFOUND, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, strmC_name, dst, NULL, 0); + ok(hr == STG_E_INVALIDNAME, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, strmC_name, NULL, strmC_name, STGMOVE_COPY); + ok(hr == STG_E_INVALIDPOINTER, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, strmC_name, NULL, strmC_name, ~0); + ok(hr == STG_E_INVALIDFLAG, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, strmC_name, src, strmC_name, STGMOVE_COPY); + ok(hr == STG_E_ACCESSDENIED, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, strmC_name, dst, strmC_name, STGMOVE_COPY); + ok(hr == S_OK, "got %#lx\n", hr); + + /* STGMOVE_COPY doesn't fail if the target already exsists */ + hr = IStorage_MoveElementTo(src, strmC_name, dst, strmC_name, STGMOVE_COPY); + ok(hr == S_OK, "got %#lx\n", hr); + + /* STGMOVE_MOVE fails if the target already exsists */ + hr = IStorage_MoveElementTo(src, strmC_name, dst, strmC_name, STGMOVE_MOVE); + ok(hr == STG_E_FILEALREADYEXISTS, "got %#lx\n", hr); + + hr = IStorage_DestroyElement(dst, strmC_name); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, strmC_name, dst, strmC_name, STGMOVE_MOVE); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IStorage_OpenStream(src, strmC_name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream); + ok(hr == STG_E_FILENOTFOUND, "got %#lx\n", hr); + + hr = IStorage_OpenStream(dst, strmC_name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stream); + ok(hr == S_OK, "got %#lx\n", hr); + IStream_Release(stream); + + hr = IStorage_MoveElementTo(src, strmC_name, dst, strmC_name, STGMOVE_COPY); + ok(hr == STG_E_FILENOTFOUND, "got %#lx\n", hr); + + /* STGTY_STORAGE */ + +next: + hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_COPY); + todo_wine + ok(hr == S_OK, "got %#lx\n", hr); + if (hr != S_OK) goto done; + + /* STGMOVE_COPY doesn't fail if the target already exsists */ + hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_COPY); + ok(hr == S_OK, "got %#lx\n", hr); + + /* STGMOVE_MOVE fails if the target already exsists */ + hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_MOVE); + ok(hr == STG_E_FILEALREADYEXISTS, "got %#lx\n", hr); + + hr = IStorage_DestroyElement(dst, stgB_name); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_MOVE); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_COPY); + ok(hr == STG_E_FILENOTFOUND, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, stgB_name, dst, stgA_name, STGMOVE_MOVE); + ok(hr == S_OK, "got %#lx\n", hr); + + hr = IStorage_MoveElementTo(src, stgB_name, dst, stgA_name, STGMOVE_COPY); + ok(hr == STG_E_FILENOTFOUND, "got %#lx\n", hr); + + hr = IStorage_OpenStorage(dst, stgB_name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, &stg); + ok(hr == S_OK, "got %#lx\n", hr); + IStorage_Release(stg); + +done: + IStorage_Release(src); + IStorage_Release(dst); + DeleteFileW(src_name); + DeleteFileW(dst_name); +} + START_TEST(storage32) { CHAR temp[MAX_PATH]; @@ -3986,4 +4100,5 @@ START_TEST(storage32) test_transacted_shared(); test_overwrite(); test_custom_lockbytes(); + test_MoveElementTo(); } -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11054
From: Dmitry Timoshkov <dmitry@baikal.ru> Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru> --- dlls/ole32/storage32.c | 58 ++++++++++++++++++++++++++++++------ dlls/ole32/tests/storage32.c | 3 -- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/dlls/ole32/storage32.c b/dlls/ole32/storage32.c index d3f4760cfe1..6b90e7cc237 100644 --- a/dlls/ole32/storage32.c +++ b/dlls/ole32/storage32.c @@ -2363,16 +2363,56 @@ static HRESULT WINAPI StorageBaseImpl_CopyTo( /************************************************************************* * MoveElementTo (IStorage) */ -static HRESULT WINAPI StorageBaseImpl_MoveElementTo( - IStorage* iface, - const OLECHAR *pwcsName, /* [string][in] */ - IStorage *pstgDest, /* [unique][in] */ - const OLECHAR *pwcsNewName,/* [string][in] */ - DWORD grfFlags) /* [in] */ +static HRESULT WINAPI StorageBaseImpl_MoveElementTo(IStorage *iface, + const OLECHAR *name, IStorage *dest, const OLECHAR *new_name, DWORD mode) { - FIXME("%p, %s, %p, %s, %#lx: stub\n", iface, debugstr_w(pwcsName), pstgDest, - debugstr_w(pwcsNewName), grfFlags); - return E_NOTIMPL; + IStream *src, *dst; + HRESULT hr; + + TRACE("%p, %s, %p, %s, %#lx\n", iface, debugstr_w(name), dest, debugstr_w(new_name), mode); + + if (mode != STGMOVE_COPY && mode != STGMOVE_MOVE) + return STG_E_INVALIDFLAG; + + if (!name || !new_name) + return STG_E_INVALIDNAME; + + if (!dest) + return STG_E_INVALIDPOINTER; + + if (iface == dest) /* FIXME */ + return STG_E_ACCESSDENIED; + + /* FIXME: Handle STGTY_STORAGE */ + hr = IStorage_OpenStream(iface, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &src); + if (hr == S_OK) + { + STATSTG stat; + + hr = IStream_Stat(src, &stat, STATFLAG_NONAME); + if (hr != S_OK) + { + IStream_Release(src); + return hr; + } + + hr = IStorage_CreateStream(dest, new_name, STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE, 0, 0, &dst); + /* STGMOVE_MOVE fails if the target already exsists */ + if (hr == STG_E_FILEALREADYEXISTS && mode != STGMOVE_MOVE) + hr = IStorage_OpenStream(dest, new_name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, 0, &dst); + if (hr == S_OK) + { + hr = IStream_CopyTo(src, dst, stat.cbSize, NULL, NULL); + IStream_Release(dst); + } + + IStream_Release(src); + } + + if (hr == S_OK && mode == STGMOVE_MOVE) + hr = IStorage_DestroyElement(iface, name); + + return hr; } /************************************************************************* diff --git a/dlls/ole32/tests/storage32.c b/dlls/ole32/tests/storage32.c index 5e8fc396718..66382548f60 100644 --- a/dlls/ole32/tests/storage32.c +++ b/dlls/ole32/tests/storage32.c @@ -3960,9 +3960,7 @@ static void test_MoveElementTo(void) /* STGTY_STREAM */ hr = IStorage_MoveElementTo(src, NULL, NULL, NULL, 0); - todo_wine ok(hr == STG_E_INVALIDNAME, "got %#lx\n", hr); - if (hr != STG_E_INVALIDNAME) goto next; hr = IStorage_MoveElementTo(src, strmA_name, dst, strmC_name, ~0); ok(hr == STG_E_INVALIDFLAG, "got %#lx\n", hr); @@ -4011,7 +4009,6 @@ static void test_MoveElementTo(void) /* STGTY_STORAGE */ -next: hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_COPY); todo_wine ok(hr == S_OK, "got %#lx\n", hr); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11054
From: Dmitry Timoshkov <dmitry@baikal.ru> Signed-off-by: Dmitry Timoshkov <dmitry@baikal.ru> --- dlls/ole32/storage32.c | 21 ++++++++++++++++++++- dlls/ole32/tests/storage32.c | 3 --- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/dlls/ole32/storage32.c b/dlls/ole32/storage32.c index 6b90e7cc237..8797fab621c 100644 --- a/dlls/ole32/storage32.c +++ b/dlls/ole32/storage32.c @@ -2383,7 +2383,6 @@ static HRESULT WINAPI StorageBaseImpl_MoveElementTo(IStorage *iface, if (iface == dest) /* FIXME */ return STG_E_ACCESSDENIED; - /* FIXME: Handle STGTY_STORAGE */ hr = IStorage_OpenStream(iface, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &src); if (hr == S_OK) { @@ -2408,6 +2407,26 @@ static HRESULT WINAPI StorageBaseImpl_MoveElementTo(IStorage *iface, IStream_Release(src); } + else if (hr == STG_E_FILENOTFOUND) + { + IStorage *src_stg, *dst_stg; + + hr = IStorage_OpenStorage(iface, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, NULL, 0, &src_stg); + if (hr == S_OK) + { + hr = IStorage_CreateStorage(dest, new_name, STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE, 0, 0, &dst_stg); + /* STGMOVE_MOVE fails if the target already exsists */ + if (hr == STG_E_FILEALREADYEXISTS && mode != STGMOVE_MOVE) + hr = IStorage_OpenStorage(dest, new_name, NULL, STGM_WRITE | STGM_SHARE_EXCLUSIVE, NULL, 0, &dst_stg); + if (hr == S_OK) + { + hr = IStorage_CopyTo(src_stg, 0, NULL, NULL, dst_stg); + IStorage_Release(dst_stg); + } + + IStorage_Release(src_stg); + } + } if (hr == S_OK && mode == STGMOVE_MOVE) hr = IStorage_DestroyElement(iface, name); diff --git a/dlls/ole32/tests/storage32.c b/dlls/ole32/tests/storage32.c index 66382548f60..7bca822aa96 100644 --- a/dlls/ole32/tests/storage32.c +++ b/dlls/ole32/tests/storage32.c @@ -4010,9 +4010,7 @@ static void test_MoveElementTo(void) /* STGTY_STORAGE */ hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_COPY); - todo_wine ok(hr == S_OK, "got %#lx\n", hr); - if (hr != S_OK) goto done; /* STGMOVE_COPY doesn't fail if the target already exsists */ hr = IStorage_MoveElementTo(src, stgA_name, dst, stgB_name, STGMOVE_COPY); @@ -4041,7 +4039,6 @@ static void test_MoveElementTo(void) ok(hr == S_OK, "got %#lx\n", hr); IStorage_Release(stg); -done: IStorage_Release(src); IStorage_Release(dst); DeleteFileW(src_name); -- GitLab https://gitlab.winehq.org/wine/wine/-/merge_requests/11054
Esme Povirk (@madewokherd) commented about dlls/ole32/storage32.c:
+ return STG_E_ACCESSDENIED; + + /* FIXME: Handle STGTY_STORAGE */ + hr = IStorage_OpenStream(iface, name, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &src); + if (hr == S_OK) + { + STATSTG stat; + + hr = IStream_Stat(src, &stat, STATFLAG_NONAME); + if (hr != S_OK) + { + IStream_Release(src); + return hr; + } + + hr = IStorage_CreateStream(dest, new_name, STGM_WRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE, 0, 0, &dst); I would suggest leaving out STGM_FAILIFTHERE depending on the mode, and then we don't need the extra check or OpenStream call.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11054#note_142025
On Tue Jun 2 18:34:34 2026 +0000, Esme Povirk wrote:
I would suggest leaving out STGM_FAILIFTHERE depending on the mode, and then we don't need the extra check or OpenStream call. My first version was not using STGM_FAILIFTHERE, but after adding the tests and experimenting with possible solutions I couldn't find a better way to make the tests pass. How would you suggest to avoid it?
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11054#note_142027
On Tue Jun 2 18:59:59 2026 +0000, Dmitry Timoshkov wrote:
My first version was not using STGM_FAILIFTHERE, but after adding the tests and experimenting with possible solutions I couldn't find a better way to make the tests pass. How would you suggest to avoid it? I was thinking something like:
DWORD createMode = STGM_WRITE | STGM_SHARE_EXCLUSIVE;
if (mode == STGMOVE_MOVE)
createMode |= STGM_FAILIFTHERE;
...
hr = IStorage_CreateStream(dest, new_name, createMode, 0, 0, &dst);
But if there's something I'm missing that makes it more complicated than that, we can use the extra call. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11054#note_142030
On Tue Jun 2 19:13:58 2026 +0000, Esme Povirk wrote:
I was thinking something like: ``` DWORD createMode = STGM_WRITE | STGM_SHARE_EXCLUSIVE; if (mode == STGMOVE_MOVE) createMode |= STGM_FAILIFTHERE; ... hr = IStorage_CreateStream(dest, new_name, createMode, 0, 0, &dst); ``` But if there's something I'm missing that makes it more complicated than that, we can use the extra call. Thanks for the suggestion, however it doesn't seem to make the tests pass. Adding STGM_CREATE didn't help either.
-- https://gitlab.winehq.org/wine/wine/-/merge_requests/11054#note_142031
On Tue Jun 2 19:28:49 2026 +0000, Dmitry Timoshkov wrote:
Thanks for the suggestion, however it doesn't seem to make the tests pass. Adding STGM_CREATE didn't help either.
DWORD createMode = STGM_WRITE | STGM_SHARE_EXCLUSIVE;
createMode |= (mode == STGMOVE_MOVE) ? STGM_FAILIFTHERE : STGM_CREATE;
seems to work. -- https://gitlab.winehq.org/wine/wine/-/merge_requests/11054#note_142032
participants (3)
-
Dmitry Timoshkov -
Dmitry Timoshkov (@dmitry) -
Esme Povirk (@madewokherd)