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