MSI: implement MsiRecordSetStreamA/W and add tests for records containing streams

Mike McCormack mike at codeweavers.com
Fri Jan 7 03:53:13 CST 2005


ChangeLog:
* implement MsiRecordSetStreamA/W and add tests for records containing 
streams
-------------- next part --------------
Index: dlls/msi/record.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/record.c,v
retrieving revision 1.18
diff -u -r1.18 record.c
--- dlls/msi/record.c	6 Jan 2005 20:42:25 -0000	1.18
+++ dlls/msi/record.c	7 Jan 2005 09:50:45 -0000
@@ -32,6 +32,7 @@
 #include "msipriv.h"
 #include "objidl.h"
 #include "winnls.h"
+#include "ole2.h"
 
 #include "query.h"
 
@@ -531,16 +532,133 @@
     return ERROR_CALL_NOT_IMPLEMENTED;
 }
 
+/* read the data in a file into an IStream */
+UINT RECORD_StreamFromFile(LPCWSTR szFile, IStream **pstm)
+{
+    DWORD sz, szHighWord = 0, read;
+    HANDLE handle;
+    HGLOBAL hGlob = 0;
+    HRESULT hr;
+    ULARGE_INTEGER ulSize;
+
+    TRACE("reading %s\n", debugstr_w(szFile));
+
+    /* read the file into memory */
+    handle = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    if( handle == INVALID_HANDLE_VALUE )
+        return GetLastError();
+    sz = GetFileSize(handle, &szHighWord);
+    if( sz != INVALID_FILE_SIZE && szHighWord == 0 )
+    {
+        hGlob = GlobalAlloc(GMEM_FIXED, sz);
+        if( hGlob )
+        {
+            BOOL r = ReadFile(handle, hGlob, sz, &read, NULL);
+            if( !r )
+            {
+                GlobalFree(hGlob);
+                hGlob = 0;
+            }
+        }
+    }
+    CloseHandle(handle);
+    if( !hGlob )
+        return ERROR_FUNCTION_FAILED;
+
+    /* make a stream out of it, and set the correct file size */
+    hr = CreateStreamOnHGlobal(hGlob, TRUE, pstm);
+    if( FAILED( hr ) )
+    {
+        GlobalFree(hGlob);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    /* set the correct size - CreateStreamOnHGlobal screws it up */
+    ulSize.QuadPart = sz;
+    IStream_SetSize(*pstm, ulSize);
+
+    TRACE("read %s, %ld bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
+
+    return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordSetStreamW(MSIRECORD *rec, unsigned int iField, LPCWSTR szFilename)
+{
+    IStream *stm = NULL;
+    HRESULT r;
+
+    if( (iField == 0) || (iField > rec->count) )
+        return ERROR_INVALID_PARAMETER;
+
+    /* no filename means we should seek back to the start of the stream */
+    if( !szFilename )
+    {
+        LARGE_INTEGER ofs;
+        ULARGE_INTEGER cur;
+
+        if( rec->fields[iField].type != MSIFIELD_STREAM )
+            return ERROR_INVALID_FIELD;
+
+        stm = rec->fields[iField].u.stream;
+        if( !stm )
+            return ERROR_INVALID_FIELD;
+
+        ofs.QuadPart = 0;
+        r = IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
+        if( FAILED( r ) )
+            return ERROR_FUNCTION_FAILED;
+    }
+    else
+    {
+        /* read the file into a stream and save the stream in the record */
+        r = RECORD_StreamFromFile(szFilename, &stm);
+        if( r != ERROR_SUCCESS )
+            return r;
+
+        /* if all's good, store it in the record */
+        MSI_FreeField( &rec->fields[iField] );
+        rec->fields[iField].type = MSIFIELD_STREAM;
+        rec->fields[iField].u.stream = stm;
+    }
+
+    return ERROR_SUCCESS;
+}
+
 UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, unsigned int iField, LPCSTR szFilename)
 {
-    FIXME("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename));
-    return ERROR_CALL_NOT_IMPLEMENTED;
+    LPWSTR wstr = NULL;
+    UINT ret, len;
+
+    TRACE("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename));
+
+    if( szFilename )
+    {
+        len = MultiByteToWideChar(CP_ACP,0,szFilename,-1,NULL,0);
+        wstr = HeapAlloc(GetProcessHeap(),0,len*sizeof(WCHAR));
+        MultiByteToWideChar(CP_ACP,0,szFilename,-1,wstr,len);
+    }
+    ret = MsiRecordSetStreamW(hRecord, iField, wstr);
+    HeapFree(GetProcessHeap(),0,wstr);
+
+    return ret;
 }
 
-UINT WINAPI MsiRecordSetStreamW(MSIHANDLE hRecord, unsigned int iField, LPCWSTR szFilename)
+UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, unsigned int iField, LPCWSTR szFilename)
 {
-    FIXME("%ld %d %s\n", hRecord, iField, debugstr_w(szFilename));
-    return ERROR_CALL_NOT_IMPLEMENTED;
+    MSIRECORD *rec;
+    UINT ret;
+
+    TRACE("%ld %d %s\n", handle, iField, debugstr_w(szFilename));
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    msiobj_lock( &rec->hdr );
+    ret = MSI_RecordSetStreamW( rec, iField, szFilename );
+    msiobj_unlock( &rec->hdr );
+    msiobj_release( &rec->hdr );
+    return ret;
 }
 
 UINT MSI_RecordReadStream(MSIRECORD *rec, unsigned int iField, char *buf, DWORD *sz)
@@ -551,18 +669,18 @@
 
     TRACE("%p %d %p %p\n", rec, iField, buf, sz);
 
-    if( iField > rec->count )
-        return ERROR_INVALID_FIELD;
+    if( !sz )
+        return ERROR_INVALID_PARAMETER;
+
+    if( iField > rec->count)
+        return ERROR_INVALID_PARAMETER;
 
     if( rec->fields[iField].type != MSIFIELD_STREAM )
-    {
-        *sz = 0;
-        return ERROR_INVALID_FIELD;
-    }
+        return ERROR_INVALID_DATATYPE;
 
     stm = rec->fields[iField].u.stream;
     if( !stm )
-        return ERROR_INVALID_FIELD;
+        return ERROR_INVALID_PARAMETER;
 
     /* if there's no buffer pointer, calculate the length to the end */
     if( !buf )
Index: dlls/msi/tests/record.c
===================================================================
RCS file: /home/wine/wine/dlls/msi/tests/record.c,v
retrieving revision 1.1
diff -u -r1.1 record.c
--- dlls/msi/tests/record.c	6 Jan 2005 20:42:48 -0000	1.1
+++ dlls/msi/tests/record.c	7 Jan 2005 09:50:45 -0000
@@ -24,6 +24,27 @@
 
 #include "wine/test.h"
 
+static BOOL create_temp_file(char *name)
+{
+    UINT r;
+    unsigned char buffer[26], i;
+    DWORD sz;
+    HANDLE handle;
+    
+    r = GetTempFileName(".", "msitest",0,name);
+    if(!r)
+        return r;
+    handle = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 
+        0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
+    if(handle==INVALID_HANDLE_VALUE)
+        return 0;
+    for(i=0; i<26; i++)
+        buffer[i]=i+'a';
+    r = WriteFile(handle,buffer,sizeof buffer,&sz,NULL);
+    CloseHandle(handle);
+    return r;
+}
+
 void test_msirecord(void)
 {
     DWORD r, sz;
@@ -31,6 +52,7 @@
     MSIHANDLE h;
     char buf[10];
     const char str[] = "hello";
+    char filename[MAX_PATH];
 
     /* check behaviour with an invalid record */
     r = MsiRecordGetFieldCount(0);
@@ -88,10 +110,8 @@
     r = MsiRecordSetInteger(h, 0, 1);
     ok(r == ERROR_SUCCESS, "Failed to set integer at 0 to 1\n");
     r = MsiRecordSetInteger(h, 1, 1);
-    /*printf("r = %d\n",r); */
     ok(r == ERROR_INVALID_PARAMETER, "set integer at 1\n");
     r = MsiRecordSetInteger(h, -1, 0);
-    /*printf("r = %d\n",r); */
     ok(r == ERROR_INVALID_PARAMETER, "set integer at -1\n");
     r = MsiRecordIsNull(h,0);
     ok(r==0, "new record is null after setting an integer\n");
@@ -185,7 +205,73 @@
     ok(r == ERROR_SUCCESS, "failed to get string from integer\n");
     ok(0==strcmp(buf,"-32"), "failed to get string from integer\n");
 
+    /* same record, now try streams */
+    r = MsiRecordSetStream(h, 0, NULL);
+    ok(r == ERROR_INVALID_PARAMETER, "set NULL stream\n");
+    sz = sizeof buf;
+    r = MsiRecordReadStream(h, 0, buf, &sz);
+    ok(r == ERROR_INVALID_DATATYPE, "read non-stream type\n");
+    ok(sz == sizeof buf, "set sz\n");
+
     /* same record, now close it */
+    r = MsiCloseHandle(h);
+    ok(r == ERROR_SUCCESS, "Failed to close handle\n");
+
+    /* now try streams in a new record - need to create a file to play with */
+    r = create_temp_file(filename); 
+    if(!r)
+        return;
+
+    /* streams can't be inserted in field 0 for some reason */
+    h = MsiCreateRecord(2);
+    ok(h, "couldn't create a two field record\n");
+    r = MsiRecordSetStream(h, 0, filename);
+    ok(r == ERROR_INVALID_PARAMETER, "added stream to field 0\n");
+    r = MsiRecordSetStream(h, 1, filename);
+    ok(r == ERROR_SUCCESS, "failed to add stream to record\n");
+    r = MsiRecordReadStream(h, 1, buf, NULL);
+    ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
+    ok(DeleteFile(filename), "failed to delete stream temp file\n");
+    r = MsiRecordReadStream(h, 1, NULL, NULL);
+    ok(r == ERROR_INVALID_PARAMETER, "should return error\n");
+    sz = sizeof buf;
+    r = MsiRecordReadStream(h, 1, NULL, &sz);
+    ok(r == ERROR_SUCCESS, "failed to read stream\n");
+    ok(sz==26,"couldn't get size of stream");
+    sz = 0;
+    r = MsiRecordReadStream(h, 1, buf, &sz);
+    ok(r == ERROR_SUCCESS, "failed to read stream\n");
+    ok(sz==0,"short read");
+    sz = sizeof buf;
+    r = MsiRecordReadStream(h, 1, buf, &sz);
+    ok(r == ERROR_SUCCESS, "failed to read stream\n");
+    ok(sz==sizeof buf,"short read");
+    ok(!strncmp(buf,"abcdefghij",10), "read the wrong thing\n");
+    sz = sizeof buf;
+    r = MsiRecordReadStream(h, 1, buf, &sz);
+    ok(r == ERROR_SUCCESS, "failed to read stream\n");
+    ok(sz==sizeof buf,"short read");
+    ok(!strncmp(buf,"klmnopqrst",10), "read the wrong thing\n");
+    memset(buf,0,sizeof buf);
+    sz = sizeof buf;
+    r = MsiRecordReadStream(h, 1, buf, &sz);
+    ok(r == ERROR_SUCCESS, "failed to read stream\n");
+    ok(sz==6,"short read");
+    ok(!strcmp(buf,"uvwxyz"), "read the wrong thing\n");
+    memset(buf,0,sizeof buf);
+    sz = sizeof buf;
+    r = MsiRecordReadStream(h, 1, buf, &sz);
+    ok(r == ERROR_SUCCESS, "failed to read stream\n");
+    ok(sz==0,"size non-zero at end of stream\n");
+    ok(buf[0]==0, "read something at end of the stream\n");
+    r = MsiRecordSetStream(h, 1, NULL);
+    ok(r == ERROR_SUCCESS, "failed to reset stream\n");
+    sz = 0;
+    r = MsiRecordReadStream(h, 1, NULL, &sz);
+    ok(r == ERROR_SUCCESS, "bytes left wrong after reset\n");
+    ok(sz==26,"couldn't get size of stream\n");
+
+    /* now close the stream record */
     r = MsiCloseHandle(h);
     ok(r == ERROR_SUCCESS, "Failed to close handle\n");
 }


More information about the wine-patches mailing list