From 921be0a87cbdee67d10372be248f599d69432dfd Mon Sep 17 00:00:00 2001 From: Mike McCormack Date: Sun, 9 Jan 2005 18:24:15 +0000 Subject: [PATCH] Implement MsiRecordSetStreamA/W and add tests for records containing streams. --- dlls/msi/record.c | 142 ++++++++++++++++++++++++++++++++++++---- dlls/msi/tests/record.c | 90 ++++++++++++++++++++++++- 2 files changed, 218 insertions(+), 14 deletions(-) diff --git a/dlls/msi/record.c b/dlls/msi/record.c index 1877722459..7f20a1aab3 100644 --- a/dlls/msi/record.c +++ b/dlls/msi/record.c @@ -32,6 +32,7 @@ #include "msipriv.h" #include "objidl.h" #include "winnls.h" +#include "ole2.h" #include "query.h" @@ -531,16 +532,133 @@ UINT WINAPI MsiFormatRecordW(MSIHANDLE hInstall, MSIHANDLE hRecord, LPWSTR szRes 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 @@ UINT MSI_RecordReadStream(MSIRECORD *rec, unsigned int iField, char *buf, DWORD 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 ) diff --git a/dlls/msi/tests/record.c b/dlls/msi/tests/record.c index 3d8f70ff07..4f1202f72d 100644 --- a/dlls/msi/tests/record.c +++ b/dlls/msi/tests/record.c @@ -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 @@ void test_msirecord(void) 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 @@ void test_msirecord(void) 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,9 +205,75 @@ void test_msirecord(void) 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"); } START_TEST(record) -- 2.33.8