MSI: start implementing actions
Mike McCormack
mike at codeweavers.com
Fri Jun 25 15:37:11 CDT 2004
More to come...
ChangeLog:
<aric at codeweavers.com>
* start implementing actions
-------------- next part --------------
diff -ur dlls/msi.old/Makefile.in dlls/msi/Makefile.in
--- dlls/msi.old/Makefile.in 2004-06-25 15:16:44.000000000 -0500
+++ dlls/msi/Makefile.in 2004-06-25 15:18:10.000000000 -0500
@@ -3,10 +3,11 @@
SRCDIR = @srcdir@
VPATH = @srcdir@
MODULE = msi.dll
-IMPORTS = ole32 user32 advapi32 kernel32
+IMPORTS = ole32 user32 advapi32 kernel32 cabinet shell32
EXTRALIBS = -luuid $(LIBUNICODE)
C_SRCS = \
+ action.c \
create.c \
distinct.c \
handle.c \
diff -ur dlls/msi.old/cond.y dlls/msi/cond.y
--- dlls/msi.old/cond.y 2004-06-25 15:16:44.000000000 -0500
+++ dlls/msi/cond.y 2004-06-25 15:24:52.000000000 -0500
@@ -105,6 +105,10 @@
{
$$ = $1 || $3;
}
+ | boolean_term COND_AND expression
+ {
+ $$ = $1 && $3;
+ }
;
boolean_term:
@@ -112,10 +116,6 @@
{
$$ = $1;
}
- | boolean_factor COND_AND term
- {
- $$ = $1 && $3;
- }
;
boolean_factor:
@@ -215,6 +215,21 @@
MsiGetFeatureStateW(cond->hInstall, $2, &install, &action );
$$ = install;
}
+ | identifier
+ {
+ WCHAR buffer[0x100];
+ DWORD sz=0x100;
+ COND_input* cond = (COND_input*) info;
+ /* Lookup the identifier */
+ /* This will not really work until we have write access to the table*/
+ /* HACK ALERT HACK ALERT... */
+ if (get_property(cond->hInstall,$1,buffer) == ERROR_SUCCESS)
+ {
+ $$ = atoiW( buffer);
+ }
+ else
+ $$=0;
+ }
;
identifier:
@@ -302,11 +317,15 @@
static int COND_lex( void *COND_lval, COND_input *cond )
{
WCHAR ch;
+ /* grammer */
+ static const WCHAR NOT[] = {'O','T',' ',0};
+ static const WCHAR AND[] = {'N','D',' ',0};
+ static const WCHAR OR[] = {'R',' ',0};
cond->start = cond->n;
ch = cond->str[cond->n];
- if( !ch )
- return COND_EOF;
+ if( ch == 0 )
+ return 0;
cond->n++;
switch( ch )
@@ -319,13 +338,38 @@
case '?': return COND_QUESTION;
case '%': return COND_PERCENT;
case ' ': return COND_SPACE;
+ case 'N':
+ if (strncmpiW(&cond->str[cond->n],NOT,3)==0)
+ {
+ cond->n+=3;
+ return COND_NOT;
+ }
+ else break;
+ case 'A':
+ if (strncmpiW(&cond->str[cond->n],AND,3)==0)
+ {
+ cond->n+=3;
+ return COND_AND;
+ }
+ else break;
+ case 'O':
+ if (strncmpiW(&cond->str[cond->n],OR,2)==0)
+ {
+ cond->n+=2;
+ return COND_OR;
+ }
+ else break;
}
if( COND_IsAlpha( ch ) )
{
ch = cond->str[cond->n];
while( COND_IsIdent( ch ) )
+ {
ch = cond->str[cond->n++];
+ if (ch==0)
+ cond->n--;
+ }
return COND_IDENT;
}
@@ -348,7 +392,11 @@
len = cond->n - cond->start;
str = HeapAlloc( GetProcessHeap(), 0, (len+1) * sizeof (WCHAR) );
if( str )
+ {
strncpyW( str, &cond->str[cond->start], len );
+ str[len]=0;
+ }
+ TRACE("Got identifier %s\n",debugstr_w(str));
return str;
}
@@ -366,13 +414,16 @@
cond.str = szCondition;
cond.n = 0;
cond.start = 0;
- cond.result = MSICONDITION_ERROR;
+ cond.result = -1;
+ TRACE("Evaluating %s\n",debugstr_w(szCondition));
+
if( !COND_parse( &cond ) )
r = cond.result;
else
r = MSICONDITION_ERROR;
+ TRACE("Evaluates to %i\n",r);
return r;
}
Binary files dlls/msi.old/create.o and dlls/msi/create.o differ
Binary files dlls/msi.old/distinct.o and dlls/msi/distinct.o differ
diff -ur dlls/msi.old/handle.c dlls/msi/handle.c
--- dlls/msi.old/handle.c 2004-06-25 15:16:44.000000000 -0500
+++ dlls/msi/handle.c 2004-06-25 15:17:51.000000000 -0500
@@ -54,6 +54,7 @@
info->magic = MSIHANDLE_MAGIC;
info->type = type;
+ info->refcount = 1;
info->destructor = destroy;
msihandletable[i] = info;
@@ -79,6 +80,26 @@
return &msihandletable[handle][1];
}
+void msihandle_addref(MSIHANDLE handle)
+{
+ MSIHANDLEINFO *info = msihandle2msiinfo(handle, 0);
+
+ TRACE("%lx\n",handle);
+
+ if( !info )
+ return;
+
+ info--;
+
+ if( info->magic != MSIHANDLE_MAGIC )
+ {
+ ERR("Invalid handle!\n");
+ return;
+ }
+
+ info->refcount++;
+}
+
UINT WINAPI MsiCloseHandle(MSIHANDLE handle)
{
MSIHANDLEINFO *info = msihandle2msiinfo(handle, 0);
@@ -96,6 +117,10 @@
return ERROR_INVALID_HANDLE;
}
+ info->refcount--;
+ if (info->refcount > 0)
+ return ERROR_SUCCESS;
+
if( info->destructor )
info->destructor( &info[1] );
diff -ur dlls/msi.old/msi.c dlls/msi/msi.c
--- dlls/msi.old/msi.c 2004-06-25 15:16:44.000000000 -0500
+++ dlls/msi/msi.c 2004-06-25 15:30:20.000000000 -0500
@@ -33,6 +33,7 @@
#include "msipriv.h"
#include "objidl.h"
#include "wincrypt.h"
+#include "wine/unicode.h"
#include "objbase.h"
WINE_DEFAULT_DEBUG_CHANNEL(msi);
@@ -302,18 +303,24 @@
r = RegOpenKeyW( HKEY_LOCAL_MACHINE, szKey, &hKeyUninstall );
if( r != ERROR_SUCCESS )
- return r;
+ return ERROR_UNKNOWN_PRODUCT;
r = RegOpenKeyW( hKeyUninstall, szProduct, &hKeyProduct );
if( r != ERROR_SUCCESS )
+ {
+ r = ERROR_UNKNOWN_PRODUCT;
goto end;
+ }
/* find the size of the path */
type = count = 0;
r = RegQueryValueExW( hKeyProduct, szLocalPackage,
NULL, &type, NULL, &count );
if( r != ERROR_SUCCESS )
+ {
+ r = ERROR_UNKNOWN_PRODUCT;
goto end;
+ }
/* now alloc and fetch the path of the database to open */
path = HeapAlloc( GetProcessHeap(), 0, count );
@@ -323,7 +330,10 @@
r = RegQueryValueExW( hKeyProduct, szLocalPackage,
NULL, &type, (LPBYTE) path, &count );
if( r != ERROR_SUCCESS )
+ {
+ r = ERROR_UNKNOWN_PRODUCT;
goto end;
+ }
r = MsiOpenPackageW( path, phProduct );
@@ -345,8 +355,10 @@
UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage)
{
+ UINT rc;
FIXME("%s %p\n",debugstr_w(szPackage), phPackage);
- return ERROR_CALL_NOT_IMPLEMENTED;
+ rc = MsiOpenDatabaseW(szPackage,MSIDBOPEN_READONLY,phPackage);
+ return rc;
}
UINT WINAPI MsiOpenPackageExA(LPCSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
@@ -426,9 +438,23 @@
UINT WINAPI MsiInstallProductW(LPCWSTR szPackagePath, LPCWSTR szCommandLine)
{
+ MSIHANDLE dbhandle;
+ UINT rc = ERROR_SUCCESS;
+
FIXME("%s %s\n",debugstr_w(szPackagePath), debugstr_w(szCommandLine));
- return ERROR_CALL_NOT_IMPLEMENTED;
+ rc = MsiVerifyPackageW(szPackagePath);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiOpenDatabaseW(szPackagePath,MSIDBOPEN_READONLY,&dbhandle);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ ACTION_DoTopLevelINSTALL(dbhandle, szPackagePath, szCommandLine);
+
+ MsiCloseHandle(dbhandle);
+ return rc;
}
UINT WINAPI MsiConfigureProductA(LPCSTR szProduct, int iInstallLevel, INSTALLSTATE eInstallState)
@@ -511,66 +537,8 @@
return ERROR_CALL_NOT_IMPLEMENTED;
}
-UINT WINAPI MsiGetPropertyA(MSIHANDLE hInstall, LPCSTR szName, LPSTR szValueBuf, DWORD* pchValueBuf)
-{
- LPWSTR szwName = NULL, szwValueBuf = NULL;
- UINT hr = ERROR_INSTALL_FAILURE;
-
- if (0 == hInstall) {
- return ERROR_INVALID_HANDLE;
- }
- if (NULL == szName) {
- return ERROR_INVALID_PARAMETER;
- }
-
- FIXME("%lu %s %lu\n", hInstall, debugstr_a(szName), *pchValueBuf);
-
- if (NULL != szValueBuf && NULL == pchValueBuf) {
- return ERROR_INVALID_PARAMETER;
- }
- if( szName )
- {
- UINT len = MultiByteToWideChar( CP_ACP, 0, szName, -1, NULL, 0 );
- szwName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
- if( !szwName )
- goto end;
- MultiByteToWideChar( CP_ACP, 0, szName, -1, szwName, len );
- } else {
- return ERROR_INVALID_PARAMETER;
- }
- if( szValueBuf )
- {
- szwValueBuf = HeapAlloc( GetProcessHeap(), 0, (*pchValueBuf) * sizeof(WCHAR) );
- if( !szwValueBuf )
- goto end;
- }
-
- hr = MsiGetPropertyW( hInstall, szwName, szwValueBuf, pchValueBuf );
-
- if( ERROR_SUCCESS == hr )
- {
- WideCharToMultiByte(CP_ACP, 0, szwValueBuf, -1, szValueBuf, *pchValueBuf, NULL, NULL);
- }
-end:
- if( szwName )
- HeapFree( GetProcessHeap(), 0, szwName );
- if( szwValueBuf )
- HeapFree( GetProcessHeap(), 0, szwValueBuf );
- return hr;
-}
-UINT WINAPI MsiGetPropertyW(MSIHANDLE hInstall, LPCWSTR szName, LPWSTR szValueBuf, DWORD* pchValueBuf)
-{
- FIXME("%lu %s %lu\n", hInstall, debugstr_w(szName), *pchValueBuf);
- if (0 == hInstall) {
- return ERROR_INVALID_HANDLE;
- }
- if (NULL == szName) {
- return ERROR_INVALID_PARAMETER;
- }
- return ERROR_CALL_NOT_IMPLEMENTED;
-}
UINT WINAPI MsiGetProductInfoA(LPCSTR szProduct, LPCSTR szAttribute, LPSTR szBuffer, DWORD *pcchValueBuf)
{
@@ -715,6 +683,13 @@
return dwUILevel;
}
+INSTALLUI_HANDLERA WINAPI MsiSetExternalUIA(INSTALLUI_HANDLERA puiHandler,
+ DWORD dwMessageFilter, LPVOID pvContext)
+{
+ FIXME("STUB\n");
+ return NULL;
+}
+
UINT WINAPI MsiLoadStringA(HINSTANCE hInstance, UINT uID, LPSTR lpBuffer, int nBufferMax, DWORD e)
{
/*FIXME("%08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e);*/
@@ -1220,3 +1195,148 @@
{
return S_FALSE;
}
+
+/* property code */
+UINT WINAPI MsiSetPropertyA( MSIHANDLE hInstall, LPCSTR szName, LPCSTR szValue)
+{
+ FIXME("STUB until write access is done: (%s %s)\n", szName,
+ szValue);
+
+ if (!hInstall)
+ return ERROR_INVALID_HANDLE;
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiSetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, LPCWSTR szValue)
+{
+ FIXME("STUB until write access is done: (%s %s)\n",debugstr_w(szName),
+ debugstr_w(szValue));
+
+ if (!hInstall)
+ return ERROR_INVALID_HANDLE;
+
+ return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiGetPropertyA(MSIHANDLE hInstall, LPCSTR szName, LPSTR szValueBuf, DWORD* pchValueBuf)
+{
+ LPWSTR szwName = NULL, szwValueBuf = NULL;
+ UINT hr = ERROR_INSTALL_FAILURE;
+
+ if (0 == hInstall) {
+ return ERROR_INVALID_HANDLE;
+ }
+ if (NULL == szName) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ FIXME("%lu %s %lu\n", hInstall, debugstr_a(szName), *pchValueBuf);
+
+ if (NULL != szValueBuf && NULL == pchValueBuf) {
+ return ERROR_INVALID_PARAMETER;
+ }
+ if( szName )
+ {
+ UINT len = MultiByteToWideChar( CP_ACP, 0, szName, -1, NULL, 0 );
+ szwName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
+ if( !szwName )
+ goto end;
+ MultiByteToWideChar( CP_ACP, 0, szName, -1, szwName, len );
+ } else {
+ return ERROR_INVALID_PARAMETER;
+ }
+ if( szValueBuf )
+ {
+ szwValueBuf = HeapAlloc( GetProcessHeap(), 0, (*pchValueBuf) * sizeof(WCHAR) );
+ if( !szwValueBuf )
+ goto end;
+ }
+
+ hr = MsiGetPropertyW( hInstall, szwName, szwValueBuf, pchValueBuf );
+
+ if( ERROR_SUCCESS == hr )
+ {
+ WideCharToMultiByte(CP_ACP, 0, szwValueBuf, -1, szValueBuf, *pchValueBuf, NULL, NULL);
+ }
+
+end:
+ if( szwName )
+ HeapFree( GetProcessHeap(), 0, szwName );
+ if( szwValueBuf )
+ HeapFree( GetProcessHeap(), 0, szwValueBuf );
+
+ return hr;
+}
+
+UINT WINAPI MsiGetPropertyW(MSIHANDLE hInstall, LPCWSTR szName,
+ LPWSTR szValueBuf, DWORD* pchValueBuf)
+{
+ MSIHANDLE view,row;
+ UINT rc;
+ WCHAR Query[1024]=
+ {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' '
+ ,'P','r','o','p','e','r','t','y',' ','w','h','e','r','e',' ','`'
+ ,'P','r','o','p','e','r','t','y','`','=','`',0};
+
+ static const WCHAR szEnd[]={'`',0};
+
+ if (0 == hInstall) {
+ return ERROR_INVALID_HANDLE;
+ }
+ if (NULL == szName) {
+ return ERROR_INVALID_PARAMETER;
+ }
+
+ strcatW(Query,szName);
+ strcatW(Query,szEnd);
+
+ rc = MsiDatabaseOpenViewW(hInstall, Query, &view);
+ if (rc == ERROR_SUCCESS)
+ {
+ DWORD sz;
+ WCHAR value[0x100];
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ rc = MsiViewFetch(view,&row);
+ if (rc == ERROR_SUCCESS)
+ {
+ sz=0x100;
+ rc = MsiRecordGetStringW(row,2,value,&sz);
+ strncpyW(szValueBuf,value,min(sz+1,*pchValueBuf));
+ *pchValueBuf = sz+1;
+ MsiCloseHandle(row);
+ }
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ }
+
+ if (rc == ERROR_SUCCESS)
+ TRACE("returning %s for property %s\n", debugstr_w(szValueBuf),
+ debugstr_w(szName));
+
+ return rc;
+}
+
+
+INT WINAPI MsiProcessMessage( MSIHANDLE hInstall, INSTALLMESSAGE eMessageType,
+ MSIHANDLE hRecord)
+{
+ FIXME("STUB: \n");
+ return ERROR_SUCCESS;
+}
+
+
+MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE hInstall)
+{
+ FIXME("Is this correct?\n");
+ msihandle_addref(hInstall);
+ return hInstall;
+}
diff -ur dlls/msi.old/msi.spec dlls/msi/msi.spec
--- dlls/msi.old/msi.spec 2004-06-25 15:16:44.000000000 -0500
+++ dlls/msi/msi.spec 2004-06-25 15:17:51.000000000 -0500
@@ -46,7 +46,7 @@
46 stdcall MsiEvaluateConditionA(long str)
47 stdcall MsiEvaluateConditionW(long wstr)
48 stub MsiGetLastErrorRecord
-49 stub MsiGetActiveDatabase
+49 stdcall MsiGetActiveDatabase(long)
50 stdcall MsiGetComponentStateA(long str ptr ptr)
51 stdcall MsiGetComponentStateW(long wstr ptr ptr)
52 stub MsiGetDatabaseState
@@ -100,7 +100,7 @@
100 stub MsiPreviewDialogW
101 stub MsiProcessAdvertiseScriptA
102 stub MsiProcessAdvertiseScriptW
-103 stub MsiProcessMessage
+103 stdcall MsiProcessMessage(long long long)
104 stub MsiProvideComponentA
105 stdcall MsiProvideComponentFromDescriptorA(str ptr ptr ptr)
106 stdcall MsiProvideComponentFromDescriptorW(wstr ptr ptr ptr)
@@ -133,7 +133,7 @@
133 stub MsiSequenceW
134 stub MsiSetComponentStateA
135 stub MsiSetComponentStateW
-136 stub MsiSetExternalUIA
+136 stdcall MsiSetExternalUIA(ptr long ptr)
137 stub MsiSetExternalUIW
138 stub MsiSetFeatureStateA
139 stub MsiSetFeatureStateW
@@ -141,8 +141,8 @@
141 stdcall MsiSetInternalUI(long ptr)
142 stub MsiVerifyDiskSpace
143 stub MsiSetMode
-144 stub MsiSetPropertyA
-145 stub MsiSetPropertyW
+144 stdcall MsiSetPropertyA(long str str)
+145 stdcall MsiSetPropertyW(long wstr wstr)
146 stub MsiSetTargetPathA
147 stub MsiSetTargetPathW
148 stdcall MsiSummaryInfoGetPropertyA(long long ptr ptr ptr ptr ptr)
diff -ur dlls/msi.old/msi.spec.def dlls/msi/msi.spec.def
--- dlls/msi.old/msi.spec.def 2004-06-25 15:16:44.000000000 -0500
+++ dlls/msi/msi.spec.def 2004-06-25 15:19:38.000000000 -0500
@@ -39,6 +39,7 @@
MsiEnumProductsW at 8 @45
MsiEvaluateConditionA at 8 @46
MsiEvaluateConditionW at 8 @47
+ MsiGetActiveDatabase at 4 @49
MsiGetComponentStateA at 16 @50
MsiGetComponentStateW at 16 @51
MsiGetFeatureStateA at 16 @57
@@ -63,6 +64,7 @@
MsiOpenPackageW at 8 @94
MsiOpenProductA at 8 @95
MsiOpenProductW at 8 @96
+ MsiProcessMessage at 12 @103
MsiProvideComponentFromDescriptorA at 16 @105
MsiProvideComponentFromDescriptorW at 16 @106
MsiQueryProductStateA at 4 @112
@@ -79,7 +81,10 @@
MsiRecordSetStreamW at 12 @123
MsiRecordSetStringA at 12 @124
MsiRecordSetStringW at 12 @125
+ MsiSetExternalUIA at 12 @136
MsiSetInternalUI at 8 @141
+ MsiSetPropertyA at 12 @144
+ MsiSetPropertyW at 12 @145
MsiSummaryInfoGetPropertyA at 28 @148
MsiSummaryInfoGetPropertyCount at 8 @149
MsiSummaryInfoGetPropertyW at 28 @150
diff -ur dlls/msi.old/msipriv.h dlls/msi/msipriv.h
--- dlls/msi.old/msipriv.h 2004-06-25 15:16:44.000000000 -0500
+++ dlls/msi/msipriv.h 2004-06-25 15:17:51.000000000 -0500
@@ -132,6 +132,7 @@
{
UINT magic;
UINT type;
+ UINT refcount;
msihandledestructor destructor;
struct tagMSIHANDLEINFO *next;
struct tagMSIHANDLEINFO *prev;
@@ -164,6 +165,7 @@
extern void *msihandle2msiinfo(MSIHANDLE handle, UINT type);
MSIHANDLE alloc_msihandle(UINT type, UINT extra, msihandledestructor destroy, void **out);
+void msihandle_addref(MSIHANDLE handle);
/* add this table to the list of cached tables in the database */
extern void add_table(MSIDATABASE *db, MSITABLE *table);
@@ -199,4 +201,9 @@
extern BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name );
+UINT read_raw_stream_data( MSIHANDLE hdb, LPCWSTR stname,
+ USHORT **pdata, UINT *psz );
+UINT ACTION_DoTopLevelINSTALL(MSIHANDLE hPackage, LPCWSTR szPackagePath,
+ LPCWSTR szCommandLine);
+
#endif /* __WINE_MSI_PRIVATE__ */
diff -ur dlls/msi.old/sql.y dlls/msi/sql.y
--- dlls/msi.old/sql.y 2004-06-25 15:16:44.000000000 -0500
+++ dlls/msi/sql.y 2004-06-25 15:17:51.000000000 -0500
@@ -571,7 +571,8 @@
LPWSTR str;
/* if there's quotes, remove them */
- if( (p[0]=='`') && (p[len-1]=='`') )
+ if( ( (p[0]=='`') && (p[len-1]=='`') ) ||
+ ( (p[0]=='\'') && (p[len-1]=='\'') ) )
{
p++;
len -= 2;
diff -ur dlls/msi.old/table.c dlls/msi/table.c
--- dlls/msi.old/table.c 2004-06-25 15:16:44.000000000 -0500
+++ dlls/msi/table.c 2004-06-25 15:17:51.000000000 -0500
@@ -1254,3 +1254,77 @@
return ERROR_SUCCESS;
}
+
+
+UINT read_raw_stream_data( MSIHANDLE hdb, LPCWSTR stname,
+ USHORT **pdata, UINT *psz )
+{
+ HRESULT r;
+ UINT ret = ERROR_FUNCTION_FAILED;
+ VOID *data;
+ ULONG sz, count;
+ IStream *stm = NULL;
+ IStorage *stg = NULL;
+ STATSTG stat;
+ WCHAR encname[0x20];
+ MSIDATABASE *db;
+
+ db = msihandle2msiinfo(hdb, MSIHANDLETYPE_DATABASE );
+
+ if ( !db )
+ return ERROR_INVALID_HANDLE;
+
+ stg = db->storage;
+
+ encode_streamname(FALSE, stname, encname);
+
+ TRACE("%s -> %s\n",debugstr_w(stname),debugstr_w(encname));
+
+ r = IStorage_OpenStream(stg, encname, NULL,
+ STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
+ if( FAILED( r ) )
+ {
+ WARN("open stream failed r = %08lx - empty table?\n",r);
+ return ret;
+ }
+
+ r = IStream_Stat(stm, &stat, STATFLAG_NONAME );
+ if( FAILED( r ) )
+ {
+ ERR("open stream failed r = %08lx!\n",r);
+ goto end;
+ }
+
+ if( stat.cbSize.QuadPart >> 32 )
+ {
+ ERR("Too big!\n");
+ goto end;
+ }
+
+ sz = stat.cbSize.QuadPart;
+ data = HeapAlloc( GetProcessHeap(), 0, sz );
+ if( !data )
+ {
+ ERR("couldn't allocate memory r=%08lx!\n",r);
+ ret = ERROR_NOT_ENOUGH_MEMORY;
+ goto end;
+ }
+
+ r = IStream_Read(stm, data, sz, &count );
+ if( FAILED( r ) || ( count != sz ) )
+ {
+ HeapFree( GetProcessHeap(), 0, data );
+ ERR("read stream failed r = %08lx!\n",r);
+ goto end;
+ }
+
+ *pdata = data;
+ *psz = sz;
+ ret = ERROR_SUCCESS;
+
+end:
+ IStream_Release( stm );
+
+ return ret;
+}
+
--- /dev/null 1994-07-17 18:46:18.000000000 -0500
+++ dlls/msi/action.c 2004-06-25 15:26:24.000000000 -0500
@@ -0,0 +1,1562 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2004 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
+ */
+
+/*
+ * pages i need
+ *
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/installexecutesequence_table.asp
+
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/standard_actions_reference.asp
+
+ */
+
+#include <stdarg.h>
+#include <stdio.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "winreg.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+#include "winuser.h"
+#include "shlobj.h"
+#include "wine/unicode.h"
+
+#define CUSTOM_ACTION_TYPE_MASK 0x3F
+
+/*
+ * These are hacks to get around the inability to write
+ * to the database and the inability to select on string values
+ * once those values are done then it will be possible to do
+ * all this inside the database.
+ */
+
+#define MAX_PROP 1024
+
+typedef struct {
+ WCHAR *prop_name;
+ WCHAR *prop_value;
+ } internal_property;
+
+static internal_property PropTableHack[MAX_PROP];
+static INT PropCount = 0;
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * Prototypes
+ */
+UINT ACTION_PerformAction(MSIHANDLE hPackage, const WCHAR *action);
+static UINT ACTION_CreateFolders(MSIHANDLE hPackage);
+static UINT ACTION_CostFinalize(MSIHANDLE hPackage);
+static UINT ACTION_InstallFiles(MSIHANDLE hPackage);
+static UINT ACTION_DuplicateFiles(MSIHANDLE hPackage);
+static UINT ACTION_WriteRegistryValues(MSIHANDLE hPackage);
+static UINT ACTION_CustomAction(MSIHANDLE hPackage,const WCHAR *action);
+
+static UINT set_property(MSIHANDLE hPackage, const WCHAR* prop,
+ const WCHAR* value);
+UINT get_property(MSIHANDLE hPackage, const WCHAR* prop, WCHAR* value);
+static VOID blitz_propertytable();
+static VOID set_installer_properties(MSIHANDLE hPackage,
+ const WCHAR* szPackagePath,
+ const WCHAR* szCommandLine);
+static DWORD deformat_string(MSIHANDLE hPackage, WCHAR* ptr,WCHAR** data);
+
+/*
+ * consts and values used
+ */
+static const WCHAR cszSourceDir[] = {'S','o','u','r','c','e','D','i','r',0};
+static const WCHAR cszRootDrive[] = {'R','O','O','T','D','R','I','V','E',0};
+static const WCHAR cszTargetDir[] = {'T','A','R','G','E','T','D','I','R',0};
+
+static const WCHAR cszlsb[]={'[',0};
+static const WCHAR cszrsb[]={']',0};
+static const WCHAR cszbs[]={'\\',0};
+
+
+/********************************************************
+ * helper functions to get around current HACKS and such
+ ********************************************************/
+
+static VOID blitz_propertytable()
+{
+ if (PropCount > 0)
+ {
+ int i;
+ TRACE("Clearing %i properties\n",PropCount);
+ for (i = 0; i < PropCount; i++)
+ {
+ HeapFree(GetProcessHeap(), 0, PropTableHack[i].prop_name);
+ HeapFree(GetProcessHeap(), 0, PropTableHack[i].prop_value);
+ }
+ PropCount = 0;
+ }
+}
+
+UINT get_property(MSIHANDLE hPackage, const WCHAR* prop, WCHAR* value)
+{
+ UINT rc = 1;
+ DWORD sz=0x100;
+ int index = 0;
+ WCHAR* pName = PropTableHack[0].prop_name;
+
+ TRACE("Looking for property %s\n",debugstr_w(prop));
+
+ /* prop table hacks take presidence */
+
+ while (pName && strcmpW(pName,prop) && index < PropCount)
+ {
+ index ++;
+ pName = PropTableHack[index].prop_name;
+ }
+
+ if (pName)
+ {
+ strcpyW(value,PropTableHack[index].prop_value);
+ TRACE(" found value %s\n",debugstr_w(value));
+ return 0;
+ }
+
+ rc = MsiGetPropertyW(hPackage,prop,value,&sz);
+
+ if (rc == ERROR_SUCCESS)
+ TRACE(" found value %s\n",debugstr_w(value));
+ else
+ TRACE(" value not found\n");
+
+ return rc;
+}
+
+static UINT set_property(MSIHANDLE hPackage, const WCHAR* prop,
+ const WCHAR* value)
+{
+ /* prop table hacks take precedence */
+ UINT rc;
+ int index = 0;
+ WCHAR* pName = PropTableHack[0].prop_name;
+
+ TRACE("Setting property %s to %s\n",debugstr_w(prop),debugstr_w(value));
+
+ while (pName && strcmpW(pName,prop) && index < PropCount)
+ {
+ index ++;
+ pName = PropTableHack[index].prop_name;
+ }
+
+ if (pName)
+ {
+ strcpyW(PropTableHack[index].prop_value,value);
+ return 0;
+ }
+ else
+ {
+ if (index > MAX_PROP)
+ {
+ ERR("EXCEEDING MAX PROP!!!!\n");
+ return ERROR_FUNCTION_FAILED;
+ }
+ PropTableHack[index].prop_name = HeapAlloc(GetProcessHeap(),0,1024);
+ PropTableHack[index].prop_value= HeapAlloc(GetProcessHeap(),0,1024);
+ strcpyW(PropTableHack[index].prop_name,prop);
+ strcpyW(PropTableHack[index].prop_value,value);
+ PropCount++;
+ return 0;
+ }
+
+ /* currently unreachable */
+ rc = MsiSetPropertyW(hPackage,prop,value);
+ return rc;
+}
+
+/*
+ * There are a whole slew of these we need to set
+ *
+ *
+http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp
+ */
+
+static VOID set_installer_properties(MSIHANDLE hPackage,
+ const WCHAR* szPackagePath,
+ const WCHAR* szCommandLine)
+{
+ WCHAR pth[MAX_PATH];
+ WCHAR *p;
+ WCHAR check[MAX_PATH];
+
+ static const WCHAR OriginalDatabase[] =
+{'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
+ static const WCHAR c_col[] =
+{'C',':','\\',0};
+ static const WCHAR CFF[] =
+{'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0};
+ static const WCHAR PFF[] =
+{'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0};
+ static const WCHAR CADF[] =
+{'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+ static const WCHAR ATF[] =
+{'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0};
+ static const WCHAR ADF[] =
+{'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+ static const WCHAR SF[] =
+{'S','y','s','t','e','m','F','o','l','d','e','r',0};
+ static const WCHAR LADF[] =
+{'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
+ static const WCHAR MPF[] =
+{'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0};
+ static const WCHAR PF[] =
+{'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0};
+ static const WCHAR WF[] =
+{'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
+
+
+/* Not yet set ... but needed by iTunes
+ *
+ static const WCHAR DF[] =
+{'D','e','s','k','t','o','p','F','o','l','d','e','r',0};
+ static const WCHAR FF[] =
+{'F','a','v','o','r','i','t','e','s','F','o','l','d','e','r',0};
+ static const WCHAR FoF[] =
+{'F','o','n','t','s','F','o','l','d','e','r',0};
+PrimaryVolumePath
+ProgramFiles64Folder
+ProgramMenuFolder
+SendToFolder
+StartMenuFolder
+StartupFolder
+System16Folder
+System64Folder
+TempFolder
+TemplateFolder
+ */
+
+/* asked for by iTunes ... but are they installer set?
+ *
+ * GlobalAssemblyCache
+ */
+
+ set_property(hPackage, OriginalDatabase, szPackagePath);
+ set_property(hPackage, cszRootDrive, c_col);
+
+ strcpyW(pth,szPackagePath);
+ p = strrchrW(pth,'\\');
+ if (p)
+ {
+ p++;
+ *p=0;
+ }
+
+ if (get_property(hPackage,cszSourceDir,check) != ERROR_SUCCESS )
+ set_property(hPackage, cszSourceDir, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth);
+ strcatW(pth,cszbs);
+ set_property(hPackage, CFF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth);
+ strcatW(pth,cszbs);
+ set_property(hPackage, PFF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth);
+ strcatW(pth,cszbs);
+ set_property(hPackage, CADF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth);
+ strcatW(pth,cszbs);
+ set_property(hPackage, ATF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth);
+ strcatW(pth,cszbs);
+ set_property(hPackage, ADF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth);
+ strcatW(pth,cszbs);
+ set_property(hPackage, SF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth);
+ strcatW(pth,cszbs);
+ set_property(hPackage, LADF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth);
+ strcatW(pth,cszbs);
+ set_property(hPackage, MPF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth);
+ strcatW(pth,cszbs);
+ set_property(hPackage, PF, pth);
+
+ SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
+ strcatW(pth,cszbs);
+ set_property(hPackage, WF, pth);
+}
+
+
+/****************************************************
+ * TOP level entry points
+ *****************************************************/
+
+UINT ACTION_DoTopLevelINSTALL(MSIHANDLE hPackage, LPCWSTR szPackagePath,
+ LPCWSTR szCommandLine)
+{
+ MSIHANDLE view;
+ UINT rc;
+ static const CHAR *ExecSeqQuery =
+"select * from InstallExecuteSequence where Sequence > 0 order by Sequence";
+
+ FIXME("****We do not do any of the UI level stuff yet***\n");
+
+ /* reset our properties */
+ /* HACK ! */
+ memset (&PropTableHack, sizeof(PropTableHack),0);
+ set_installer_properties(hPackage, szPackagePath, szCommandLine);
+
+ rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
+
+ if (rc == ERROR_SUCCESS)
+ {
+ rc = MsiViewExecute(view, 0);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ goto end;
+ }
+
+ TRACE("Running the actions \n");
+
+ while (1)
+ {
+ WCHAR buffer[0x100];
+ DWORD sz = 0x100;
+ MSIHANDLE row = 0;
+
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ /* check conditions */
+ if (!MsiRecordIsNull(row,2))
+ {
+ sz=0x100;
+ rc = MsiRecordGetStringW(row,2,buffer,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiCloseHandle(row);
+ break;
+ }
+
+ /* this is a hack to skip errors in the condition code */
+ if (MsiEvaluateConditionW(hPackage, buffer) ==
+ MSICONDITION_FALSE)
+ {
+ MsiCloseHandle(row);
+ continue;
+ }
+
+ }
+
+ sz=0x100;
+ rc = MsiRecordGetStringW(row,1,buffer,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Error is %x\n",rc);
+ MsiCloseHandle(row);
+ break;
+ }
+
+ rc = ACTION_PerformAction(hPackage,buffer);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Execution halted due to error (%i)\n",rc);
+ MsiCloseHandle(row);
+ break;
+ }
+
+ MsiCloseHandle(row);
+ }
+
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ }
+
+end:
+ blitz_propertytable();
+ return rc;
+}
+
+
+/********************************************************
+ * ACTION helper functions and functions that perform the actions
+ *******************************************************/
+
+/*
+ * Alot of actions are really important even if they don't do anything
+ * explicit.. Lots of properties are set at the beginning of the installation
+ * CostFinalize does a bunch of work to translated the directorys and such
+ *
+ * But until I get write access to the database that is hard. so I am going to
+ * hack it to see if I can get something to run.
+ */
+UINT ACTION_PerformAction(MSIHANDLE hPackage, const WCHAR *action)
+{
+ const static WCHAR szCreateFolders[] =
+ {'C','r','e','a','t','e','F','o','l','d','e','r','s',0};
+ const static WCHAR szCostFinalize[] =
+ {'C','o','s','t','F','i','n','a','l','i','z','e',0};
+ const static WCHAR szInstallFiles[] =
+ {'I','n','s','t','a','l','l','F','i','l','e','s',0};
+ const static WCHAR szDuplicateFiles[] =
+ {'D','u','p','l','i','c','a','t','e','F','i','l','e','s',0};
+ const static WCHAR szWriteRegistryValues[] =
+{'W','r','i','t','e','R','e','g','i','s','t','r','y','V','a','l','u','e','s',0};
+
+ TRACE("Performing action (%s)\n",debugstr_w(action));
+
+ if (strcmpW(action,szCreateFolders)==0)
+ return ACTION_CreateFolders(hPackage);
+ if (strcmpW(action,szCostFinalize)==0)
+ return ACTION_CostFinalize(hPackage);
+ if (strcmpW(action,szInstallFiles)==0)
+ return ACTION_InstallFiles(hPackage);
+ if (strcmpW(action,szDuplicateFiles)==0)
+ return ACTION_DuplicateFiles(hPackage);
+ if (strcmpW(action,szWriteRegistryValues)==0)
+ return ACTION_WriteRegistryValues(hPackage);
+ /*
+ .
+ .
+ .
+ */
+ if (ACTION_CustomAction(hPackage,action) != ERROR_SUCCESS)
+ ERR("UNHANDLED MSI ACTION %s\n",debugstr_w(action));
+
+ return ERROR_SUCCESS;
+}
+
+
+static UINT ACTION_CustomAction(MSIHANDLE hPackage,const WCHAR *action)
+{
+ UINT rc;
+ MSIHANDLE view;
+ MSIHANDLE row = 0;
+ WCHAR ExecSeqQuery[1024] =
+ {'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','u','s','t','o'
+,'m','A','c','t','i','o','n',' ','w','h','e','r','e',' ','`','A','c','t','i'
+,'o','n','`',' ','=',' ','`',0};
+ static const WCHAR end[]={'`',0};
+ UINT type;
+ DWORD sz;
+ WCHAR source[0x100];
+ WCHAR target[0x200];
+ WCHAR *deformated=NULL;
+
+ strcatW(ExecSeqQuery,action);
+ strcatW(ExecSeqQuery,end);
+ rc = MsiDatabaseOpenViewW(hPackage, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ TRACE("Handling custom action %s\n",debugstr_w(action));
+ type = MsiRecordGetInteger(row,2);
+
+ switch (type & CUSTOM_ACTION_TYPE_MASK)
+ {
+ case 35: /* Directory set with formatted text. */
+ case 51: /* Property set with formatted text. */
+ sz=0x100;
+ MsiRecordGetStringW(row,3,source,&sz);
+ sz=0x200;
+ MsiRecordGetStringW(row,4,target,&sz);
+ deformat_string(hPackage,target,&deformated);
+ set_property(hPackage,source,deformated);
+ HeapFree(GetProcessHeap(),0,deformated);
+ break;
+ default:
+ ERR("UNHANDLED ACTION TYPE %i\n",type & CUSTOM_ACTION_TYPE_MASK);
+ }
+
+ MsiCloseHandle(row);
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+}
+
+
+/***********************************************************************
+ * create_full_pathW
+ *
+ * Recursively create all directories in the path.
+ *
+ * shamelessly stolen from setupapi/queue.c
+ */
+static BOOL create_full_pathW(const WCHAR *path)
+{
+ BOOL ret = TRUE;
+ int len;
+ WCHAR *new_path;
+
+ new_path = HeapAlloc(GetProcessHeap(), 0, (strlenW(path) + 1) *
+sizeof(WCHAR));
+ strcpyW(new_path, path);
+
+ while((len = strlenW(new_path)) && new_path[len - 1] == '\\')
+ new_path[len - 1] = 0;
+
+ while(!CreateDirectoryW(new_path, NULL))
+ {
+ WCHAR *slash;
+ DWORD last_error = GetLastError();
+ if(last_error == ERROR_ALREADY_EXISTS)
+ break;
+
+ if(last_error != ERROR_PATH_NOT_FOUND)
+ {
+ ret = FALSE;
+ break;
+ }
+
+ if(!(slash = strrchrW(new_path, '\\')))
+ {
+ ret = FALSE;
+ break;
+ }
+
+ len = slash - new_path;
+ new_path[len] = 0;
+ if(!create_full_pathW(new_path))
+ {
+ ret = FALSE;
+ break;
+ }
+ new_path[len] = '\\';
+ }
+
+ HeapFree(GetProcessHeap(), 0, new_path);
+ return ret;
+}
+
+/*
+ * Also we cannot enable/disable components either, so for now I am just going
+ * to do all the directorys for all the components.
+ */
+static UINT ACTION_CreateFolders(MSIHANDLE hPackage)
+{
+ static const CHAR *ExecSeqQuery = "select * from CreateFolder";
+ UINT rc;
+ MSIHANDLE view;
+
+ rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR dir[0x100];
+ WCHAR full_path[MAX_PATH];
+ DWORD sz;
+ MSIHANDLE row = 0;
+
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz=0x100;
+ rc = MsiRecordGetStringW(row,1,dir,&sz);
+
+ if (rc!= ERROR_SUCCESS)
+ {
+ ERR("Unable to get folder id \n");
+ MsiCloseHandle(row);
+ continue;
+ }
+
+ rc = get_property(hPackage, dir,full_path);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to resolve folder id %s\n",debugstr_w(dir));
+ MsiCloseHandle(row);
+ continue;
+ }
+
+ TRACE("Folder is %s\n",debugstr_w(full_path));
+ create_full_pathW(full_path);
+
+ MsiCloseHandle(row);
+ }
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+
+ return rc;
+}
+
+/*
+ * Workhorse function for creating the directories
+ * during Costing
+ */
+static UINT resolve_directory(MSIHANDLE hPackage, const WCHAR* dir,
+ WCHAR* path, BOOL source)
+{
+ static const WCHAR cszsrc[]={'_','S','o','u','r','c','e',0};
+ static const WCHAR cszsrcroot[]=
+ {'[','S','o','u','r','c','e','D','i','r',']',0};
+
+ WCHAR Query[1024] =
+{'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','D','i','r','e','c',
+'t','o','r','y',' ','w','h','e','r','e',' ','`','D','i','r','e','c','t',
+'o','r','y','`',' ','=',' ','`',0};
+ static const WCHAR end[]={'`',0};
+ UINT rc;
+ MSIHANDLE view;
+ WCHAR targetbuffer[0x100];
+ WCHAR *srcdir = NULL;
+ WCHAR *targetdir = NULL;
+ WCHAR buffer[0x100];
+ WCHAR parent[0x100];
+ WCHAR parent_path[MAX_PATH];
+ DWORD sz=0x100;
+ MSIHANDLE row = 0;
+ WCHAR full_path[MAX_PATH];
+ WCHAR name_source[0x100];
+
+ if (get_property(hPackage,dir,path)==ERROR_SUCCESS)
+ return ERROR_SUCCESS;
+
+ TRACE("Working to resolve %s\n",debugstr_w(dir));
+
+ /* special case... root drive */
+ if (strcmpW(dir,cszTargetDir)==0)
+ {
+ if (!source)
+ {
+ if(!get_property(hPackage,cszRootDrive,buffer))
+ {
+ set_property(hPackage,cszTargetDir,buffer);
+ strcpyW(path,buffer);
+ }
+ else
+ {
+ ERR("No RootDrive property defined disaster!\n");
+ MsiCloseHandle(row);
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return ERROR_FUNCTION_FAILED;
+ }
+ }
+ else
+ strcpyW(path,cszsrcroot);
+
+ return ERROR_SUCCESS;
+ }
+
+ strcatW(Query,dir);
+ strcatW(Query,end);
+ rc = MsiDatabaseOpenViewW(hPackage, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ sz=0x100;
+ MsiRecordGetStringW(row,3,targetbuffer,&sz);
+ targetdir=targetbuffer;
+
+ /* split src and target dir */
+ if (strchrW(targetdir,':'))
+ {
+ srcdir=strchrW(targetdir,':');
+ *srcdir=0;
+ srcdir ++;
+ }
+ else
+ srcdir=NULL;
+
+ /* for now only pick long filename versions */
+ if (strchrW(targetdir,'|'))
+ {
+ targetdir = strchrW(targetdir,'|');
+ *targetdir = 0;
+ targetdir ++;
+ }
+ if (srcdir && strchrW(srcdir,'|'))
+ {
+ srcdir= strchrW(srcdir,'|');
+ *srcdir= 0;
+ srcdir ++;
+ }
+
+ /* now check for root dirs */
+ if (targetdir[0] == '.' && targetdir[1] == 0)
+ targetdir = NULL;
+
+ if (srcdir && srcdir[0] == '.' && srcdir[1] == 0)
+ srcdir = NULL;
+
+ if (MsiRecordIsNull(row,2))
+ parent[0]=0;
+ else
+ {
+ sz=0x100;
+ MsiRecordGetStringW(row,2,parent,&sz);
+ }
+
+ if (parent[0])
+ {
+ resolve_directory(hPackage,parent,parent_path,FALSE);
+ strcpyW(full_path,parent_path);
+ if (targetdir)
+ {
+ strcatW(full_path,targetdir);
+ strcatW(full_path,cszbs);
+ }
+ set_property(hPackage,dir,full_path);
+ if (!source)
+ strcpyW(path,full_path);
+
+ resolve_directory(hPackage,parent,parent_path,TRUE);
+ strcpyW(full_path,parent_path);
+ if (srcdir)
+ {
+ strcatW(full_path,srcdir);
+ strcatW(full_path,cszbs);
+ }
+ else if (targetdir)
+ {
+ strcatW(full_path,targetdir);
+ strcatW(full_path,cszbs);
+ }
+
+ strcpyW(name_source,dir);
+ strcatW(name_source,cszsrc);
+ set_property(hPackage,name_source,full_path);
+ if (source)
+ strcpyW(path,full_path);
+ }
+
+ MsiCloseHandle(row);
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+}
+
+/*
+ * Alot is done in this function aside from just the costing.
+ * The costing needs to be implemented at some point but for now i am going
+ * to focus on the directory building
+ *
+ * Note about directory names: I know that directory names get processed into
+ * properties. I am still very unclear where the name_source
+ * part is used but I am preserving it just as a precaution
+ */
+static UINT ACTION_CostFinalize(MSIHANDLE hPackage)
+{
+ static const CHAR *ExecSeqQuery = "select * from Directory";
+ UINT rc;
+ MSIHANDLE view;
+
+ TRACE("Building Directory properties\n");
+
+ rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR name[0x100];
+ WCHAR path[MAX_PATH];
+ MSIHANDLE row = 0;
+ DWORD sz;
+
+ rc = MsiViewFetch(view,&row);
+
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz=0x100;
+ MsiRecordGetStringW(row,1,name,&sz);
+
+ /* This helper function now does ALL the work */
+ TRACE("Dir %s ...\n",debugstr_w(name));
+ resolve_directory(hPackage,name,path,FALSE);
+ TRACE("resolves to %s\n",debugstr_w(path));
+
+ MsiCloseHandle(row);
+ }
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+
+ return ERROR_SUCCESS;
+}
+
+/*
+ * This is a helper function for handling embedded cabinet media
+ */
+static UINT writeout_cabinet_stream(MSIHANDLE hPackage, WCHAR* stream_name,
+ WCHAR* source)
+{
+ UINT rc;
+ USHORT* data;
+ UINT size;
+ DWORD write;
+ HANDLE the_file;
+
+ rc = read_raw_stream_data(hPackage,stream_name,&data,&size);
+
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ if (get_property(hPackage, cszSourceDir, source))
+ {
+ ERR("No Source dir defined \n");
+ rc = ERROR_FUNCTION_FAILED;
+ goto end;
+ }
+
+ strcatW(source,stream_name);
+ the_file = CreateFileW(source, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (the_file == INVALID_HANDLE_VALUE)
+ {
+ rc = ERROR_FUNCTION_FAILED;
+ goto end;
+ }
+
+ WriteFile(the_file,data,size,&write,NULL);
+ CloseHandle(the_file);
+ TRACE("wrote %li bytes to %s\n",write,debugstr_w(source));
+end:
+ HeapFree(GetProcessHeap(),0,data);
+ return rc;
+}
+
+inline static char *strdupWtoA( const WCHAR *str )
+{
+ char *ret = NULL;
+ if (str)
+ {
+ DWORD len = WideCharToMultiByte( CP_ACP, 0, str, -1, NULL, 0, NULL, NULL
+);
+ if ((ret = HeapAlloc( GetProcessHeap(), 0, len )))
+ WideCharToMultiByte( CP_ACP, 0, str, -1, ret, len, NULL, NULL );
+ }
+ return ret;
+}
+
+/***********************************************************************
+ * extract_cabinet_file
+ *
+ * Extract a file from a .cab file.
+ */
+static BOOL extract_cabinet_file( const WCHAR *cabinet, const WCHAR *root)
+
+{
+ static const WCHAR extW[] = {'.','c','a','b',0};
+
+ /* from cabinet.h */
+ typedef struct {
+ long result1; /* 0x000 */
+ long unknown1[3]; /* 0x004 */
+ void* filelist; /* 0x010 */
+ long filecount; /* 0x014 */
+ long unknown2; /* 0x018 */
+ char directory[0x104]; /* 0x01c */
+ char lastfile[0x20c]; /* 0x120 */
+ } EXTRACTdest;
+ extern HRESULT Extract(EXTRACTdest*, LPCSTR);
+
+ char *cab_path, *src_path;
+ int len = strlenW( cabinet );
+ EXTRACTdest exd;
+
+ /* make sure the cabinet file has a .cab extension */
+ if (len <= 4 || strcmpiW( cabinet + len - 4, extW )) return FALSE;
+
+ if (!(cab_path = strdupWtoA( cabinet ))) return FALSE;
+ if (!(src_path = strdupWtoA( root ))) return FALSE;
+
+ memset(&exd,0,sizeof(exd));
+ strcpy(exd.directory,src_path);
+ Extract(&exd,cab_path);
+ HeapFree( GetProcessHeap(), 0, cab_path );
+ HeapFree( GetProcessHeap(), 0, src_path );
+ return TRUE;
+}
+
+static UINT ready_media_for_file(MSIHANDLE hPackage, UINT sequence,
+ WCHAR* path)
+{
+ UINT rc;
+ MSIHANDLE view;
+ MSIHANDLE row = 0;
+ WCHAR source[MAX_PATH];
+ static const CHAR *ExecSeqQuery =
+ "select * from Media where LastSequence > %i order by LastSequence";
+ CHAR Query[1024];
+ WCHAR cab[0x100];
+ DWORD sz=0x100;
+ INT seq;
+ static INT last_sequence = 0;
+
+ if (sequence <= last_sequence)
+ {
+ TRACE("Media already ready (%i, %i)\n",sequence,last_sequence);
+ return ERROR_SUCCESS;
+ }
+
+ sprintf(Query,ExecSeqQuery,sequence);
+
+ rc = MsiDatabaseOpenViewA(hPackage, Query, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+ seq = MsiRecordGetInteger(row,2);
+ last_sequence = seq;
+
+ if (!MsiRecordIsNull(row,4))
+ {
+ sz=0x100;
+ MsiRecordGetStringW(row,4,cab,&sz);
+ /* the stream does not contain the # character */
+ if (cab[0]=='#')
+ {
+ writeout_cabinet_stream(hPackage,&cab[1],source);
+ strcpyW(path,source);
+ *(strrchrW(path,'\\')+1)=0;
+ }
+ else
+ {
+ if (get_property(hPackage, cszSourceDir, source))
+ {
+ ERR("No Source dir defined \n");
+ rc = ERROR_FUNCTION_FAILED;
+ }
+ else
+ {
+ strcpyW(path,source);
+ strcatW(source,cab);
+ }
+ }
+ rc = !extract_cabinet_file(source,path);
+ }
+ MsiCloseHandle(row);
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+}
+
+static void reduce_to_longfilename(WCHAR* filename)
+{
+ if (strchrW(filename,'|'))
+ {
+ WCHAR newname[MAX_PATH];
+ strcpyW(newname,strchrW(filename,'|')+1);
+ strcpyW(filename,newname);
+ }
+}
+
+static UINT get_directory_for_component(MSIHANDLE hPackage,
+ const WCHAR* component, WCHAR* install_path)
+{
+ UINT rc;
+ MSIHANDLE view;
+ MSIHANDLE row = 0;
+ WCHAR ExecSeqQuery[1023] =
+{'s','e','l','e','c','t',' ','*',' ','f','r','o','m',' ','C','o','m'
+,'p','o','n','e','n','t',' ','w','h','e','r','e',' ','C','o','m'
+,'p','o','n','e','n','t',' ','=',' ','`',0};
+ static const WCHAR end[]={'`',0};
+ WCHAR dir[0x100];
+ DWORD sz=0x100;
+
+ strcatW(ExecSeqQuery,component);
+ strcatW(ExecSeqQuery,end);
+
+ rc = MsiDatabaseOpenViewW(hPackage, ExecSeqQuery, &view);
+
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ MsiCloseHandle(row);
+ return rc;
+ }
+
+ sz=0x100;
+ MsiRecordGetStringW(row,3,dir,&sz);
+ rc = get_property(hPackage, dir, install_path);
+
+ MsiCloseHandle(row);
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+}
+
+static UINT ACTION_InstallFiles(MSIHANDLE hPackage)
+{
+ UINT rc;
+ MSIHANDLE view;
+ MSIHANDLE row = 0;
+ static const CHAR *ExecSeqQuery =
+ "select * from File order by Sequence";
+
+ /* REALLY what we want to do is go through all the enabled
+ * features and check all the components of that feature and
+ * make sure that component is not already install and blah
+ * blah blah... I will do it that way some day.. really
+ * but for sheer gratification I am going to just brute force
+ * install all the files
+ */
+
+ rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ while (1)
+ {
+ INT seq = 0;
+ WCHAR component[0x100];
+ WCHAR install_path[MAX_PATH];
+ WCHAR path_to_source[MAX_PATH];
+ WCHAR src_path[MAX_PATH];
+ WCHAR filename[0x100];
+ WCHAR sourcename[0x100];
+ DWORD sz=0x100;
+
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ seq = MsiRecordGetInteger(row,8);
+ rc = ready_media_for_file(hPackage,seq,path_to_source);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to ready media\n");
+ MsiCloseHandle(row);
+ break;
+ }
+ sz=0x100;
+ rc = MsiRecordGetStringW(row,2,component,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to read component\n");
+ MsiCloseHandle(row);
+ break;
+ }
+ rc = get_directory_for_component(hPackage,component,install_path);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to get directory\n");
+ MsiCloseHandle(row);
+ break;
+ }
+
+ sz=0x100;
+ rc = MsiRecordGetStringW(row,1,sourcename,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to get sourcename\n");
+ MsiCloseHandle(row);
+ break;
+ }
+ strcpyW(src_path,path_to_source);
+ strcatW(src_path,sourcename);
+
+ sz=0x100;
+ rc = MsiRecordGetStringW(row,3,filename,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to get filename\n");
+ MsiCloseHandle(row);
+ break;
+ }
+ reduce_to_longfilename(filename);
+ strcatW(install_path,filename);
+
+ TRACE("Installing file %s to %s\n",debugstr_w(src_path),
+ debugstr_w(install_path));
+
+ rc = !MoveFileW(src_path,install_path);
+ if (rc)
+ {
+ ERR("Unable to move file\n");
+ }
+
+ /* for future use lets keep track of this file and where it went */
+ set_property(hPackage,sourcename,install_path);
+
+ MsiCloseHandle(row);
+ }
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+
+ return rc;
+}
+
+static UINT ACTION_DuplicateFiles(MSIHANDLE hPackage)
+{
+ UINT rc;
+ MSIHANDLE view;
+ MSIHANDLE row = 0;
+ static const CHAR *ExecSeqQuery = "select * from DuplicateFile";
+
+
+ /*
+ * Yes we should only do this for componenets that are installed
+ * but again I need to do that went I track components.
+ */
+
+ rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR file_key[0x100];
+ WCHAR file_source[MAX_PATH];
+ WCHAR dest_name[0x100];
+ WCHAR dest_path[MAX_PATH];
+
+ DWORD sz=0x100;
+
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ sz=0x100;
+ rc = MsiRecordGetStringW(row,3,file_key,&sz);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to get file key\n");
+ MsiCloseHandle(row);
+ break;
+ }
+
+ rc = get_property(hPackage,file_key,file_source);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Original file unknown %s\n",debugstr_w(file_key));
+ MsiCloseHandle(row);
+ break;
+ }
+
+ if (MsiRecordIsNull(row,4))
+ strcpyW(dest_name,strrchrW(file_source,'\\')+1);
+ else
+ {
+ sz=0x100;
+ MsiRecordGetStringW(row,4,dest_name,&sz);
+ reduce_to_longfilename(dest_name);
+ }
+
+ if (MsiRecordIsNull(row,5))
+ {
+ strcpyW(dest_path,file_source);
+ *strrchrW(dest_path,'\\')=0;
+ }
+ else
+ {
+ WCHAR destkey[0x100];
+ sz=0x100;
+ MsiRecordGetStringW(row,5,destkey,&sz);
+ rc = get_property(hPackage, destkey, dest_path);
+ if (rc != ERROR_SUCCESS)
+ {
+ ERR("Unable to get destination folder\n");
+ MsiCloseHandle(row);
+ break;
+ }
+ }
+
+ strcatW(dest_path,dest_name);
+
+ TRACE("Duplicating file %s to %s\n",debugstr_w(file_source),
+ debugstr_w(dest_path));
+
+ if (strcmpW(file_source,dest_path))
+ rc = !CopyFileW(file_source,dest_path,TRUE);
+ else
+ rc = ERROR_SUCCESS;
+
+ if (rc != ERROR_SUCCESS)
+ ERR("Failed to copy file\n");
+
+ MsiCloseHandle(row);
+ }
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+
+}
+
+
+static LPSTR parse_value(MSIHANDLE hPackage, WCHAR *value, DWORD *type,
+ DWORD *size)
+{
+ LPSTR data = NULL;
+ if (value[0]=='#' && value[1]!='#')
+ {
+ ERR("UNHANDLED VALUE TYPE\n");
+ }
+ else
+ {
+ WCHAR *ptr;
+ if (value[0]=='#')
+ ptr = &value[1];
+ else
+ ptr=value;
+
+ *type=REG_SZ;
+ *size = deformat_string(hPackage, ptr,(LPWSTR*)&data);
+ }
+ return data;
+}
+
+static UINT ACTION_WriteRegistryValues(MSIHANDLE hPackage)
+{
+ UINT rc;
+ MSIHANDLE view;
+ MSIHANDLE row = 0;
+ static const CHAR *ExecSeqQuery = "select * from Registry";
+
+ /* Again here we want to key off of the components being installed...
+ * oh well
+ */
+
+ rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ while (1)
+ {
+ WCHAR key[0x100];
+ WCHAR name[0x100];
+ WCHAR value[0x100];
+ LPSTR value_data = NULL;
+ HKEY root_key, hkey;
+ DWORD type,size;
+
+ INT root;
+ DWORD sz=0x100;
+
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ /* null values have special meanings during uninstalls and such */
+
+ if(MsiRecordIsNull(row,5))
+ {
+ MsiCloseHandle(row);
+ continue;
+ }
+
+ root = MsiRecordGetInteger(row,2);
+ sz = 0x100;
+ MsiRecordGetStringW(row,3,key,&sz);
+
+ sz = 0x100;
+ if (MsiRecordIsNull(row,4))
+ name[0]=0;
+ else
+ MsiRecordGetStringW(row,4,name,&sz);
+
+ sz=0x100;
+ MsiRecordGetStringW(row,5,value,&sz);
+
+
+ /* get the root key */
+ switch (root)
+ {
+ case 0: root_key = HKEY_CLASSES_ROOT; break;
+ case 1: root_key = HKEY_CURRENT_USER; break;
+ case 2: root_key = HKEY_LOCAL_MACHINE; break;
+ case 3: root_key = HKEY_USERS; break;
+ default:
+ ERR("Unknown root %i\n",root);
+ root_key=NULL;
+ break;
+ }
+ if (!root_key)
+ {
+ MsiCloseHandle(row);
+ continue;
+ }
+
+ if (RegCreateKeyW( root_key, key, &hkey))
+ {
+ ERR("Could not create key %s\n",debugstr_w(key));
+ MsiCloseHandle(row);
+ continue;
+ }
+
+ value_data = parse_value(hPackage, value, &type, &size);
+
+ if (value_data)
+ {
+ TRACE("Setting value %s\n",debugstr_w(name));
+ RegSetValueExW(hkey, name, 0, type, value_data, size);
+ HeapFree(GetProcessHeap(),0,value_data);
+ }
+
+ MsiCloseHandle(row);
+ }
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+}
+
+/*
+ * This helper function should probably go alot of places
+ */
+static DWORD deformat_string(MSIHANDLE hPackage, WCHAR* ptr,WCHAR** data)
+{
+ WCHAR* mark=NULL;
+ DWORD size=0;
+ DWORD chunk=0;
+ WCHAR key[0x100];
+ WCHAR value[0x100];
+
+ /* scan for special characters */
+ if (!strchrW(ptr,'[') || (strchrW(ptr,'[') && !strchrW(ptr,']')))
+ {
+ /* not formatted */
+ size = (strlenW(ptr)+1) * sizeof(WCHAR);
+ *data = HeapAlloc(GetProcessHeap(),0,size);
+ strcpyW(*data,ptr);
+ return size;
+ }
+
+ /* formatted string located */
+ mark = strchrW(ptr,'[');
+ if (mark != ptr)
+ {
+ INT cnt = (mark - ptr);
+ TRACE("%i (%i) characters before marker\n",cnt,(mark-ptr));
+ size = cnt * sizeof(WCHAR);
+ size += sizeof(WCHAR);
+ *data = HeapAlloc(GetProcessHeap(),0,size);
+ strncpyW(*data,ptr,cnt);
+ (*data)[cnt]=0;
+ }
+ else
+ {
+ size = sizeof(WCHAR);
+ *data = HeapAlloc(GetProcessHeap(),0,size);
+ (*data)[0]=0;
+ }
+ mark++;
+ strcpyW(key,mark);
+ *strchrW(key,']')=0;
+ mark = strchrW(mark,']');
+ mark++;
+ TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
+ if (get_property(hPackage, key, value) == ERROR_SUCCESS)
+ {
+ LPWSTR newdata;
+ chunk = (strlenW(value)+1) * sizeof(WCHAR);
+ size+=chunk;
+ newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
+ *data = newdata;
+ strcatW(*data,value);
+ }
+ TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
+ if (*mark!=0)
+ {
+ LPWSTR newdata;
+ chunk = (strlenW(mark)+1) * sizeof(WCHAR);
+ size+=chunk;
+ newdata = HeapReAlloc(GetProcessHeap(),0,*data,size);
+ *data = newdata;
+ strcatW(*data,mark);
+ }
+ (*data)[strlenW(*data)]=0;
+ TRACE("Current %s .. %s\n",debugstr_w(*data),debugstr_w(mark));
+
+ /* recursivly do this to clean up */
+ mark = HeapAlloc(GetProcessHeap(),0,size);
+ strcpyW(mark,*data);
+ TRACE("String at this point %s\n",debugstr_w(mark));
+ size = deformat_string(hPackage,mark,data);
+ HeapFree(GetProcessHeap(),0,mark);
+ return size;
+}
+
+
+
+#if 0
+static UINT ACTION_Template(MSIHANDLE hPackage)
+{
+ UINT rc;
+ MSIHANDLE view;
+ MSIHANDLE row = 0;
+ static const CHAR *ExecSeqQuery;
+
+ rc = MsiDatabaseOpenViewA(hPackage, ExecSeqQuery, &view);
+ if (rc != ERROR_SUCCESS)
+ return rc;
+
+ rc = MsiViewExecute(view, 0);
+ if (rc != ERROR_SUCCESS)
+ {
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+ }
+
+ while (1)
+ {
+ rc = MsiViewFetch(view,&row);
+ if (rc != ERROR_SUCCESS)
+ {
+ rc = ERROR_SUCCESS;
+ break;
+ }
+
+ MsiCloseHandle(row);
+ }
+ MsiViewClose(view);
+ MsiCloseHandle(view);
+ return rc;
+}
+#endif
More information about the wine-patches
mailing list