#include "msipriv.h"
#include "objidl.h"
#include "winnls.h"
+#include "ole2.h"
#include "query.h"
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)
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 )
#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;
MSIHANDLE h;
char buf[10];
const char str[] = "hello";
+ char filename[MAX_PATH];
/* check behaviour with an invalid record */
r = MsiRecordGetFieldCount(0);
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");
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)