[PATCH 5/6] opcservices: Partially implement file-based stream.

Nikolay Sivov nsivov at codeweavers.com
Tue Sep 4 00:04:57 CDT 2018


Signed-off-by: Nikolay Sivov <nsivov at codeweavers.com>
---
 dlls/opcservices/factory.c           | 252 ++++++++++++++++++++++++++-
 dlls/opcservices/tests/opcservices.c |  98 +++++++++++
 2 files changed, 348 insertions(+), 2 deletions(-)

diff --git a/dlls/opcservices/factory.c b/dlls/opcservices/factory.c
index 7cbe233e9d..ca46d0a202 100644
--- a/dlls/opcservices/factory.c
+++ b/dlls/opcservices/factory.c
@@ -33,6 +33,254 @@
 
 WINE_DEFAULT_DEBUG_CHANNEL(msopc);
 
+struct opc_filestream
+{
+    IStream IStream_iface;
+    LONG refcount;
+
+    HANDLE hfile;
+};
+
+static inline struct opc_filestream *impl_from_IStream(IStream *iface)
+{
+    return CONTAINING_RECORD(iface, struct opc_filestream, IStream_iface);
+}
+
+static HRESULT WINAPI opc_filestream_QueryInterface(IStream *iface, REFIID iid, void **out)
+{
+    TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
+
+    if (IsEqualIID(iid, &IID_IStream) ||
+            IsEqualIID(iid, &IID_ISequentialStream) ||
+            IsEqualIID(iid, &IID_IUnknown))
+    {
+        *out = iface;
+        IStream_AddRef(iface);
+        return S_OK;
+    }
+
+    *out = NULL;
+    WARN("Unsupported interface %s.\n", debugstr_guid(iid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI opc_filestream_AddRef(IStream *iface)
+{
+    struct opc_filestream *stream = impl_from_IStream(iface);
+    ULONG refcount = InterlockedIncrement(&stream->refcount);
+
+    TRACE("%p increasing refcount to %u.\n", iface, refcount);
+
+    return refcount;
+}
+
+static ULONG WINAPI opc_filestream_Release(IStream *iface)
+{
+    struct opc_filestream *stream = impl_from_IStream(iface);
+    ULONG refcount = InterlockedDecrement(&stream->refcount);
+
+    TRACE("%p decreasing refcount to %u.\n", iface, refcount);
+
+    if (!refcount)
+    {
+        CloseHandle(stream->hfile);
+        heap_free(stream);
+    }
+
+    return refcount;
+}
+
+static HRESULT WINAPI opc_filestream_Read(IStream *iface, void *buff, ULONG size, ULONG *num_read)
+{
+    struct opc_filestream *stream = impl_from_IStream(iface);
+    DWORD read = 0;
+
+    TRACE("iface %p, buff %p, size %u, num_read %p.\n", iface, buff, size, num_read);
+
+    if (!num_read)
+        num_read = &read;
+
+    *num_read = 0;
+    if (!ReadFile(stream->hfile, buff, size, num_read, NULL))
+    {
+        WARN("Failed to read file, error %d.\n", GetLastError());
+        return HRESULT_FROM_WIN32(GetLastError());
+    }
+
+    return *num_read == size ? S_OK : S_FALSE;
+}
+
+static HRESULT WINAPI opc_filestream_Write(IStream *iface, const void *data, ULONG size, ULONG *num_written)
+{
+    struct opc_filestream *stream = impl_from_IStream(iface);
+    DWORD written = 0;
+
+    TRACE("iface %p, data %p, size %u, num_written %p.\n", iface, data, size, num_written);
+
+    if (!num_written)
+        num_written = &written;
+
+    *num_written = 0;
+    if (!WriteFile(stream->hfile, data, size, num_written, NULL))
+    {
+        WARN("Failed to write to file, error %d.\n", GetLastError());
+        return HRESULT_FROM_WIN32(GetLastError());
+    }
+
+    return S_OK;
+}
+
+static HRESULT WINAPI opc_filestream_Seek(IStream *iface, LARGE_INTEGER move, DWORD origin, ULARGE_INTEGER *newpos)
+{
+    struct opc_filestream *stream = impl_from_IStream(iface);
+
+    TRACE("iface %p, move %s, origin %d, newpos %p.\n", iface, wine_dbgstr_longlong(move.QuadPart), origin, newpos);
+
+    if (!SetFilePointerEx(stream->hfile, move, (LARGE_INTEGER *)newpos, origin))
+        return HRESULT_FROM_WIN32(GetLastError());
+
+    return S_OK;
+}
+
+static HRESULT WINAPI opc_filestream_SetSize(IStream *iface, ULARGE_INTEGER size)
+{
+    FIXME("iface %p, size %s stub!\n", iface, wine_dbgstr_longlong(size.QuadPart));
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI opc_filestream_CopyTo(IStream *iface, IStream *dest, ULARGE_INTEGER size,
+        ULARGE_INTEGER *num_read, ULARGE_INTEGER *written)
+{
+    FIXME("iface %p, dest %p, size %s, num_read %p, written %p stub!\n", iface, dest,
+            wine_dbgstr_longlong(size.QuadPart), num_read, written);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI opc_filestream_Commit(IStream *iface, DWORD flags)
+{
+    FIXME("iface %p, flags %#x stub!\n", iface, flags);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI opc_filestream_Revert(IStream *iface)
+{
+    FIXME("iface %p stub!\n", iface);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI opc_filestream_LockRegion(IStream *iface, ULARGE_INTEGER offset,
+        ULARGE_INTEGER size, DWORD lock_type)
+{
+    FIXME("iface %p, offset %s, size %s, lock_type %d stub!\n", iface, wine_dbgstr_longlong(offset.QuadPart),
+            wine_dbgstr_longlong(size.QuadPart), lock_type);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI opc_filestream_UnlockRegion(IStream *iface, ULARGE_INTEGER offset, ULARGE_INTEGER size,
+        DWORD lock_type)
+{
+    FIXME("iface %p, offset %s, size %s, lock_type %d stub!\n", iface, wine_dbgstr_longlong(offset.QuadPart),
+            wine_dbgstr_longlong(size.QuadPart), lock_type);
+
+    return E_NOTIMPL;
+}
+
+static HRESULT WINAPI opc_filestream_Stat(IStream *iface, STATSTG *statstg, DWORD flag)
+{
+    struct opc_filestream *stream = impl_from_IStream(iface);
+    BY_HANDLE_FILE_INFORMATION fi;
+
+    TRACE("iface %p, statstg %p, flag %d.\n", iface, statstg, flag);
+
+    if (!statstg)
+        return E_POINTER;
+
+    memset(&fi, 0, sizeof(fi));
+    GetFileInformationByHandle(stream->hfile, &fi);
+
+    memset(statstg, 0, sizeof(*statstg));
+    statstg->type = STGTY_STREAM;
+    statstg->cbSize.u.LowPart = fi.nFileSizeLow;
+    statstg->cbSize.u.HighPart = fi.nFileSizeHigh;
+    statstg->mtime = fi.ftLastWriteTime;
+    statstg->ctime = fi.ftCreationTime;
+    statstg->atime = fi.ftLastAccessTime;
+
+    return S_OK;
+}
+
+static HRESULT WINAPI opc_filestream_Clone(IStream *iface, IStream **result)
+{
+    FIXME("iface %p, result %p stub!\n", iface, result);
+
+    return E_NOTIMPL;
+}
+
+static const IStreamVtbl opc_filestream_vtbl =
+{
+    opc_filestream_QueryInterface,
+    opc_filestream_AddRef,
+    opc_filestream_Release,
+    opc_filestream_Read,
+    opc_filestream_Write,
+    opc_filestream_Seek,
+    opc_filestream_SetSize,
+    opc_filestream_CopyTo,
+    opc_filestream_Commit,
+    opc_filestream_Revert,
+    opc_filestream_LockRegion,
+    opc_filestream_UnlockRegion,
+    opc_filestream_Stat,
+    opc_filestream_Clone,
+};
+
+static HRESULT opc_filestream_create(const WCHAR *filename, OPC_STREAM_IO_MODE io_mode, SECURITY_ATTRIBUTES *sa,
+        DWORD flags, IStream **out)
+{
+    struct opc_filestream *stream;
+    DWORD access, creation;
+
+    if (!filename || !out)
+        return E_POINTER;
+
+    switch (io_mode)
+    {
+    case OPC_STREAM_IO_READ:
+        access = GENERIC_READ;
+        creation = OPEN_EXISTING;
+        break;
+    case OPC_STREAM_IO_WRITE:
+        access = GENERIC_WRITE;
+        creation = CREATE_ALWAYS;
+        break;
+    default:
+        return E_INVALIDARG;
+    }
+
+    if (!(stream = heap_alloc_zero(sizeof(*stream))))
+        return E_OUTOFMEMORY;
+
+    stream->hfile = CreateFileW(filename, access, 0, sa, creation, flags, NULL);
+    if (stream->hfile == INVALID_HANDLE_VALUE)
+    {
+        HRESULT hr = HRESULT_FROM_WIN32(GetLastError());
+        heap_free(stream);
+        return hr;
+    }
+
+    stream->IStream_iface.lpVtbl = &opc_filestream_vtbl;
+    stream->refcount = 1;
+
+    *out = &stream->IStream_iface;
+    TRACE("Created file steam %p.\n", *out);
+    return S_OK;
+}
+
 static HRESULT WINAPI opc_factory_QueryInterface(IOpcFactory *iface, REFIID iid, void **out)
 {
     TRACE("iface %p, iid %s, out %p.\n", iface, debugstr_guid(iid), out);
@@ -76,10 +324,10 @@ static HRESULT WINAPI opc_factory_CreatePartUri(IOpcFactory *iface, LPCWSTR uri,
 static HRESULT WINAPI opc_factory_CreateStreamOnFile(IOpcFactory *iface, LPCWSTR filename,
         OPC_STREAM_IO_MODE io_mode, SECURITY_ATTRIBUTES *sa, DWORD flags, IStream **stream)
 {
-    FIXME("iface %p, filename %s, io_mode %d, sa %p, flags %#x, stream %p stub!\n", iface, debugstr_w(filename),
+    TRACE("iface %p, filename %s, io_mode %d, sa %p, flags %#x, stream %p.\n", iface, debugstr_w(filename),
             io_mode, sa, flags, stream);
 
-    return E_NOTIMPL;
+    return opc_filestream_create(filename, io_mode, sa, flags, stream);
 }
 
 static HRESULT WINAPI opc_factory_CreatePackage(IOpcFactory *iface, IOpcPackage **package)
diff --git a/dlls/opcservices/tests/opcservices.c b/dlls/opcservices/tests/opcservices.c
index 8ab7f39e08..5dc0d8c772 100644
--- a/dlls/opcservices/tests/opcservices.c
+++ b/dlls/opcservices/tests/opcservices.c
@@ -107,6 +107,103 @@ todo_wine {
     IOpcFactory_Release(factory);
 }
 
+#define test_stream_stat(stream, size) test_stream_stat_(__LINE__, stream, size)
+static void test_stream_stat_(unsigned int line, IStream *stream, ULONG size)
+{
+    STATSTG statstg;
+    HRESULT hr;
+
+    hr = IStream_Stat(stream, NULL, 0);
+    ok_(__FILE__, line)(hr == E_POINTER, "Unexpected hr %#x.\n", hr);
+
+    memset(&statstg, 0xff, sizeof(statstg));
+    hr = IStream_Stat(stream, &statstg, 0);
+    ok_(__FILE__, line)(SUCCEEDED(hr), "Failed to get stat info, hr %#x.\n", hr);
+
+    ok_(__FILE__, line)(statstg.pwcsName == NULL, "Unexpected name %s.\n", wine_dbgstr_w(statstg.pwcsName));
+    ok_(__FILE__, line)(statstg.type == STGTY_STREAM, "Unexpected type.\n");
+    ok_(__FILE__, line)(statstg.cbSize.QuadPart == size, "Unexpected size %u, expected %u.\n",
+            statstg.cbSize.LowPart, size);
+    ok_(__FILE__, line)(statstg.grfMode == STGM_READ, "Unexpected mode.\n");
+    ok_(__FILE__, line)(statstg.grfLocksSupported == 0, "Unexpected lock mode.\n");
+    ok_(__FILE__, line)(statstg.grfStateBits == 0, "Unexpected state bits.\n");
+}
+
+static void test_file_stream(void)
+{
+    static const WCHAR filereadW[] = {'o','p','c','f','i','l','e','r','e','a','d','.','e','x','t',0};
+    WCHAR temppathW[MAX_PATH], pathW[MAX_PATH];
+    IOpcFactory *factory;
+    LARGE_INTEGER move;
+    IStream *stream;
+    char buff[64];
+    HRESULT hr;
+    ULONG size;
+
+    factory = create_factory();
+
+    hr = IOpcFactory_CreateStreamOnFile(factory, NULL, OPC_STREAM_IO_READ, NULL, 0, &stream);
+    ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr);
+
+    GetTempPathW(ARRAY_SIZE(temppathW), temppathW);
+    lstrcpyW(pathW, temppathW);
+    lstrcatW(pathW, filereadW);
+    DeleteFileW(pathW);
+
+    hr = IOpcFactory_CreateStreamOnFile(factory, pathW, OPC_STREAM_IO_READ, NULL, 0, NULL);
+    ok(hr == E_POINTER, "Unexpected hr %#x.\n", hr);
+
+    /* File does not exist */
+    hr = IOpcFactory_CreateStreamOnFile(factory, pathW, OPC_STREAM_IO_READ, NULL, 0, &stream);
+    ok(FAILED(hr), "Unexpected hr %#x.\n", hr);
+
+    hr = IOpcFactory_CreateStreamOnFile(factory, pathW, OPC_STREAM_IO_WRITE, NULL, 0, &stream);
+    ok(SUCCEEDED(hr), "Failed to create a write stream, hr %#x.\n", hr);
+
+    test_stream_stat(stream, 0);
+
+    size = lstrlenW(pathW) * sizeof(WCHAR);
+    hr = IStream_Write(stream, pathW, size, NULL);
+    ok(hr == S_OK, "Stream write failed, hr %#x.\n", hr);
+
+    test_stream_stat(stream, size);
+    IStream_Release(stream);
+
+    /* Invalid I/O mode */
+    hr = IOpcFactory_CreateStreamOnFile(factory, pathW, 10, NULL, 0, &stream);
+    ok(hr == E_INVALIDARG, "Failed to create a write stream, hr %#x.\n", hr);
+
+    /* Write to read-only stream. */
+    hr = IOpcFactory_CreateStreamOnFile(factory, pathW, OPC_STREAM_IO_READ, NULL, 0, &stream);
+    ok(SUCCEEDED(hr), "Failed to create a read stream, hr %#x.\n", hr);
+
+    test_stream_stat(stream, size);
+    hr = IStream_Write(stream, pathW, size, NULL);
+    ok(hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), "Stream write failed, hr %#x.\n", hr);
+    IStream_Release(stream);
+
+    /* Read from write-only stream. */
+    hr = IOpcFactory_CreateStreamOnFile(factory, pathW, OPC_STREAM_IO_WRITE, NULL, 0, &stream);
+    ok(SUCCEEDED(hr), "Failed to create a read stream, hr %#x.\n", hr);
+
+    test_stream_stat(stream, 0);
+    hr = IStream_Write(stream, pathW, size, NULL);
+    ok(hr == S_OK, "Stream write failed, hr %#x.\n", hr);
+    test_stream_stat(stream, size);
+
+    move.QuadPart = 0;
+    hr = IStream_Seek(stream, move, STREAM_SEEK_SET, NULL);
+    ok(SUCCEEDED(hr), "Seek failed, hr %#x.\n", hr);
+
+    hr = IStream_Read(stream, buff, sizeof(buff), NULL);
+    ok(hr == HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED), "Stream read failed, hr %#x.\n", hr);
+
+    IStream_Release(stream);
+
+    IOpcFactory_Release(factory);
+    DeleteFileW(pathW);
+}
+
 START_TEST(opcservices)
 {
     IOpcFactory *factory;
@@ -122,6 +219,7 @@ START_TEST(opcservices)
     }
 
     test_package();
+    test_file_stream();
 
     IOpcFactory_Release(factory);
 
-- 
2.18.0




More information about the wine-devel mailing list