mirror of
https://github.com/ApfelTeeSaft/reactos.git
synced 2026-08-31 04:13:37 +00:00
Ivan Leo Puoti <[email protected]> - Fix some wrong prototypes. Thomas Weidenmueller <[email protected]> - Passing NULL as the last two parameters to ReadFile is illegal and actually causes a crash on windows. The attached patch fixes this. Aric Stewart <[email protected]> - Force files to install if the REINSTALL property is set. Mike McCormack <[email protected]> - Some installers don't call the CreateFolders action before the InstallFiles action as MSDN specifies, but it still seems to work, so make sure that we create component directories in the InstallFiles action anyway. - Create component folders in the CreateFolders action. - If an action fails, print out its name as well as the error code. Use %d for error codes so it's easy to match them up to something in winerror.h. - Rename load_dynamic_stringW to msi_dup_record_field to better describe what it does, and replace a few instances with MSI_RecordGetString to avoid allocating memory. - Tidy up the RegisterProduct action a little. - Create a stub function to apply a single table transform and call it where we need to apply transforms. - Enumerate the substorage transforms for any patches that are passed on the command line with PATCH=. Need to implement table_apply_transform() before this will do anything interesting. - Simplify register_progid() and remove a fixed length buffer. - Make enter and escape trigger the default and cancel buttons in dialogs. - Switch back to using IPicture to load images. LoadImage did the resizing for us, but doesn't handle jpeg files and requires us writing a temp file, whereas IPicture handles jpeg files and can load directly from a stream. - Remove unused package parameter of register_progid_base(). - Remove an incorrect comment and check for 0 being an invalid file handle. - Add missing semicolons that caused compile trouble on FreeBSD. - Extract file directly to their target location, bypassing the need to use temporary files and move files. - Put the UI update code for cabinet file into a separate function. - Translate INVALID_HANDLE_VALUE to zero for cabinet handles. - Fix a memory leak in the cabinet extracting code. - Fix passing of NULL pointers to MsiDecomposeDescriptor and add a test. - Fix parameter handling in MsiSetTargetPath, and add a test for it. - Deleted two buggy functions that incorrectly and inefficiently check whether a row already exists in a table, and replaced them with a call to an existing working function that does the same thing correctly. - Fix and test MsiGetProperty and MsiSetProperty. - Add a stub implementation of msi.MsiSetMode. - NULL and empty strings are the same in conditions. - Add a bunch of tests for MsiEvaluateCondition and make them pass. - Fix error handling in MsiEvaluateCondition. - Create the +msidb debug channel for msi database code. - Implement transforms. This still includes some debugging code which can be enabled by setting debug_transform to 1 in the relevant places. - Define NONAMELESSUNION and NONAMELESSSTRUCT for older compilers. - Remove some redundant null pointer checks. - Make sure to unregister all the classes that were registered when msi is unloaded, so we can register again cleanly. - Fix a memory leak. - Implement the RemoveFiles action. - Add a read-only implementation of the SelectionTree control. - Make sure we only CoUninitialize after successfully initializing. Fix the address of the returned IShellLinkDataList interface. - Use an enumeration for file states. - Handle MaskEdit masks containing dashes and other constant characters. - Stub implementation for MsiAdvertiseScriptA/W. - Add a stub for the AllocateRegistrySpace action. - Explicitly check for MSICONDITION_TRUE being returned from MsiEvaluateCondition. - Stub implementation for MsiGetFileHashA/W. - Define MSIDBOPEN_ constants using LPCWSTR when compiling Wine. - Improve MsiUseFeatureEx and MsiGetFeatureState a little, add some simple test cases. - Use msi_get_property_int() in a few more places. - Implement MsiGetFeatureUsageA and MsiUseFeature(Ex)A using their W versions. - Use "static const" rather than "const static" as gcc -W complains about the former. - Add an implementation for MsiGetShortcutTargetA/W. - Don't change the UI level during ExecuteAction. - Return an error in MsiGetComponentPathW if passed a null component. - Remove the unused 1st parameter of ACTION_VerifyComponentForAction. - Fix MsiGetPropertyA/W spec declarations. - Create an internal handle-free api for reading MSI database summary information. svn path=/trunk/; revision=19307
730 lines
18 KiB
C
730 lines
18 KiB
C
/*
|
|
* Implementation of the Microsoft Installer (msi.dll)
|
|
*
|
|
* Copyright 2005 Mike McCormack for CodeWeavers
|
|
* Copyright 2005 Aric Stewart 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
/*
|
|
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/msiformatrecord.asp
|
|
*/
|
|
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
|
|
#define COBJMACROS
|
|
|
|
#include "windef.h"
|
|
#include "winbase.h"
|
|
#include "winerror.h"
|
|
#include "wine/debug.h"
|
|
#include "msi.h"
|
|
#include "msipriv.h"
|
|
#include "winnls.h"
|
|
#include "wine/unicode.h"
|
|
#include "action.h"
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(msi);
|
|
|
|
|
|
static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
|
|
WCHAR** data, DWORD len, MSIRECORD* record,
|
|
BOOL* in_group);
|
|
|
|
|
|
static LPWSTR build_default_format(MSIRECORD* record)
|
|
{
|
|
int i;
|
|
int count;
|
|
LPWSTR rc;
|
|
static const WCHAR fmt[] = {'%','i',':',' ','[','%','i',']',' ',0};
|
|
WCHAR buf[11];
|
|
|
|
count = MSI_RecordGetFieldCount(record);
|
|
|
|
rc = msi_alloc((11*count)*sizeof(WCHAR));
|
|
rc[0] = 0;
|
|
for (i = 1; i <= count; i++)
|
|
{
|
|
sprintfW(buf,fmt,i,i);
|
|
strcatW(rc,buf);
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static const WCHAR* scanW(LPCWSTR buf, WCHAR token, DWORD len)
|
|
{
|
|
DWORD i;
|
|
for (i = 0; i < len; i++)
|
|
if (buf[i] == token)
|
|
return &buf[i];
|
|
return NULL;
|
|
}
|
|
|
|
/* break out helper functions for deformating */
|
|
static LPWSTR deformat_component(MSIPACKAGE* package, LPCWSTR key, DWORD* sz)
|
|
{
|
|
LPWSTR value = NULL;
|
|
MSICOMPONENT *comp;
|
|
|
|
*sz = 0;
|
|
if (!package)
|
|
return NULL;
|
|
|
|
FIXME("component key %s\n", debugstr_w(key));
|
|
comp = get_loaded_component(package,key);
|
|
if (comp)
|
|
{
|
|
value = resolve_folder(package, comp->Directory, FALSE, FALSE, NULL);
|
|
*sz = (strlenW(value)) * sizeof(WCHAR);
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static LPWSTR deformat_file(MSIPACKAGE* package, LPCWSTR key, DWORD* sz,
|
|
BOOL shortname)
|
|
{
|
|
LPWSTR value = NULL;
|
|
MSIFILE *file;
|
|
|
|
*sz = 0;
|
|
|
|
if (!package)
|
|
return NULL;
|
|
|
|
file = get_loaded_file( package, key );
|
|
if (file)
|
|
{
|
|
if (!shortname)
|
|
{
|
|
value = strdupW( file->TargetPath );
|
|
*sz = (strlenW(value)) * sizeof(WCHAR);
|
|
}
|
|
else
|
|
{
|
|
DWORD size = 0;
|
|
size = GetShortPathNameW( file->TargetPath, NULL, 0 );
|
|
|
|
if (size > 0)
|
|
{
|
|
*sz = (size-1) * sizeof (WCHAR);
|
|
size ++;
|
|
value = msi_alloc(size * sizeof(WCHAR));
|
|
GetShortPathNameW( file->TargetPath, value, size );
|
|
}
|
|
else
|
|
{
|
|
ERR("Unable to get ShortPath size (%s)\n",
|
|
debugstr_w( file->TargetPath) );
|
|
value = NULL;
|
|
*sz = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
return value;
|
|
}
|
|
|
|
static LPWSTR deformat_environment(MSIPACKAGE* package, LPCWSTR key,
|
|
DWORD* chunk)
|
|
{
|
|
LPWSTR value = NULL;
|
|
DWORD sz;
|
|
|
|
sz = GetEnvironmentVariableW(key,NULL,0);
|
|
if (sz > 0)
|
|
{
|
|
sz++;
|
|
value = msi_alloc(sz * sizeof(WCHAR));
|
|
GetEnvironmentVariableW(&key[1],value,sz);
|
|
*chunk = (strlenW(value)) * sizeof(WCHAR);
|
|
}
|
|
else
|
|
{
|
|
ERR("Unknown environment variable %s\n", debugstr_w(key));
|
|
*chunk = 0;
|
|
value = NULL;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
|
|
static LPWSTR deformat_NULL(DWORD* chunk)
|
|
{
|
|
LPWSTR value;
|
|
|
|
value = msi_alloc(sizeof(WCHAR)*2);
|
|
value[0] = 0;
|
|
*chunk = sizeof(WCHAR);
|
|
return value;
|
|
}
|
|
|
|
static LPWSTR deformat_escape(LPCWSTR key, DWORD* chunk)
|
|
{
|
|
LPWSTR value;
|
|
|
|
value = msi_alloc(sizeof(WCHAR)*2);
|
|
value[0] = key[0];
|
|
*chunk = sizeof(WCHAR);
|
|
|
|
return value;
|
|
}
|
|
|
|
|
|
static BOOL is_key_number(LPCWSTR key)
|
|
{
|
|
INT index = 0;
|
|
if (key[0] == 0)
|
|
return FALSE;
|
|
|
|
while (isdigitW(key[index])) index++;
|
|
if (key[index] == 0)
|
|
return TRUE;
|
|
else
|
|
return FALSE;
|
|
}
|
|
|
|
static LPWSTR deformat_index(MSIRECORD* record, LPCWSTR key, DWORD* chunk )
|
|
{
|
|
INT index;
|
|
LPWSTR value;
|
|
|
|
index = atoiW(key);
|
|
TRACE("record index %i\n",index);
|
|
value = msi_dup_record_field(record,index);
|
|
if (value)
|
|
*chunk = strlenW(value) * sizeof(WCHAR);
|
|
else
|
|
{
|
|
value = NULL;
|
|
*chunk = 0;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
static LPWSTR deformat_property(MSIPACKAGE* package, LPCWSTR key, DWORD* chunk)
|
|
{
|
|
LPWSTR value;
|
|
|
|
if (!package)
|
|
return NULL;
|
|
|
|
value = msi_dup_property( package, key );
|
|
|
|
if (value)
|
|
*chunk = (strlenW(value)) * sizeof(WCHAR);
|
|
|
|
return value;
|
|
}
|
|
|
|
/*
|
|
* Groups cannot be nested. They are just treated as from { to next }
|
|
*/
|
|
static BOOL find_next_group(LPCWSTR source, DWORD len_remaining,
|
|
LPWSTR *group, LPCWSTR *mark,
|
|
LPCWSTR* mark2)
|
|
{
|
|
int i;
|
|
BOOL found = FALSE;
|
|
|
|
*mark = scanW(source,'{',len_remaining);
|
|
if (!*mark)
|
|
return FALSE;
|
|
|
|
for (i = 1; (*mark - source) + i < len_remaining; i++)
|
|
{
|
|
if ((*mark)[i] == '}')
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (! found)
|
|
return FALSE;
|
|
|
|
*mark2 = &(*mark)[i];
|
|
|
|
i = *mark2 - *mark;
|
|
*group = msi_alloc(i*sizeof(WCHAR));
|
|
|
|
i -= 1;
|
|
memcpy(*group,&(*mark)[1],i*sizeof(WCHAR));
|
|
(*group)[i] = 0;
|
|
|
|
TRACE("Found group %s\n",debugstr_w(*group));
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
static BOOL find_next_outermost_key(LPCWSTR source, DWORD len_remaining,
|
|
LPWSTR *key, LPCWSTR *mark, LPCWSTR* mark2,
|
|
BOOL *nested)
|
|
{
|
|
INT count = 0;
|
|
INT total_count = 0;
|
|
int i;
|
|
|
|
*mark = scanW(source,'[',len_remaining);
|
|
if (!*mark)
|
|
return FALSE;
|
|
|
|
count = 1;
|
|
total_count = 1;
|
|
*nested = FALSE;
|
|
for (i = 1; (*mark - source) + i < len_remaining && count > 0; i++)
|
|
{
|
|
if ((*mark)[i] == '[' && (*mark)[i-1] != '\\')
|
|
{
|
|
count ++;
|
|
total_count ++;
|
|
*nested = TRUE;
|
|
}
|
|
else if ((*mark)[i] == ']' && (*mark)[i-1] != '\\')
|
|
{
|
|
count --;
|
|
}
|
|
}
|
|
|
|
if (count > 0)
|
|
return FALSE;
|
|
|
|
*mark2 = &(*mark)[i-1];
|
|
|
|
i = *mark2 - *mark;
|
|
*key = msi_alloc(i*sizeof(WCHAR));
|
|
/* do not have the [] in the key */
|
|
i -= 1;
|
|
memcpy(*key,&(*mark)[1],i*sizeof(WCHAR));
|
|
(*key)[i] = 0;
|
|
|
|
TRACE("Found Key %s\n",debugstr_w(*key));
|
|
return TRUE;
|
|
}
|
|
|
|
static LPWSTR deformat_group(MSIPACKAGE* package, LPWSTR group, DWORD len,
|
|
MSIRECORD* record, DWORD* size)
|
|
{
|
|
LPWSTR value = NULL;
|
|
LPCWSTR mark, mark2;
|
|
LPWSTR key;
|
|
BOOL nested;
|
|
INT failcount;
|
|
static const WCHAR fmt[] = {'{','%','s','}',0};
|
|
UINT sz;
|
|
|
|
if (!group || group[0] == 0)
|
|
{
|
|
*size = 0;
|
|
return NULL;
|
|
}
|
|
/* if no [] then group is returned as is */
|
|
|
|
if (!find_next_outermost_key(group, len, &key, &mark, &mark2, &nested))
|
|
{
|
|
*size = (len+2)*sizeof(WCHAR);
|
|
value = msi_alloc(*size);
|
|
sprintfW(value,fmt,group);
|
|
/* do not return size of the null at the end */
|
|
*size = (len+1)*sizeof(WCHAR);
|
|
return value;
|
|
}
|
|
|
|
msi_free(key);
|
|
failcount = 0;
|
|
sz = deformat_string_internal(package, group, &value, strlenW(group),
|
|
record, &failcount);
|
|
if (failcount==0)
|
|
{
|
|
*size = sz * sizeof(WCHAR);
|
|
return value;
|
|
}
|
|
else if (failcount < 0)
|
|
{
|
|
LPWSTR v2;
|
|
|
|
v2 = msi_alloc((sz+2)*sizeof(WCHAR));
|
|
v2[0] = '{';
|
|
memcpy(&v2[1],value,sz*sizeof(WCHAR));
|
|
v2[sz+1]='}';
|
|
msi_free(value);
|
|
|
|
*size = (sz+2)*sizeof(WCHAR);
|
|
return v2;
|
|
}
|
|
else
|
|
{
|
|
msi_free(value);
|
|
*size = 0;
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
* len is in WCHARs
|
|
* return is also in WCHARs
|
|
*/
|
|
static DWORD deformat_string_internal(MSIPACKAGE *package, LPCWSTR ptr,
|
|
WCHAR** data, DWORD len, MSIRECORD* record,
|
|
INT* failcount)
|
|
{
|
|
LPCWSTR mark = NULL;
|
|
LPCWSTR mark2 = NULL;
|
|
DWORD size=0;
|
|
DWORD chunk=0;
|
|
LPWSTR key;
|
|
LPWSTR value = NULL;
|
|
DWORD sz;
|
|
LPBYTE newdata = NULL;
|
|
const WCHAR* progress = NULL;
|
|
BOOL nested;
|
|
|
|
if (ptr==NULL)
|
|
{
|
|
TRACE("Deformatting NULL string\n");
|
|
*data = NULL;
|
|
return 0;
|
|
}
|
|
|
|
TRACE("Starting with %s\n",debugstr_wn(ptr,len));
|
|
|
|
/* scan for special characters... fast exit */
|
|
if ((!scanW(ptr,'[',len) || (scanW(ptr,'[',len) && !scanW(ptr,']',len))) &&
|
|
(scanW(ptr,'{',len) && !scanW(ptr,'}',len)))
|
|
{
|
|
/* not formatted */
|
|
*data = msi_alloc((len*sizeof(WCHAR)));
|
|
memcpy(*data,ptr,len*sizeof(WCHAR));
|
|
TRACE("Returning %s\n",debugstr_wn(*data,len));
|
|
return len;
|
|
}
|
|
|
|
progress = ptr;
|
|
|
|
while (progress - ptr < len)
|
|
{
|
|
/* seek out first group if existing */
|
|
if (find_next_group(progress, len - (progress - ptr), &key,
|
|
&mark, &mark2))
|
|
{
|
|
value = deformat_group(package, key, strlenW(key)+1, record,
|
|
&chunk);
|
|
msi_free( key );
|
|
key = NULL;
|
|
nested = FALSE;
|
|
}
|
|
/* formatted string located */
|
|
else if (!find_next_outermost_key(progress, len - (progress - ptr),
|
|
&key, &mark, &mark2, &nested))
|
|
{
|
|
LPBYTE nd2;
|
|
|
|
TRACE("after value %s\n", debugstr_wn((LPWSTR)newdata,
|
|
size/sizeof(WCHAR)));
|
|
chunk = (len - (progress - ptr)) * sizeof(WCHAR);
|
|
TRACE("after chunk is %li + %li\n",size,chunk);
|
|
if (size)
|
|
nd2 = msi_realloc(newdata,(size+chunk));
|
|
else
|
|
nd2 = msi_alloc(chunk);
|
|
|
|
newdata = nd2;
|
|
memcpy(&newdata[size],progress,chunk);
|
|
size+=chunk;
|
|
break;
|
|
}
|
|
|
|
if (mark != progress)
|
|
{
|
|
LPBYTE tgt;
|
|
DWORD old_size = size;
|
|
INT cnt = (mark - progress);
|
|
TRACE("%i (%i) characters before marker\n",cnt,(mark-progress));
|
|
size += cnt * sizeof(WCHAR);
|
|
if (!old_size)
|
|
tgt = msi_alloc(size);
|
|
else
|
|
tgt = msi_realloc(newdata,size);
|
|
newdata = tgt;
|
|
memcpy(&newdata[old_size],progress,(cnt * sizeof(WCHAR)));
|
|
}
|
|
|
|
progress = mark;
|
|
|
|
if (nested)
|
|
{
|
|
TRACE("Nested key... %s\n",debugstr_w(key));
|
|
deformat_string_internal(package, key, &value, strlenW(key)+1,
|
|
record, failcount);
|
|
|
|
msi_free(key);
|
|
key = value;
|
|
}
|
|
|
|
TRACE("Current %s .. %s\n",debugstr_wn((LPWSTR)newdata,
|
|
size/sizeof(WCHAR)),debugstr_w(key));
|
|
|
|
if (!package)
|
|
{
|
|
/* only deformat number indexs */
|
|
if (key && is_key_number(key))
|
|
{
|
|
value = deformat_index(record,key,&chunk);
|
|
if (!chunk && failcount && *failcount >= 0)
|
|
(*failcount)++;
|
|
}
|
|
else
|
|
{
|
|
if (failcount)
|
|
*failcount = -1;
|
|
if(key)
|
|
{
|
|
DWORD keylen = strlenW(key);
|
|
chunk = (keylen + 2)*sizeof(WCHAR);
|
|
value = msi_alloc(chunk);
|
|
value[0] = '[';
|
|
memcpy(&value[1],key,keylen*sizeof(WCHAR));
|
|
value[1+keylen] = ']';
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
sz = 0;
|
|
if (key) switch (key[0])
|
|
{
|
|
case '~':
|
|
value = deformat_NULL(&chunk);
|
|
break;
|
|
case '$':
|
|
value = deformat_component(package,&key[1],&chunk);
|
|
break;
|
|
case '#':
|
|
value = deformat_file(package,&key[1], &chunk, FALSE);
|
|
break;
|
|
case '!': /* should be short path */
|
|
value = deformat_file(package,&key[1], &chunk, TRUE);
|
|
break;
|
|
case '\\':
|
|
value = deformat_escape(&key[1],&chunk);
|
|
break;
|
|
case '%':
|
|
value = deformat_environment(package,&key[1],&chunk);
|
|
break;
|
|
default:
|
|
/* index keys cannot be nested */
|
|
if (is_key_number(key))
|
|
if (!nested)
|
|
value = deformat_index(record,key,&chunk);
|
|
else
|
|
{
|
|
static const WCHAR fmt[] = {'[','%','s',']',0};
|
|
value = msi_alloc(10);
|
|
sprintfW(value,fmt,key);
|
|
chunk = strlenW(value)*sizeof(WCHAR);
|
|
}
|
|
else
|
|
value = deformat_property(package,key,&chunk);
|
|
break;
|
|
}
|
|
}
|
|
|
|
msi_free(key);
|
|
|
|
if (value!=NULL)
|
|
{
|
|
LPBYTE nd2;
|
|
TRACE("value %s, chunk %li size %li\n",debugstr_w((LPWSTR)value),
|
|
chunk, size);
|
|
if (size)
|
|
nd2= msi_realloc(newdata,(size + chunk));
|
|
else
|
|
nd2= msi_alloc(chunk);
|
|
newdata = nd2;
|
|
memcpy(&newdata[size],value,chunk);
|
|
size+=chunk;
|
|
msi_free(value);
|
|
}
|
|
else if (failcount && *failcount >=0 )
|
|
(*failcount)++;
|
|
|
|
progress = mark2+1;
|
|
}
|
|
|
|
TRACE("after everything %s\n",debugstr_wn((LPWSTR)newdata,
|
|
size/sizeof(WCHAR)));
|
|
|
|
*data = (LPWSTR)newdata;
|
|
return size / sizeof(WCHAR);
|
|
}
|
|
|
|
|
|
UINT MSI_FormatRecordW( MSIPACKAGE* package, MSIRECORD* record, LPWSTR buffer,
|
|
DWORD *size )
|
|
{
|
|
LPWSTR deformated;
|
|
LPWSTR rec;
|
|
DWORD len;
|
|
UINT rc = ERROR_INVALID_PARAMETER;
|
|
|
|
TRACE("%p %p %p %li\n",package, record ,buffer, *size);
|
|
|
|
rec = msi_dup_record_field(record,0);
|
|
if (!rec)
|
|
rec = build_default_format(record);
|
|
|
|
TRACE("(%s)\n",debugstr_w(rec));
|
|
|
|
len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
|
|
record, NULL);
|
|
|
|
if (buffer)
|
|
{
|
|
if (*size>len)
|
|
{
|
|
memcpy(buffer,deformated,len*sizeof(WCHAR));
|
|
rc = ERROR_SUCCESS;
|
|
buffer[len] = 0;
|
|
}
|
|
else
|
|
{
|
|
if (*size > 0)
|
|
{
|
|
memcpy(buffer,deformated,(*size)*sizeof(WCHAR));
|
|
buffer[(*size)-1] = 0;
|
|
}
|
|
rc = ERROR_MORE_DATA;
|
|
}
|
|
}
|
|
else
|
|
rc = ERROR_SUCCESS;
|
|
|
|
*size = len;
|
|
|
|
msi_free(rec);
|
|
msi_free(deformated);
|
|
return rc;
|
|
}
|
|
|
|
UINT MSI_FormatRecordA( MSIPACKAGE* package, MSIRECORD* record, LPSTR buffer,
|
|
DWORD *size )
|
|
{
|
|
LPWSTR deformated;
|
|
LPWSTR rec;
|
|
DWORD len,lenA;
|
|
UINT rc = ERROR_INVALID_PARAMETER;
|
|
|
|
TRACE("%p %p %p %li\n",package, record ,buffer, *size);
|
|
|
|
rec = msi_dup_record_field(record,0);
|
|
if (!rec)
|
|
rec = build_default_format(record);
|
|
|
|
TRACE("(%s)\n",debugstr_w(rec));
|
|
|
|
len = deformat_string_internal(package,rec,&deformated,strlenW(rec),
|
|
record, NULL);
|
|
lenA = WideCharToMultiByte(CP_ACP,0,deformated,len,NULL,0,NULL,NULL);
|
|
|
|
if (buffer)
|
|
{
|
|
WideCharToMultiByte(CP_ACP,0,deformated,len,buffer,*size,NULL, NULL);
|
|
if (*size>lenA)
|
|
{
|
|
rc = ERROR_SUCCESS;
|
|
buffer[lenA] = 0;
|
|
}
|
|
else
|
|
{
|
|
rc = ERROR_MORE_DATA;
|
|
buffer[(*size)-1] = 0;
|
|
}
|
|
}
|
|
else
|
|
rc = ERROR_SUCCESS;
|
|
|
|
*size = lenA;
|
|
|
|
msi_free(rec);
|
|
msi_free(deformated);
|
|
return rc;
|
|
}
|
|
|
|
|
|
UINT WINAPI MsiFormatRecordW( MSIHANDLE hInstall, MSIHANDLE hRecord,
|
|
LPWSTR szResult, DWORD *sz )
|
|
{
|
|
UINT r = ERROR_INVALID_HANDLE;
|
|
MSIPACKAGE *package;
|
|
MSIRECORD *record;
|
|
|
|
TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
|
|
|
|
record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
|
|
|
|
if (!record)
|
|
return ERROR_INVALID_HANDLE;
|
|
if (!sz)
|
|
{
|
|
msiobj_release( &record->hdr );
|
|
if (szResult)
|
|
return ERROR_INVALID_PARAMETER;
|
|
else
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
|
|
|
|
r = MSI_FormatRecordW( package, record, szResult, sz );
|
|
msiobj_release( &record->hdr );
|
|
if (package)
|
|
msiobj_release( &package->hdr );
|
|
return r;
|
|
}
|
|
|
|
UINT WINAPI MsiFormatRecordA( MSIHANDLE hInstall, MSIHANDLE hRecord,
|
|
LPSTR szResult, DWORD *sz )
|
|
{
|
|
UINT r = ERROR_INVALID_HANDLE;
|
|
MSIPACKAGE *package = NULL;
|
|
MSIRECORD *record = NULL;
|
|
|
|
TRACE("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
|
|
|
|
record = msihandle2msiinfo( hRecord, MSIHANDLETYPE_RECORD );
|
|
|
|
if (!record)
|
|
return ERROR_INVALID_HANDLE;
|
|
if (!sz)
|
|
{
|
|
msiobj_release( &record->hdr );
|
|
if (szResult)
|
|
return ERROR_INVALID_PARAMETER;
|
|
else
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE );
|
|
|
|
r = MSI_FormatRecordA( package, record, szResult, sz );
|
|
msiobj_release( &record->hdr );
|
|
if (package)
|
|
msiobj_release( &package->hdr );
|
|
return r;
|
|
}
|