summaryrefslogtreecommitdiffstats
path: root/libmsi/record.c
diff options
context:
space:
mode:
authorPaolo Bonzini <pbonzini@redhat.com>2012-10-22 10:18:57 +0200
committerPaolo Bonzini <pbonzini@redhat.com>2012-10-22 10:51:11 +0200
commiteb59fe88595685f56db50540ec5626c7c24c454a (patch)
treecf12783c8fdbab451a1f3afdea78ee98b00c9de4 /libmsi/record.c
downloadmsitools-eb59fe88595685f56db50540ec5626c7c24c454a.tar.gz
msitools-eb59fe88595685f56db50540ec5626c7c24c454a.tar.xz
msitools-eb59fe88595685f56db50540ec5626c7c24c454a.zip
initial commit
Diffstat (limited to 'libmsi/record.c')
-rw-r--r--libmsi/record.c1066
1 files changed, 1066 insertions, 0 deletions
diff --git a/libmsi/record.c b/libmsi/record.c
new file mode 100644
index 0000000..1dace78
--- /dev/null
+++ b/libmsi/record.c
@@ -0,0 +1,1066 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002-2004 Mike McCormack for CodeWeavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#define COBJMACROS
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "objidl.h"
+#include "winnls.h"
+#include "ole2.h"
+
+#include "winreg.h"
+#include "shlwapi.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msidb);
+
+#define MSIFIELD_NULL 0
+#define MSIFIELD_INT 1
+#define MSIFIELD_WSTR 3
+#define MSIFIELD_STREAM 4
+#define MSIFIELD_INTPTR 5
+
+static void MSI_FreeField( MSIFIELD *field )
+{
+ switch( field->type )
+ {
+ case MSIFIELD_NULL:
+ case MSIFIELD_INT:
+ case MSIFIELD_INTPTR:
+ break;
+ case MSIFIELD_WSTR:
+ msi_free( field->u.szwVal);
+ break;
+ case MSIFIELD_STREAM:
+ IStream_Release( field->u.stream );
+ break;
+ default:
+ ERR("Invalid field type %d\n", field->type);
+ }
+}
+
+void MSI_CloseRecord( MSIOBJECTHDR *arg )
+{
+ MSIRECORD *rec = (MSIRECORD *) arg;
+ UINT i;
+
+ for( i=0; i<=rec->count; i++ )
+ MSI_FreeField( &rec->fields[i] );
+}
+
+MSIRECORD *MSI_CreateRecord( UINT cParams )
+{
+ MSIRECORD *rec;
+ UINT len;
+
+ TRACE("%d\n", cParams);
+
+ if( cParams>65535 )
+ return NULL;
+
+ len = sizeof (MSIRECORD) + sizeof (MSIFIELD)*cParams;
+ rec = alloc_msiobject( MSIHANDLETYPE_RECORD, len, MSI_CloseRecord );
+ if( rec )
+ rec->count = cParams;
+ return rec;
+}
+
+MSIHANDLE WINAPI MsiCreateRecord( UINT cParams )
+{
+ MSIRECORD *rec;
+ MSIHANDLE ret = 0;
+
+ TRACE("%d\n", cParams);
+
+ rec = MSI_CreateRecord( cParams );
+ if( rec )
+ {
+ ret = alloc_msihandle( &rec->hdr );
+ msiobj_release( &rec->hdr );
+ }
+ return ret;
+}
+
+UINT MSI_RecordGetFieldCount( const MSIRECORD *rec )
+{
+ return rec->count;
+}
+
+UINT WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d\n", handle );
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return -1;
+
+ msiobj_lock( &rec->hdr );
+ ret = MSI_RecordGetFieldCount( rec );
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+
+ return ret;
+}
+
+static BOOL string2intW( LPCWSTR str, int *out )
+{
+ int x = 0;
+ LPCWSTR p = str;
+
+ if( *p == '-' ) /* skip the minus sign */
+ p++;
+ while ( *p )
+ {
+ if( (*p < '0') || (*p > '9') )
+ return FALSE;
+ x *= 10;
+ x += (*p - '0');
+ p++;
+ }
+
+ if( str[0] == '-' ) /* check if it's negative */
+ x = -x;
+ *out = x;
+
+ return TRUE;
+}
+
+UINT MSI_RecordCopyField( MSIRECORD *in_rec, UINT in_n,
+ MSIRECORD *out_rec, UINT out_n )
+{
+ UINT r = ERROR_SUCCESS;
+
+ msiobj_lock( &in_rec->hdr );
+
+ if ( in_n > in_rec->count || out_n > out_rec->count )
+ r = ERROR_FUNCTION_FAILED;
+ else if ( in_rec != out_rec || in_n != out_n )
+ {
+ LPWSTR str;
+ MSIFIELD *in, *out;
+
+ in = &in_rec->fields[in_n];
+ out = &out_rec->fields[out_n];
+
+ switch ( in->type )
+ {
+ case MSIFIELD_NULL:
+ break;
+ case MSIFIELD_INT:
+ out->u.iVal = in->u.iVal;
+ break;
+ case MSIFIELD_INTPTR:
+ out->u.pVal = in->u.pVal;
+ break;
+ case MSIFIELD_WSTR:
+ str = strdupW( in->u.szwVal );
+ if ( !str )
+ r = ERROR_OUTOFMEMORY;
+ else
+ out->u.szwVal = str;
+ break;
+ case MSIFIELD_STREAM:
+ IStream_AddRef( in->u.stream );
+ out->u.stream = in->u.stream;
+ break;
+ default:
+ ERR("invalid field type %d\n", in->type);
+ }
+ if (r == ERROR_SUCCESS)
+ out->type = in->type;
+ }
+
+ msiobj_unlock( &in_rec->hdr );
+
+ return r;
+}
+
+INT_PTR MSI_RecordGetIntPtr( MSIRECORD *rec, UINT iField )
+{
+ int ret;
+
+ TRACE( "%p %d\n", rec, iField );
+
+ if( iField > rec->count )
+ return MININT_PTR;
+
+ switch( rec->fields[iField].type )
+ {
+ case MSIFIELD_INT:
+ return rec->fields[iField].u.iVal;
+ case MSIFIELD_INTPTR:
+ return rec->fields[iField].u.pVal;
+ case MSIFIELD_WSTR:
+ if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
+ return ret;
+ return MININT_PTR;
+ default:
+ break;
+ }
+
+ return MININT_PTR;
+}
+
+int MSI_RecordGetInteger( MSIRECORD *rec, UINT iField)
+{
+ int ret = 0;
+
+ TRACE("%p %d\n", rec, iField );
+
+ if( iField > rec->count )
+ return MSI_NULL_INTEGER;
+
+ switch( rec->fields[iField].type )
+ {
+ case MSIFIELD_INT:
+ return rec->fields[iField].u.iVal;
+ case MSIFIELD_INTPTR:
+ return rec->fields[iField].u.pVal;
+ case MSIFIELD_WSTR:
+ if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
+ return ret;
+ return MSI_NULL_INTEGER;
+ default:
+ break;
+ }
+
+ return MSI_NULL_INTEGER;
+}
+
+int WINAPI MsiRecordGetInteger( MSIHANDLE handle, UINT iField)
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d %d\n", handle, iField );
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return MSI_NULL_INTEGER;
+
+ msiobj_lock( &rec->hdr );
+ ret = MSI_RecordGetInteger( rec, iField );
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+
+ return ret;
+}
+
+UINT WINAPI MsiRecordClearData( MSIHANDLE handle )
+{
+ MSIRECORD *rec;
+ UINT i;
+
+ TRACE("%d\n", handle );
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+
+ msiobj_lock( &rec->hdr );
+ for( i=0; i<=rec->count; i++)
+ {
+ MSI_FreeField( &rec->fields[i] );
+ rec->fields[i].type = MSIFIELD_NULL;
+ rec->fields[i].u.iVal = 0;
+ }
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+
+ return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordSetIntPtr( MSIRECORD *rec, UINT iField, INT_PTR pVal )
+{
+ TRACE("%p %u %ld\n", rec, iField, pVal);
+
+ if( iField > rec->count )
+ return ERROR_INVALID_PARAMETER;
+
+ MSI_FreeField( &rec->fields[iField] );
+ rec->fields[iField].type = MSIFIELD_INTPTR;
+ rec->fields[iField].u.pVal = pVal;
+
+ return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordSetInteger( MSIRECORD *rec, UINT iField, int iVal )
+{
+ TRACE("%p %u %d\n", rec, iField, iVal);
+
+ if( iField > rec->count )
+ return ERROR_INVALID_PARAMETER;
+
+ MSI_FreeField( &rec->fields[iField] );
+ rec->fields[iField].type = MSIFIELD_INT;
+ rec->fields[iField].u.iVal = iVal;
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, UINT iField, int iVal )
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d %u %d\n", handle, iField, iVal);
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+
+ msiobj_lock( &rec->hdr );
+ ret = MSI_RecordSetInteger( rec, iField, iVal );
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+BOOL MSI_RecordIsNull( MSIRECORD *rec, UINT iField )
+{
+ BOOL r = TRUE;
+
+ TRACE("%p %d\n", rec, iField );
+
+ r = ( iField > rec->count ) ||
+ ( rec->fields[iField].type == MSIFIELD_NULL );
+
+ return r;
+}
+
+BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, UINT iField )
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d %d\n", handle, iField );
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return 0;
+ msiobj_lock( &rec->hdr );
+ ret = MSI_RecordIsNull( rec, iField );
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+ return ret;
+
+}
+
+UINT MSI_RecordGetStringA(MSIRECORD *rec, UINT iField,
+ LPSTR szValue, LPDWORD pcchValue)
+{
+ UINT len=0, ret;
+ CHAR buffer[16];
+
+ TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
+
+ if( iField > rec->count )
+ {
+ if ( szValue && *pcchValue > 0 )
+ szValue[0] = 0;
+
+ *pcchValue = 0;
+ return ERROR_SUCCESS;
+ }
+
+ ret = ERROR_SUCCESS;
+ switch( rec->fields[iField].type )
+ {
+ case MSIFIELD_INT:
+ wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
+ len = lstrlenA( buffer );
+ if (szValue)
+ lstrcpynA(szValue, buffer, *pcchValue);
+ break;
+ case MSIFIELD_WSTR:
+ len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
+ NULL, 0 , NULL, NULL);
+ if (szValue)
+ WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
+ szValue, *pcchValue, NULL, NULL);
+ if( szValue && *pcchValue && len>*pcchValue )
+ szValue[*pcchValue-1] = 0;
+ if( len )
+ len--;
+ break;
+ case MSIFIELD_NULL:
+ if( szValue && *pcchValue > 0 )
+ szValue[0] = 0;
+ break;
+ default:
+ ret = ERROR_INVALID_PARAMETER;
+ break;
+ }
+
+ if( szValue && *pcchValue <= len )
+ ret = ERROR_MORE_DATA;
+ *pcchValue = len;
+
+ return ret;
+}
+
+UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, UINT iField,
+ LPSTR szValue, LPDWORD pcchValue)
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+ msiobj_lock( &rec->hdr );
+ ret = MSI_RecordGetStringA( rec, iField, szValue, pcchValue);
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+const WCHAR *MSI_RecordGetString( const MSIRECORD *rec, UINT iField )
+{
+ if( iField > rec->count )
+ return NULL;
+
+ if( rec->fields[iField].type != MSIFIELD_WSTR )
+ return NULL;
+
+ return rec->fields[iField].u.szwVal;
+}
+
+UINT MSI_RecordGetStringW(MSIRECORD *rec, UINT iField,
+ LPWSTR szValue, LPDWORD pcchValue)
+{
+ UINT len=0, ret;
+ WCHAR buffer[16];
+ static const WCHAR szFormat[] = { '%','d',0 };
+
+ TRACE("%p %d %p %p\n", rec, iField, szValue, pcchValue);
+
+ if( iField > rec->count )
+ {
+ if ( szValue && *pcchValue > 0 )
+ szValue[0] = 0;
+
+ *pcchValue = 0;
+ return ERROR_SUCCESS;
+ }
+
+ ret = ERROR_SUCCESS;
+ switch( rec->fields[iField].type )
+ {
+ case MSIFIELD_INT:
+ wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
+ len = lstrlenW( buffer );
+ if (szValue)
+ lstrcpynW(szValue, buffer, *pcchValue);
+ break;
+ case MSIFIELD_WSTR:
+ len = lstrlenW( rec->fields[iField].u.szwVal );
+ if (szValue)
+ lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue);
+ break;
+ case MSIFIELD_NULL:
+ if( szValue && *pcchValue > 0 )
+ szValue[0] = 0;
+ break;
+ default:
+ break;
+ }
+
+ if( szValue && *pcchValue <= len )
+ ret = ERROR_MORE_DATA;
+ *pcchValue = len;
+
+ return ret;
+}
+
+UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, UINT iField,
+ LPWSTR szValue, LPDWORD pcchValue)
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d %d %p %p\n", handle, iField, szValue, pcchValue);
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+
+ msiobj_lock( &rec->hdr );
+ ret = MSI_RecordGetStringW( rec, iField, szValue, pcchValue );
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+static UINT msi_get_stream_size( IStream *stm )
+{
+ STATSTG stat;
+ HRESULT r;
+
+ r = IStream_Stat( stm, &stat, STATFLAG_NONAME );
+ if( FAILED(r) )
+ return 0;
+ return stat.cbSize.QuadPart;
+}
+
+static UINT MSI_RecordDataSize(MSIRECORD *rec, UINT iField)
+{
+ TRACE("%p %d\n", rec, iField);
+
+ if( iField > rec->count )
+ return 0;
+
+ switch( rec->fields[iField].type )
+ {
+ case MSIFIELD_INT:
+ return sizeof (INT);
+ case MSIFIELD_WSTR:
+ return lstrlenW( rec->fields[iField].u.szwVal );
+ case MSIFIELD_NULL:
+ break;
+ case MSIFIELD_STREAM:
+ return msi_get_stream_size( rec->fields[iField].u.stream );
+ }
+ return 0;
+}
+
+UINT WINAPI MsiRecordDataSize(MSIHANDLE handle, UINT iField)
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d %d\n", handle, iField);
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return 0;
+ msiobj_lock( &rec->hdr );
+ ret = MSI_RecordDataSize( rec, iField);
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+static UINT MSI_RecordSetStringA( MSIRECORD *rec, UINT iField, LPCSTR szValue )
+{
+ LPWSTR str;
+
+ TRACE("%p %d %s\n", rec, iField, debugstr_a(szValue));
+
+ if( iField > rec->count )
+ return ERROR_INVALID_FIELD;
+
+ MSI_FreeField( &rec->fields[iField] );
+ if( szValue && szValue[0] )
+ {
+ str = strdupAtoW( szValue );
+ rec->fields[iField].type = MSIFIELD_WSTR;
+ rec->fields[iField].u.szwVal = str;
+ }
+ else
+ {
+ rec->fields[iField].type = MSIFIELD_NULL;
+ rec->fields[iField].u.szwVal = NULL;
+ }
+
+ return 0;
+}
+
+UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, UINT iField, LPCSTR szValue )
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d %d %s\n", handle, iField, debugstr_a(szValue));
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+ msiobj_lock( &rec->hdr );
+ ret = MSI_RecordSetStringA( rec, iField, szValue );
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+UINT MSI_RecordSetStringW( MSIRECORD *rec, UINT iField, LPCWSTR szValue )
+{
+ LPWSTR str;
+
+ TRACE("%p %d %s\n", rec, iField, debugstr_w(szValue));
+
+ if( iField > rec->count )
+ return ERROR_INVALID_FIELD;
+
+ MSI_FreeField( &rec->fields[iField] );
+
+ if( szValue && szValue[0] )
+ {
+ str = strdupW( szValue );
+ rec->fields[iField].type = MSIFIELD_WSTR;
+ rec->fields[iField].u.szwVal = str;
+ }
+ else
+ {
+ rec->fields[iField].type = MSIFIELD_NULL;
+ rec->fields[iField].u.szwVal = NULL;
+ }
+
+ return 0;
+}
+
+UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, UINT iField, LPCWSTR szValue )
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d %d %s\n", handle, iField, debugstr_w(szValue));
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+
+ msiobj_lock( &rec->hdr );
+ ret = MSI_RecordSetStringW( rec, iField, szValue );
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+/* read the data in a file into an IStream */
+static 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, %d bytes into IStream %p\n", debugstr_w(szFile), sz, *pstm);
+
+ return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordSetStream(MSIRECORD *rec, UINT iField, IStream *stream)
+{
+ if ( (iField == 0) || (iField > rec->count) )
+ return ERROR_INVALID_PARAMETER;
+
+ MSI_FreeField( &rec->fields[iField] );
+ rec->fields[iField].type = MSIFIELD_STREAM;
+ rec->fields[iField].u.stream = stream;
+
+ return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordSetStreamFromFileW(MSIRECORD *rec, UINT 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_RecordSetStream(rec, iField, stm);
+ }
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, UINT iField, LPCSTR szFilename)
+{
+ LPWSTR wstr = NULL;
+ UINT ret;
+
+ TRACE("%d %d %s\n", hRecord, iField, debugstr_a(szFilename));
+
+ if( szFilename )
+ {
+ wstr = strdupAtoW( szFilename );
+ if( !wstr )
+ return ERROR_OUTOFMEMORY;
+ }
+ ret = MsiRecordSetStreamW(hRecord, iField, wstr);
+ msi_free(wstr);
+
+ return ret;
+}
+
+UINT WINAPI MsiRecordSetStreamW(MSIHANDLE handle, UINT iField, LPCWSTR szFilename)
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d %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_RecordSetStreamFromFileW( rec, iField, szFilename );
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+UINT MSI_RecordReadStream(MSIRECORD *rec, UINT iField, char *buf, LPDWORD sz)
+{
+ ULONG count;
+ HRESULT r;
+ IStream *stm;
+
+ TRACE("%p %d %p %p\n", rec, iField, buf, sz);
+
+ if( !sz )
+ return ERROR_INVALID_PARAMETER;
+
+ if( iField > rec->count)
+ return ERROR_INVALID_PARAMETER;
+
+ if ( rec->fields[iField].type == MSIFIELD_NULL )
+ {
+ *sz = 0;
+ return ERROR_INVALID_DATA;
+ }
+
+ if( rec->fields[iField].type != MSIFIELD_STREAM )
+ return ERROR_INVALID_DATATYPE;
+
+ stm = rec->fields[iField].u.stream;
+ if( !stm )
+ return ERROR_INVALID_PARAMETER;
+
+ /* if there's no buffer pointer, calculate the length to the end */
+ if( !buf )
+ {
+ LARGE_INTEGER ofs;
+ ULARGE_INTEGER end, cur;
+
+ ofs.QuadPart = cur.QuadPart = 0;
+ end.QuadPart = 0;
+ IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
+ IStream_Seek( stm, ofs, STREAM_SEEK_END, &end );
+ ofs.QuadPart = cur.QuadPart;
+ IStream_Seek( stm, ofs, STREAM_SEEK_SET, &cur );
+ *sz = end.QuadPart - cur.QuadPart;
+
+ return ERROR_SUCCESS;
+ }
+
+ /* read the data */
+ count = 0;
+ r = IStream_Read( stm, buf, *sz, &count );
+ if( FAILED( r ) )
+ {
+ *sz = 0;
+ return ERROR_FUNCTION_FAILED;
+ }
+
+ *sz = count;
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, UINT iField, char *buf, LPDWORD sz)
+{
+ MSIRECORD *rec;
+ UINT ret;
+
+ TRACE("%d %d %p %p\n", handle, iField, buf, sz);
+
+ rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+ if( !rec )
+ return ERROR_INVALID_HANDLE;
+ msiobj_lock( &rec->hdr );
+ ret = MSI_RecordReadStream( rec, iField, buf, sz );
+ msiobj_unlock( &rec->hdr );
+ msiobj_release( &rec->hdr );
+ return ret;
+}
+
+UINT MSI_RecordSetIStream( MSIRECORD *rec, UINT iField, IStream *stm )
+{
+ TRACE("%p %d %p\n", rec, iField, stm);
+
+ if( iField > rec->count )
+ return ERROR_INVALID_FIELD;
+
+ MSI_FreeField( &rec->fields[iField] );
+
+ rec->fields[iField].type = MSIFIELD_STREAM;
+ rec->fields[iField].u.stream = stm;
+ IStream_AddRef( stm );
+
+ return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordGetIStream( MSIRECORD *rec, UINT iField, IStream **pstm)
+{
+ TRACE("%p %d %p\n", rec, iField, pstm);
+
+ if( iField > rec->count )
+ return ERROR_INVALID_FIELD;
+
+ if( rec->fields[iField].type != MSIFIELD_STREAM )
+ return ERROR_INVALID_FIELD;
+
+ *pstm = rec->fields[iField].u.stream;
+ IStream_AddRef( *pstm );
+
+ return ERROR_SUCCESS;
+}
+
+static UINT msi_dump_stream_to_file( IStream *stm, LPCWSTR name )
+{
+ ULARGE_INTEGER size;
+ LARGE_INTEGER pos;
+ IStream *out;
+ DWORD stgm;
+ HRESULT r;
+
+ stgm = STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_FAILIFTHERE;
+ r = SHCreateStreamOnFileW( name, stgm, &out );
+ if( FAILED( r ) )
+ return ERROR_FUNCTION_FAILED;
+
+ pos.QuadPart = 0;
+ r = IStream_Seek( stm, pos, STREAM_SEEK_END, &size );
+ if( FAILED( r ) )
+ goto end;
+
+ pos.QuadPart = 0;
+ r = IStream_Seek( stm, pos, STREAM_SEEK_SET, NULL );
+ if( FAILED( r ) )
+ goto end;
+
+ r = IStream_CopyTo( stm, out, size, NULL, NULL );
+
+end:
+ IStream_Release( out );
+ if( FAILED( r ) )
+ return ERROR_FUNCTION_FAILED;
+ return ERROR_SUCCESS;
+}
+
+UINT MSI_RecordStreamToFile( MSIRECORD *rec, UINT iField, LPCWSTR name )
+{
+ IStream *stm = NULL;
+ UINT r;
+
+ TRACE("%p %u %s\n", rec, iField, debugstr_w(name));
+
+ msiobj_lock( &rec->hdr );
+
+ r = MSI_RecordGetIStream( rec, iField, &stm );
+ if( r == ERROR_SUCCESS )
+ {
+ r = msi_dump_stream_to_file( stm, name );
+ IStream_Release( stm );
+ }
+
+ msiobj_unlock( &rec->hdr );
+
+ return r;
+}
+
+MSIRECORD *MSI_CloneRecord(MSIRECORD *rec)
+{
+ MSIRECORD *clone;
+ UINT r, i, count;
+
+ count = MSI_RecordGetFieldCount(rec);
+ clone = MSI_CreateRecord(count);
+ if (!clone)
+ return NULL;
+
+ for (i = 0; i <= count; i++)
+ {
+ if (rec->fields[i].type == MSIFIELD_STREAM)
+ {
+ if (FAILED(IStream_Clone(rec->fields[i].u.stream,
+ &clone->fields[i].u.stream)))
+ {
+ msiobj_release(&clone->hdr);
+ return NULL;
+ }
+ clone->fields[i].type = MSIFIELD_STREAM;
+ }
+ else
+ {
+ r = MSI_RecordCopyField(rec, i, clone, i);
+ if (r != ERROR_SUCCESS)
+ {
+ msiobj_release(&clone->hdr);
+ return NULL;
+ }
+ }
+ }
+
+ return clone;
+}
+
+BOOL MSI_RecordsAreFieldsEqual(MSIRECORD *a, MSIRECORD *b, UINT field)
+{
+ if (a->fields[field].type != b->fields[field].type)
+ return FALSE;
+
+ switch (a->fields[field].type)
+ {
+ case MSIFIELD_NULL:
+ break;
+
+ case MSIFIELD_INT:
+ if (a->fields[field].u.iVal != b->fields[field].u.iVal)
+ return FALSE;
+ break;
+
+ case MSIFIELD_WSTR:
+ if (strcmpW(a->fields[field].u.szwVal, b->fields[field].u.szwVal))
+ return FALSE;
+ break;
+
+ case MSIFIELD_STREAM:
+ default:
+ return FALSE;
+ }
+ return TRUE;
+}
+
+
+BOOL MSI_RecordsAreEqual(MSIRECORD *a, MSIRECORD *b)
+{
+ UINT i;
+
+ if (a->count != b->count)
+ return FALSE;
+
+ for (i = 0; i <= a->count; i++)
+ {
+ if (!MSI_RecordsAreFieldsEqual( a, b, i ))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+WCHAR *msi_dup_record_field( MSIRECORD *rec, INT field )
+{
+ DWORD sz = 0;
+ WCHAR *str;
+ UINT r;
+
+ if (MSI_RecordIsNull( rec, field )) return NULL;
+
+ r = MSI_RecordGetStringW( rec, field, NULL, &sz );
+ if (r != ERROR_SUCCESS)
+ return NULL;
+
+ sz++;
+ str = msi_alloc( sz * sizeof(WCHAR) );
+ if (!str) return NULL;
+ str[0] = 0;
+ r = MSI_RecordGetStringW( rec, field, str, &sz );
+ if (r != ERROR_SUCCESS)
+ {
+ ERR("failed to get string!\n");
+ msi_free( str );
+ return NULL;
+ }
+ return str;
+}