*
* 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
*/
/*
- * Here are helper functions formally in action.c that are used by a variaty of
+ * Here are helper functions formally in action.c that are used by a variety of
* actions and functions.
*/
#include <stdarg.h>
#include "windef.h"
-#include "winbase.h"
-#include "winerror.h"
#include "wine/debug.h"
#include "msipriv.h"
#include "winuser.h"
#include "wine/unicode.h"
-#include "action.h"
+#include "msidefs.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
static const WCHAR cszDatabase[]={'D','A','T','A','B','A','S','E',0};
const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
+const WCHAR cszSOURCEDIR[] = {'S','O','U','R','C','E','D','I','R',0};
const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
const WCHAR cszbs[]={'\\',0};
-
-DWORD build_version_dword(LPCWSTR version_string)
-{
- SHORT major,minor;
- WORD build;
- DWORD rc = 0x00000000;
- LPCWSTR ptr1;
-
- ptr1 = version_string;
-
- if (!ptr1)
- return rc;
- else
- major = atoiW(ptr1);
-
-
- if(ptr1)
- ptr1 = strchrW(ptr1,'.');
- if (ptr1)
- {
- ptr1++;
- minor = atoiW(ptr1);
- }
- else
- minor = 0;
-
- if (ptr1)
- ptr1 = strchrW(ptr1,'.');
-
- if (ptr1)
- {
- ptr1++;
- build = atoiW(ptr1);
- }
- else
- build = 0;
-
- rc = MAKELONG(build,MAKEWORD(minor,major));
- TRACE("%s -> 0x%lx\n",debugstr_w(version_string),rc);
- return rc;
-}
+const WCHAR szLocalSid[] = {'S','-','1','-','5','-','1','8',0};
LPWSTR build_icon_path(MSIPACKAGE *package, LPCWSTR icon_name )
{
return FilePath;
}
-LPWSTR msi_dup_record_field( MSIRECORD *row, INT index )
-{
- return strdupW( MSI_RecordGetString(row,index) );
-}
-
-LPWSTR msi_dup_property(MSIPACKAGE *package, LPCWSTR prop)
+LPWSTR msi_dup_record_field( MSIRECORD *rec, INT field )
{
DWORD sz = 0;
LPWSTR str;
UINT r;
- r = MSI_GetPropertyW(package, prop, NULL, &sz);
- if (r != ERROR_SUCCESS && r != ERROR_MORE_DATA)
+ 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));
- r = MSI_GetPropertyW(package, prop, str, &sz);
+ sz ++;
+ str = msi_alloc( sz * sizeof (WCHAR) );
+ if (!str)
+ return str;
+ str[0] = 0;
+ r = MSI_RecordGetStringW( rec, field, str, &sz );
if (r != ERROR_SUCCESS)
{
- msi_free(str);
- str = NULL;
+ ERR("failed to get string!\n");
+ msi_free( str );
+ return NULL;
}
return str;
}
return NULL;
}
-int track_tempfile( MSIPACKAGE *package, LPCWSTR name, LPCWSTR path )
+int track_tempfile( MSIPACKAGE *package, LPCWSTR path )
{
MSITEMPFILE *temp;
+ TRACE("%s\n", debugstr_w(path));
+
LIST_FOR_EACH_ENTRY( temp, &package->tempfiles, MSITEMPFILE, entry )
- {
- if (lstrcmpW( name, temp->File )==0)
- {
- TRACE("tempfile %s already exists with path %s\n",
- debugstr_w(temp->File), debugstr_w(temp->Path));
- return -1;
- }
- }
+ if (!lstrcmpW( path, temp->Path ))
+ return 0;
temp = msi_alloc_zero( sizeof (MSITEMPFILE) );
if (!temp)
return -1;
list_add_head( &package->tempfiles, &temp->entry );
-
- temp->File = strdupW( name );
temp->Path = strdupW( path );
- TRACE("adding tempfile %s with path %s\n",
- debugstr_w(temp->File), debugstr_w(temp->Path));
-
return 0;
}
MSIFOLDER *get_loaded_folder( MSIPACKAGE *package, LPCWSTR dir )
{
MSIFOLDER *folder;
-
+
LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
{
if (lstrcmpW( dir, folder->Directory )==0)
return NULL;
}
-LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name, BOOL source,
- BOOL set_prop, MSIFOLDER **folder)
+void msi_reset_folders( MSIPACKAGE *package, BOOL source )
{
- MSIFOLDER *f;
- LPWSTR p, path = NULL;
-
- TRACE("Working to resolve %s\n",debugstr_w(name));
-
- if (!name)
- return NULL;
+ MSIFOLDER *folder;
- /* source directories appear to always be at the root */
- if (source)
+ LIST_FOR_EACH_ENTRY( folder, &package->folders, MSIFOLDER, entry )
{
- path = msi_dup_property( package, cszSourceDir );
- if (!path)
+ if ( source )
{
- path = msi_dup_property( package, cszDatabase );
- if (path)
- {
- p = strrchrW(path,'\\');
- if (p)
- *(p+1) = 0;
- }
+ msi_free( folder->ResolvedSource );
+ folder->ResolvedSource = NULL;
+ }
+ else
+ {
+ msi_free( folder->ResolvedTarget );
+ folder->ResolvedTarget = NULL;
}
- if (folder)
- *folder = get_loaded_folder( package, name );
+ }
+}
+
+static LPWSTR get_source_root( MSIPACKAGE *package )
+{
+ LPWSTR path, p;
+
+ path = msi_dup_property( package, cszSourceDir );
+ if (path)
return path;
+
+ path = msi_dup_property( package, cszDatabase );
+ if (path)
+ {
+ p = strrchrW(path,'\\');
+ if (p)
+ *(p+1) = 0;
}
+ return path;
+}
- /* special resolving for Target and Source root dir */
- if (strcmpW(name,cszTargetDir)==0 || strcmpW(name,cszSourceDir)==0)
+/*
+ * clean_spaces_from_path()
+ *
+ * removes spaces from the beginning and end of path segments
+ * removes multiple \\ characters
+ */
+static void clean_spaces_from_path( LPWSTR p )
+{
+ LPWSTR q = p;
+ int n, len = 0;
+
+ while (1)
{
- LPWSTR check_path;
- check_path = msi_dup_property( package, cszTargetDir );
- if (!check_path)
+ /* copy until the end of the string or a space */
+ while (*p != ' ' && (*q = *p))
{
- check_path = msi_dup_property( package, cszRootDrive );
- if (set_prop)
- MSI_SetPropertyW(package,cszTargetDir,check_path);
+ p++, len++;
+ /* reduce many backslashes to one */
+ if (*p != '\\' || *q != '\\')
+ q++;
}
- /* correct misbuilt target dir */
- path = build_directory_name(2, check_path, NULL);
- if (strcmpiW(path,check_path)!=0)
- MSI_SetPropertyW(package,cszTargetDir,path);
- msi_free(check_path);
- if (folder)
- *folder = get_loaded_folder( package, name );
- return path;
+ /* quit at the end of the string */
+ if (!*p)
+ break;
+
+ /* count the number of spaces */
+ n = 0;
+ while (p[n] == ' ')
+ n++;
+
+ /* if it's leading or trailing space, skip it */
+ if ( len == 0 || p[-1] == '\\' || p[n] == '\\' )
+ p += n;
+ else /* copy n spaces */
+ while (n && (*q++ = *p++)) n--;
}
+}
+
+LPWSTR resolve_file_source(MSIPACKAGE *package, MSIFILE *file)
+{
+ LPWSTR p, path;
+
+ TRACE("Working to resolve source of file %s\n", debugstr_w(file->File));
+
+ if (file->IsCompressed)
+ return NULL;
+
+ p = resolve_folder(package, file->Component->Directory,
+ TRUE, FALSE, TRUE, NULL);
+ path = build_directory_name(2, p, file->ShortName);
+
+ if (file->LongName &&
+ GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES)
+ {
+ msi_free(path);
+ path = build_directory_name(2, p, file->LongName);
+ }
+
+ msi_free(p);
+
+ TRACE("file %s source resolves to %s\n", debugstr_w(file->File),
+ debugstr_w(path));
+
+ return path;
+}
+
+LPWSTR resolve_folder(MSIPACKAGE *package, LPCWSTR name, BOOL source,
+ BOOL set_prop, BOOL load_prop, MSIFOLDER **folder)
+{
+ MSIFOLDER *f;
+ LPWSTR p, path = NULL, parent;
+
+ TRACE("Working to resolve %s\n",debugstr_w(name));
+
+ if (!name)
+ return NULL;
+
+ if (!lstrcmpW(name,cszSourceDir))
+ name = cszTargetDir;
f = get_loaded_folder( package, name );
if (!f)
return NULL;
+ /* special resolving for Target and Source root dir */
+ if (!strcmpW(name,cszTargetDir))
+ {
+ if (!f->ResolvedTarget && !f->Property)
+ {
+ LPWSTR check_path;
+ check_path = msi_dup_property( package, cszTargetDir );
+ if (!check_path)
+ {
+ check_path = msi_dup_property( package, cszRootDrive );
+ if (set_prop)
+ MSI_SetPropertyW(package,cszTargetDir,check_path);
+ }
+
+ /* correct misbuilt target dir */
+ path = build_directory_name(2, check_path, NULL);
+ clean_spaces_from_path( path );
+ if (strcmpiW(path,check_path)!=0)
+ MSI_SetPropertyW(package,cszTargetDir,path);
+ msi_free(check_path);
+
+ f->ResolvedTarget = path;
+ }
+
+ if (!f->ResolvedSource)
+ f->ResolvedSource = get_source_root( package );
+ }
+
if (folder)
*folder = f;
- if (f->ResolvedTarget)
+ if (!source && f->ResolvedTarget)
{
path = strdupW( f->ResolvedTarget );
TRACE(" already resolved to %s\n",debugstr_w(path));
return path;
}
- else if (f->Property)
+
+ if (source && f->ResolvedSource)
+ {
+ path = strdupW( f->ResolvedSource );
+ TRACE(" (source)already resolved to %s\n",debugstr_w(path));
+ return path;
+ }
+
+ if (!source && f->Property)
{
path = build_directory_name( 2, f->Property, NULL );
-
+
TRACE(" internally set to %s\n",debugstr_w(path));
if (set_prop)
MSI_SetPropertyW( package, name, path );
return path;
}
- if (f->Parent)
+ if (!source && load_prop && (path = msi_dup_property( package, name )))
{
- LPWSTR parent = f->Parent->Directory;
+ f->ResolvedTarget = strdupW( path );
+ TRACE(" property set to %s\n", debugstr_w(path));
+ return path;
+ }
+
+ if (!f->Parent)
+ return path;
- TRACE(" ! Parent is %s\n", debugstr_w(parent));
+ parent = f->Parent;
- p = resolve_folder(package, parent, source, set_prop, NULL);
+ TRACE(" ! Parent is %s\n", debugstr_w(parent));
+
+ p = resolve_folder(package, parent, source, set_prop, load_prop, NULL);
+ if (!source)
+ {
TRACE(" TargetDefault = %s\n", debugstr_w(f->TargetDefault));
path = build_directory_name( 3, p, f->TargetDefault, NULL );
+ clean_spaces_from_path( path );
f->ResolvedTarget = strdupW( path );
- TRACE(" resolved into %s\n",debugstr_w(path));
+ TRACE("target -> %s\n", debugstr_w(path));
if (set_prop)
MSI_SetPropertyW(package,name,path);
- msi_free(p);
}
+ else
+ {
+ path = NULL;
+
+ if (package->WordCount & msidbSumInfoSourceTypeCompressed)
+ path = get_source_root( package );
+ else if (package->WordCount & msidbSumInfoSourceTypeSFN)
+ path = build_directory_name( 3, p, f->SourceShortPath, NULL );
+ else
+ path = build_directory_name( 3, p, f->SourceLongPath, NULL );
+
+ TRACE("source -> %s\n", debugstr_w(path));
+ f->ResolvedSource = strdupW( path );
+ }
+ msi_free(p);
+
return path;
}
MSI_RecordSetStringW(rec,0,ptr);
MSI_FormatRecordW(package,rec,NULL,&size);
- if (size >= 0)
- {
- size++;
- *data = msi_alloc(size*sizeof(WCHAR));
- if (size > 1)
- MSI_FormatRecordW(package,rec,*data,&size);
- else
- *data[0] = 0;
- msiobj_release( &rec->hdr );
- return sizeof(WCHAR)*size;
- }
+
+ size++;
+ *data = msi_alloc(size*sizeof(WCHAR));
+ if (size > 1)
+ MSI_FormatRecordW(package,rec,*data,&size);
+ else
+ *data[0] = 0;
+
msiobj_release( &rec->hdr );
+ return sizeof(WCHAR)*size;
}
*data = NULL;
return ERROR_SUCCESS;
}
+void msi_free_action_script(MSIPACKAGE *package, UINT script)
+{
+ UINT i;
+ for (i = 0; i < package->script->ActionCount[script]; i++)
+ msi_free(package->script->Actions[script][i]);
+
+ msi_free(package->script->Actions[script]);
+ package->script->Actions[script] = NULL;
+ package->script->ActionCount[script] = 0;
+}
+
static void remove_tracked_tempfiles(MSIPACKAGE* package)
{
struct list *item, *cursor;
list_remove( &temp->entry );
TRACE("deleting temp file %s\n", debugstr_w( temp->Path ));
- DeleteFileW( temp->Path );
- msi_free( temp->File );
+ if (!DeleteFileW( temp->Path ))
+ ERR("failed to delete %s\n", debugstr_w( temp->Path ));
msi_free( temp->Path );
msi_free( temp );
}
{
struct list *item, *cursor;
+ LIST_FOR_EACH_SAFE( item, cursor, &feature->Children )
+ {
+ FeatureList *fl = LIST_ENTRY( item, FeatureList, entry );
+ list_remove( &fl->entry );
+ msi_free( fl );
+ }
+
LIST_FOR_EACH_SAFE( item, cursor, &feature->Components )
{
ComponentList *cl = LIST_ENTRY( item, ComponentList, entry );
msi_free( feature );
}
-void free_extension( MSIEXTENSION *ext )
+static void free_extension( MSIEXTENSION *ext )
{
struct list *item, *cursor;
{
INT i;
struct list *item, *cursor;
-
+
TRACE("Freeing package action data\n");
remove_tracked_tempfiles(package);
MSIFOLDER *folder = LIST_ENTRY( item, MSIFOLDER, entry );
list_remove( &folder->entry );
+ msi_free( folder->Parent );
msi_free( folder->Directory );
msi_free( folder->TargetDefault );
- msi_free( folder->SourceDefault );
+ msi_free( folder->SourceLongPath );
+ msi_free( folder->SourceShortPath );
msi_free( folder->ResolvedTarget );
msi_free( folder->ResolvedSource );
msi_free( folder->Property );
LIST_FOR_EACH_SAFE( item, cursor, &package->components )
{
MSICOMPONENT *comp = LIST_ENTRY( item, MSICOMPONENT, entry );
-
+
list_remove( &comp->entry );
msi_free( comp->Component );
msi_free( comp->ComponentId );
msi_free( file->File );
msi_free( file->FileName );
msi_free( file->ShortName );
+ msi_free( file->LongName );
msi_free( file->Version );
msi_free( file->Language );
- msi_free( file->SourcePath );
msi_free( file->TargetPath );
msi_free( file );
}
msi_free( appid );
}
+ LIST_FOR_EACH_SAFE( item, cursor, &package->sourcelist_info )
+ {
+ MSISOURCELISTINFO *info = LIST_ENTRY( item, MSISOURCELISTINFO, entry );
+
+ list_remove( &info->entry );
+ msi_free( info->value );
+ msi_free( info );
+ }
+
+ LIST_FOR_EACH_SAFE( item, cursor, &package->sourcelist_media )
+ {
+ MSIMEDIADISK *info = LIST_ENTRY( item, MSIMEDIADISK, entry );
+
+ list_remove( &info->entry );
+ msi_free( info->volume_label );
+ msi_free( info->disk_prompt );
+ msi_free( info );
+ }
+
if (package->script)
{
for (i = 0; i < TOTAL_SCRIPTS; i++)
- {
- int j;
- for (j = 0; j < package->script->ActionCount[i]; j++)
- msi_free(package->script->Actions[i][j]);
-
- msi_free(package->script->Actions[i]);
- }
+ msi_free_action_script(package, i);
for (i = 0; i < package->script->UniqueActionsCount; i++)
msi_free(package->script->UniqueActions[i]);
msi_free(package->script);
}
+ if (package->patch)
+ {
+ msi_free(package->patch->patchcode);
+ msi_free(package->patch->transforms);
+ msi_free(package->patch);
+ }
+
+ msi_free(package->BaseURL);
msi_free(package->PackagePath);
msi_free(package->ProductCode);
msi_free(package->ActionFormat);
WCHAR message[1024];
MSIRECORD * row = 0;
DWORD size;
- static const WCHAR szActionData[] =
- {'A','c','t','i','o','n','D','a','t','a',0};
if (!package->LastAction || strcmpW(package->LastAction,action))
{
MSI_ProcessMessage(package, INSTALLMESSAGE_ACTIONDATA, row);
- ControlEvent_FireSubscribedEvent(package,szActionData, row);
-
msiobj_release(&row->hdr);
}
-BOOL ACTION_VerifyComponentForAction( MSICOMPONENT* comp, INSTALLSTATE check )
+BOOL ACTION_VerifyComponentForAction( const MSICOMPONENT* comp, INSTALLSTATE check )
{
if (!comp)
return FALSE;
- if (comp->Installed == check)
- return FALSE;
-
if (comp->ActionRequest == check)
return TRUE;
else
return FALSE;
}
-BOOL ACTION_VerifyFeatureForAction( MSIFEATURE* feature, INSTALLSTATE check )
+BOOL ACTION_VerifyFeatureForAction( const MSIFEATURE* feature, INSTALLSTATE check )
{
if (!feature)
return FALSE;
- if (feature->Installed == check)
- return FALSE;
-
if (feature->ActionRequest == check)
return TRUE;
else
memmove(filename, p+1, (strlenW(p+1)+1)*sizeof(WCHAR));
}
-void reduce_to_shortfilename(WCHAR* filename)
-{
- LPWSTR p = strchrW(filename,'|');
- if (p)
- *p = 0;
-}
-
LPWSTR create_component_advertise_string(MSIPACKAGE* package,
MSICOMPONENT* component, LPCWSTR feature)
{
- GUID clsid;
- WCHAR productid_85[21];
- WCHAR component_85[21];
- /*
- * I have a fair bit of confusion as to when a < is used and when a > is
- * used. I do not think i have it right...
- *
- * Ok it appears that the > is used if there is a guid for the compoenent
- * and the < is used if not.
- */
- static WCHAR fmt1[] = {'%','s','%','s','<',0,0};
- static WCHAR fmt2[] = {'%','s','%','s','>','%','s',0,0};
+ static const WCHAR fmt[] = {'%','s','%','s','%','c','%','s',0};
+ WCHAR productid_85[21], component_85[21];
LPWSTR output = NULL;
DWORD sz = 0;
+ GUID clsid;
- memset(productid_85,0,sizeof(productid_85));
- memset(component_85,0,sizeof(component_85));
+ /* > is used if there is a component GUID and < if not. */
+
+ productid_85[0] = 0;
+ component_85[0] = 0;
CLSIDFromString(package->ProductCode, &clsid);
-
- encode_base85_guid(&clsid,productid_85);
+ encode_base85_guid(&clsid, productid_85);
- CLSIDFromString(component->ComponentId, &clsid);
- encode_base85_guid(&clsid,component_85);
+ if (component)
+ {
+ CLSIDFromString(component->ComponentId, &clsid);
+ encode_base85_guid(&clsid, component_85);
+ }
- TRACE("Doing something with this... %s %s %s\n",
- debugstr_w(productid_85), debugstr_w(feature),
- debugstr_w(component_85));
+ TRACE("prod=%s feat=%s comp=%s\n", debugstr_w(productid_85),
+ debugstr_w(feature), debugstr_w(component_85));
- sz = lstrlenW(productid_85) + lstrlenW(feature);
- if (component)
- sz += lstrlenW(component_85);
+ sz = 20 + lstrlenW(feature) + 20 + 3;
- sz+=3;
- sz *= sizeof(WCHAR);
-
- output = msi_alloc(sz);
- memset(output,0,sz);
+ output = msi_alloc_zero(sz*sizeof(WCHAR));
- if (component)
- sprintfW(output,fmt2,productid_85,feature,component_85);
- else
- sprintfW(output,fmt1,productid_85,feature);
+ sprintfW(output, fmt, productid_85, feature,
+ component?'>':'<', component_85);
return output;
}
-/* update compoennt state based on a feature change */
+/* update component state based on a feature change */
void ACTION_UpdateComponentStates(MSIPACKAGE *package, LPCWSTR szFeature)
{
INSTALLSTATE newstate;
newstate = feature->ActionRequest;
+ if (newstate == INSTALLSTATE_ABSENT)
+ newstate = INSTALLSTATE_UNKNOWN;
+
LIST_FOR_EACH_ENTRY( cl, &feature->Components, ComponentList, entry )
{
MSICOMPONENT* component = cl->component;
continue;
if (newstate == INSTALLSTATE_LOCAL)
- {
- component->ActionRequest = INSTALLSTATE_LOCAL;
- component->Action = INSTALLSTATE_LOCAL;
- }
+ msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
else
{
ComponentList *clist;
MSIFEATURE *f;
- component->ActionRequest = newstate;
- component->Action = newstate;
+ component->hasLocalFeature = FALSE;
+
+ msi_component_set_state(package, component, newstate);
/*if any other feature wants is local we need to set it local*/
LIST_FOR_EACH_ENTRY( f, &package->features, MSIFEATURE, entry )
{
- if ( component->ActionRequest != INSTALLSTATE_LOCAL )
- break;
+ if ( f->ActionRequest != INSTALLSTATE_LOCAL &&
+ f->ActionRequest != INSTALLSTATE_SOURCE )
+ {
+ continue;
+ }
LIST_FOR_EACH_ENTRY( clist, &f->Components, ComponentList, entry )
{
- if ( clist->component == component )
+ if ( clist->component == component &&
+ (f->ActionRequest == INSTALLSTATE_LOCAL ||
+ f->ActionRequest == INSTALLSTATE_SOURCE) )
{
- if (f->ActionRequest == INSTALLSTATE_LOCAL)
+ TRACE("Saved by %s\n", debugstr_w(f->Feature));
+ component->hasLocalFeature = TRUE;
+
+ if (component->Attributes & msidbComponentAttributesOptional)
{
- TRACE("Saved by %s\n", debugstr_w(f->Feature));
- component->ActionRequest = INSTALLSTATE_LOCAL;
- component->Action = INSTALLSTATE_LOCAL;
+ if (f->Attributes & msidbFeatureAttributesFavorSource)
+ msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
+ else
+ msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
}
- break;
+ else if (component->Attributes & msidbComponentAttributesSourceOnly)
+ msi_component_set_state(package, component, INSTALLSTATE_SOURCE);
+ else
+ msi_component_set_state(package, component, INSTALLSTATE_LOCAL);
}
}
}
return ERROR_SUCCESS;
}
-BOOL check_unique_action(MSIPACKAGE *package, LPCWSTR action)
+BOOL check_unique_action(const MSIPACKAGE *package, LPCWSTR action)
{
- INT i;
+ UINT i;
if (!package->script)
return FALSE;
va_end(va);
MSI_FormatRecordW(package,rec,NULL,&size);
- if (size >= 0)
- {
- size++;
- data = msi_alloc(size*sizeof(WCHAR));
- if (size > 1)
- MSI_FormatRecordW(package,rec,data,&size);
- else
- data[0] = 0;
- msiobj_release( &rec->hdr );
- return data;
- }
+ size++;
+ data = msi_alloc(size*sizeof(WCHAR));
+ if (size > 1)
+ MSI_FormatRecordW(package,rec,data,&size);
+ else
+ data[0] = 0;
msiobj_release( &rec->hdr );
- data = NULL;
return data;
}