From 6386edc5d8616e3aff9bd4f552cf9c30020bf522 Mon Sep 17 00:00:00 2001
From: Mike McCormack <mike@codeweavers.com>
Date: Wed, 13 Aug 2003 01:27:48 +0000
Subject: [PATCH] Partial implementation of the Microsoft Installer (msi.dll).

---
 configure            |   3 +-
 configure.ac         |   1 +
 dlls/Makefile.in     |  13 +
 dlls/msi/.cvsignore  |   6 +
 dlls/msi/Makefile.in |  39 ++
 dlls/msi/distinct.c  | 286 ++++++++++++++
 dlls/msi/handle.c    | 115 ++++++
 dlls/msi/msi.c       | 640 +++++++++++++++++++++++++++++++
 dlls/msi/msi.spec    | 218 +++++++++++
 dlls/msi/msipriv.h   | 186 +++++++++
 dlls/msi/msiquery.c  | 376 ++++++++++++++++++
 dlls/msi/order.c     | 328 ++++++++++++++++
 dlls/msi/query.h     |  88 +++++
 dlls/msi/record.c    | 442 +++++++++++++++++++++
 dlls/msi/select.c    | 224 +++++++++++
 dlls/msi/sql.y       | 513 +++++++++++++++++++++++++
 dlls/msi/suminfo.c   | 223 +++++++++++
 dlls/msi/table.c     | 887 +++++++++++++++++++++++++++++++++++++++++++
 dlls/msi/tokenize.c  | 379 ++++++++++++++++++
 dlls/msi/where.c     | 384 +++++++++++++++++++
 include/Makefile.in  |   2 +
 include/msi.h        |  85 +++++
 include/msiquery.h   |  98 +++++
 23 files changed, 5535 insertions(+), 1 deletion(-)
 create mode 100644 dlls/msi/.cvsignore
 create mode 100644 dlls/msi/Makefile.in
 create mode 100644 dlls/msi/distinct.c
 create mode 100644 dlls/msi/handle.c
 create mode 100644 dlls/msi/msi.c
 create mode 100644 dlls/msi/msi.spec
 create mode 100644 dlls/msi/msipriv.h
 create mode 100644 dlls/msi/msiquery.c
 create mode 100644 dlls/msi/order.c
 create mode 100644 dlls/msi/query.h
 create mode 100644 dlls/msi/record.c
 create mode 100644 dlls/msi/select.c
 create mode 100644 dlls/msi/sql.y
 create mode 100644 dlls/msi/suminfo.c
 create mode 100644 dlls/msi/table.c
 create mode 100644 dlls/msi/tokenize.c
 create mode 100644 dlls/msi/where.c
 create mode 100644 include/msi.h
 create mode 100644 include/msiquery.h

diff --git a/configure b/configure
index b3fc46d2b7f..587b4ae336d 100755
--- a/configure
+++ b/configure
@@ -16478,7 +16478,7 @@ MAKE_LIB_RULES=libs/Makelib.rules
 MAKE_PROG_RULES=programs/Makeprog.rules
 
 
-                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/ctl3d/Makefile dlls/d3d8/Makefile dlls/d3d9/Makefile dlls/d3dim/Makefile dlls/d3dx8/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/icmp/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lzexpand/Makefile dlls/mapi32/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msdmo/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrtd/Makefile dlls/msvideo/Makefile dlls/msvideo/msrle32/Makefile dlls/mswsock/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/sti/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/version/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/tests/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winenas/Makefile dlls/winmm/winejack/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winsock/tests/Makefile dlls/winspool/Makefile dlls/winspool/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/x11drv/Makefile documentation/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/uuid/Makefile libs/wine/Makefile libs/wpp/Makefile miscemu/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/notepad/Makefile programs/osversioncheck/Makefile programs/progman/Makefile programs/regapi/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/regtest/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/wmc/Makefile tools/wrc/Makefile"
+                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            ac_config_files="$ac_config_files Make.rules dlls/Makedll.rules dlls/Maketest.rules libs/Makelib.rules programs/Makeprog.rules Makefile dlls/Makefile dlls/advapi32/Makefile dlls/advapi32/tests/Makefile dlls/avicap32/Makefile dlls/avifil32/Makefile dlls/cabinet/Makefile dlls/comcat/Makefile dlls/comctl32/Makefile dlls/comctl32/tests/Makefile dlls/commdlg/Makefile dlls/crtdll/Makefile dlls/crypt32/Makefile dlls/ctl3d/Makefile dlls/d3d8/Makefile dlls/d3d9/Makefile dlls/d3dim/Makefile dlls/d3dx8/Makefile dlls/dciman32/Makefile dlls/ddraw/Makefile dlls/devenum/Makefile dlls/dinput/Makefile dlls/dinput8/Makefile dlls/dmband/Makefile dlls/dmcompos/Makefile dlls/dmime/Makefile dlls/dmloader/Makefile dlls/dmscript/Makefile dlls/dmstyle/Makefile dlls/dmsynth/Makefile dlls/dmusic/Makefile dlls/dmusic32/Makefile dlls/dplay/Makefile dlls/dplayx/Makefile dlls/dpnhpast/Makefile dlls/dsound/Makefile dlls/dsound/tests/Makefile dlls/gdi/Makefile dlls/gdi/tests/Makefile dlls/glu32/Makefile dlls/icmp/Makefile dlls/imagehlp/Makefile dlls/imm32/Makefile dlls/iphlpapi/Makefile dlls/kernel/Makefile dlls/kernel/tests/Makefile dlls/lzexpand/Makefile dlls/mapi32/Makefile dlls/mpr/Makefile dlls/msacm/Makefile dlls/msacm/imaadp32/Makefile dlls/msacm/msadp32/Makefile dlls/msacm/msg711/Makefile dlls/msacm/winemp3/Makefile dlls/msdmo/Makefile dlls/msi/Makefile dlls/msimg32/Makefile dlls/msisys/Makefile dlls/msnet32/Makefile dlls/msvcrt/Makefile dlls/msvcrt/tests/Makefile dlls/msvcrt20/Makefile dlls/msvcrtd/Makefile dlls/msvideo/Makefile dlls/msvideo/msrle32/Makefile dlls/mswsock/Makefile dlls/netapi32/Makefile dlls/netapi32/tests/Makefile dlls/ntdll/Makefile dlls/ntdll/tests/Makefile dlls/odbc32/Makefile dlls/ole32/Makefile dlls/oleacc/Makefile dlls/oleaut32/Makefile dlls/oleaut32/tests/Makefile dlls/olecli/Makefile dlls/oledlg/Makefile dlls/olepro32/Makefile dlls/olesvr/Makefile dlls/opengl32/Makefile dlls/psapi/Makefile dlls/qcap/Makefile dlls/quartz/Makefile dlls/rasapi32/Makefile dlls/richedit/Makefile dlls/rpcrt4/Makefile dlls/rpcrt4/tests/Makefile dlls/serialui/Makefile dlls/setupapi/Makefile dlls/shdocvw/Makefile dlls/shell32/Makefile dlls/shell32/tests/Makefile dlls/shfolder/Makefile dlls/shlwapi/Makefile dlls/shlwapi/tests/Makefile dlls/snmpapi/Makefile dlls/sti/Makefile dlls/tapi32/Makefile dlls/ttydrv/Makefile dlls/twain/Makefile dlls/url/Makefile dlls/urlmon/Makefile dlls/urlmon/tests/Makefile dlls/user/Makefile dlls/user/tests/Makefile dlls/version/Makefile dlls/win32s/Makefile dlls/winaspi/Makefile dlls/winedos/Makefile dlls/wineps/Makefile dlls/wininet/Makefile dlls/wininet/tests/Makefile dlls/winmm/Makefile dlls/winmm/joystick/Makefile dlls/winmm/mcianim/Makefile dlls/winmm/mciavi/Makefile dlls/winmm/mcicda/Makefile dlls/winmm/mciseq/Makefile dlls/winmm/mciwave/Makefile dlls/winmm/midimap/Makefile dlls/winmm/tests/Makefile dlls/winmm/wavemap/Makefile dlls/winmm/winealsa/Makefile dlls/winmm/winearts/Makefile dlls/winmm/wineaudioio/Makefile dlls/winmm/winenas/Makefile dlls/winmm/winejack/Makefile dlls/winmm/wineoss/Makefile dlls/winnls/Makefile dlls/winsock/Makefile dlls/winsock/tests/Makefile dlls/winspool/Makefile dlls/winspool/tests/Makefile dlls/wintab32/Makefile dlls/wintrust/Makefile dlls/wow32/Makefile dlls/wsock32/Makefile dlls/x11drv/Makefile documentation/Makefile include/Makefile libs/Makefile libs/port/Makefile libs/unicode/Makefile libs/uuid/Makefile libs/wine/Makefile libs/wpp/Makefile miscemu/Makefile programs/Makefile programs/avitools/Makefile programs/clock/Makefile programs/cmdlgtst/Makefile programs/control/Makefile programs/expand/Makefile programs/notepad/Makefile programs/osversioncheck/Makefile programs/progman/Makefile programs/regapi/Makefile programs/regedit/Makefile programs/regsvr32/Makefile programs/regtest/Makefile programs/rpcss/Makefile programs/rundll32/Makefile programs/start/Makefile programs/uninstaller/Makefile programs/view/Makefile programs/wcmd/Makefile programs/wineboot/Makefile programs/winecfg/Makefile programs/wineconsole/Makefile programs/winedbg/Makefile programs/winefile/Makefile programs/winemenubuilder/Makefile programs/winemine/Makefile programs/winepath/Makefile programs/winevdm/Makefile programs/winhelp/Makefile programs/winver/Makefile server/Makefile tools/Makefile tools/widl/Makefile tools/winapi/Makefile tools/winebuild/Makefile tools/winedump/Makefile tools/wmc/Makefile tools/wrc/Makefile"
 
 
 cat >confcache <<\_ACEOF
@@ -17068,6 +17068,7 @@ do
   "dlls/msacm/msg711/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msacm/msg711/Makefile" ;;
   "dlls/msacm/winemp3/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msacm/winemp3/Makefile" ;;
   "dlls/msdmo/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msdmo/Makefile" ;;
+  "dlls/msi/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msi/Makefile" ;;
   "dlls/msimg32/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msimg32/Makefile" ;;
   "dlls/msisys/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msisys/Makefile" ;;
   "dlls/msnet32/Makefile" ) CONFIG_FILES="$CONFIG_FILES dlls/msnet32/Makefile" ;;
diff --git a/configure.ac b/configure.ac
index e9e378b8008..161cca04efa 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1452,6 +1452,7 @@ dlls/msacm/msadp32/Makefile
 dlls/msacm/msg711/Makefile
 dlls/msacm/winemp3/Makefile
 dlls/msdmo/Makefile
+dlls/msi/Makefile
 dlls/msimg32/Makefile
 dlls/msisys/Makefile
 dlls/msnet32/Makefile
diff --git a/dlls/Makefile.in b/dlls/Makefile.in
index 23a4711c76f..a2298753c38 100644
--- a/dlls/Makefile.in
+++ b/dlls/Makefile.in
@@ -58,6 +58,7 @@ BASEDIRS = \
 	msacm/msg711 \
 	msacm/winemp3 \
 	msdmo \
+	msi \
 	msimg32 \
 	msisys \
 	msnet32 \
@@ -247,6 +248,7 @@ SYMLINKS = \
 	msadp32.acm$(DLLEXT) \
 	msdmo.dll$(DLLEXT) \
 	msg711.acm$(DLLEXT) \
+	msi.dll$(DLLEXT) \
 	msimg32.dll$(DLLEXT) \
 	msisys.ocx$(DLLEXT) \
 	msnet32.dll$(DLLEXT) \
@@ -482,6 +484,9 @@ msdmo.dll$(DLLEXT): msdmo/msdmo.dll$(DLLEXT)
 msg711.acm$(DLLEXT): msacm/msg711/msg711.acm$(DLLEXT)
 	$(RM) $@ && $(LN_S) msacm/msg711/msg711.acm$(DLLEXT) $@
 
+msi.dll$(DLLEXT): msi/msi.dll$(DLLEXT)
+	$(RM) $@ && $(LN_S) msi/msi.dll$(DLLEXT) $@
+
 msimg32.dll$(DLLEXT): msimg32/msimg32.dll$(DLLEXT)
 	$(RM) $@ && $(LN_S) msimg32/msimg32.dll$(DLLEXT) $@
 
@@ -719,6 +724,7 @@ IMPORT_LIBS = \
 	libmpr \
 	libmsacm32 \
 	libmsdmo \
+	libmsi \
 	libmsimg32 \
 	libmsnet32 \
 	libmsrle32 \
@@ -994,6 +1000,11 @@ libmsdmo.def: msdmo/msdmo.spec.def
 libmsdmo.a: msdmo/msdmo.spec.def
 	$(DLLTOOL) -k -l $@ -d msdmo/msdmo.spec.def
 
+libmsi.def: msi/msi.spec.def
+	$(RM) $@ && $(LN_S) msi/msi.spec.def $@
+libmsi.a: msi/msi.spec.def
+	$(DLLTOOL) -k -l $@ -d msi/msi.spec.def
+
 libmsimg32.def: msimg32/msimg32.spec.def
 	$(RM) $@ && $(LN_S) msimg32/msimg32.spec.def $@
 libmsimg32.a: msimg32/msimg32.spec.def
@@ -1308,6 +1319,7 @@ mapi32/mapi32.spec.def: $(WINEBUILD)
 mpr/mpr.spec.def: $(WINEBUILD)
 msacm/msacm32.spec.def: $(WINEBUILD)
 msdmo/msdmo.spec.def: $(WINEBUILD)
+msi/msi.spec.def: $(WINEBUILD)
 msimg32/msimg32.spec.def: $(WINEBUILD)
 msnet32/msnet32.spec.def: $(WINEBUILD)
 msvideo/msrle32/msrle32.spec.def: $(WINEBUILD)
@@ -1424,6 +1436,7 @@ msacm/msacm32.dll$(DLLEXT): msacm
 msacm/msadp32/msadp32.acm$(DLLEXT): msacm/msadp32
 msdmo/msdmo.dll$(DLLEXT): msdmo
 msacm/msg711/msg711.acm$(DLLEXT): msacm/msg711
+msi/msi.dll$(DLLEXT): msi
 msimg32/msimg32.dll$(DLLEXT): msimg32
 msisys/msisys.ocx$(DLLEXT): msisys
 msnet32/msnet32.dll$(DLLEXT): msnet32
diff --git a/dlls/msi/.cvsignore b/dlls/msi/.cvsignore
new file mode 100644
index 00000000000..d96b95b4550
--- /dev/null
+++ b/dlls/msi/.cvsignore
@@ -0,0 +1,6 @@
+Makefile
+msi.dll.dbg.c
+msi.spec.c
+msi.spec.def
+y.tab.c
+y.tab.h
diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in
new file mode 100644
index 00000000000..eca9c4b01a4
--- /dev/null
+++ b/dlls/msi/Makefile.in
@@ -0,0 +1,39 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = msi.dll
+IMPORTS   = ole32 user32 advapi32 kernel32
+EXTRALIBS = $(LIBUUID) $(LIBUNICODE)
+
+LDDLLFLAGS = @LDDLLFLAGS@
+SYMBOLFILE = $(MODULE).tmp.o
+
+C_SRCS = \
+	distinct.c \
+	handle.c \
+	msi.c \
+	msiquery.c \
+	order.c \
+	record.c \
+	select.c \
+	suminfo.c \
+	table.c \
+	tokenize.c \
+	where.c
+
+#RC_SRCS= msi_rc.rc
+
+EXTRA_SRCS = sql.y 
+EXTRA_OBJS = y.tab.o
+
+@MAKE_DLL_RULES@
+
+y.tab.c y.tab.h: sql.y
+	$(YACC) -d -t $(SRCDIR)/sql.y
+
+# hack to allow parallel make
+y.tab.h: y.tab.c
+y.tab.o: y.tab.h
+
+### Dependencies:
diff --git a/dlls/msi/distinct.c b/dlls/msi/distinct.c
new file mode 100644
index 00000000000..aaa140958bd
--- /dev/null
+++ b/dlls/msi/distinct.c
@@ -0,0 +1,286 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tagDISTINCTSET
+{
+    UINT val;
+    UINT count;
+    UINT row;
+    struct tagDISTINCTSET *nextrow;
+    struct tagDISTINCTSET *nextcol;
+} DISTINCTSET;
+
+typedef struct tagMSIDISTINCTVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSIVIEW       *table;
+    UINT           row_count;
+    UINT          *translation;
+} MSIDISTINCTVIEW;
+
+static DISTINCTSET ** distinct_insert( DISTINCTSET **x, UINT val, UINT row )
+{
+    /* horrible O(n) find */
+    while( *x )
+    {
+        if( (*x)->val == val )
+        {
+            (*x)->count++;
+            return x;
+        }
+        x = &(*x)->nextrow;
+    }
+
+    /* nothing found, so add one */
+    *x = HeapAlloc( GetProcessHeap(), 0, sizeof (DISTINCTSET) );
+    if( *x )
+    {
+        (*x)->val = val;
+        (*x)->count = 1;
+        (*x)->row = row;
+        (*x)->nextrow = NULL;
+        (*x)->nextcol = NULL;
+    }
+    return x;
+}
+
+static void distinct_free( DISTINCTSET *x )
+{
+    while( x )
+    {
+        DISTINCTSET *next = x->nextrow;
+        distinct_free( x->nextcol );
+        HeapFree( GetProcessHeap(), 0, x );
+        x = next;
+    }
+}
+
+static UINT DISTINCT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p %d %d %p\n", dv, row, col, val );
+
+    if( !dv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    if( row >= dv->row_count )
+        return ERROR_INVALID_PARAMETER;
+
+    row = dv->translation[ row ];
+
+    return dv->table->ops->fetch_int( dv->table, row, col, val );
+}
+
+static UINT DISTINCT_execute( struct tagMSIVIEW *view, MSIHANDLE record )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+    UINT r, i, j, r_count, c_count;
+    DISTINCTSET *rowset = NULL;
+
+    TRACE("%p %ld\n", dv, record);
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    r = dv->table->ops->execute( dv->table, record );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = dv->table->ops->get_dimensions( dv->table, &r_count, &c_count );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    dv->translation = HeapAlloc( GetProcessHeap(), 0, r_count*sizeof(UINT) );
+    if( !dv->translation )
+        return ERROR_FUNCTION_FAILED;
+
+    /* build it */
+    for( i=0; i<r_count; i++ )
+    {
+        DISTINCTSET **x = &rowset;
+
+        for( j=1; j<=c_count; j++ )
+        {
+            UINT val = 0;
+            r = dv->table->ops->fetch_int( dv->table, i, j, &val );
+            if( r != ERROR_SUCCESS )
+            {
+                ERR("Failed to fetch int at %d %d\n", i, j );
+                distinct_free( rowset );
+                return r;
+            }
+            x = distinct_insert( x, val, i );
+            if( !*x )
+            {
+                ERR("Failed to insert at %d %d\n", i, j );
+                distinct_free( rowset );
+                return ERROR_FUNCTION_FAILED;
+            }
+            if( j != c_count )
+                x = &(*x)->nextcol;
+        }
+
+        /* check if it was distinct and if so, include it */
+        if( (*x)->row == i )
+        {
+            TRACE("Row %d -> %d\n", dv->row_count, i);
+            dv->translation[dv->row_count++] = i;
+        }
+    }
+
+    distinct_free( rowset );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT DISTINCT_close( struct tagMSIVIEW *view )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p\n", dv );
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( dv->translation )
+        HeapFree( GetProcessHeap(), 0, dv->translation );
+    dv->translation = NULL;
+    dv->row_count = 0;
+
+    return dv->table->ops->close( dv->table );
+}
+
+static UINT DISTINCT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p %p %p\n", dv, rows, cols );
+
+    if( !dv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    if( rows )
+    {
+        if( !dv->translation )
+            return ERROR_FUNCTION_FAILED;
+        *rows = dv->row_count;
+    }
+
+    return dv->table->ops->get_dimensions( dv->table, NULL, cols );
+}
+
+static UINT DISTINCT_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p %d %p %p\n", dv, n, name, type );
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return dv->table->ops->get_column_info( dv->table, n, name, type );
+}
+
+static UINT DISTINCT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p %d %ld\n", dv, eModifyMode, hrec );
+
+    if( !dv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return dv->table->ops->modify( dv->table, eModifyMode, hrec );
+}
+
+static UINT DISTINCT_delete( struct tagMSIVIEW *view )
+{
+    MSIDISTINCTVIEW *dv = (MSIDISTINCTVIEW*)view;
+
+    TRACE("%p\n", dv );
+
+    if( dv->table )
+        dv->table->ops->delete( dv->table );
+
+    if( dv->translation )
+        HeapFree( GetProcessHeap(), 0, dv->translation );
+    HeapFree( GetProcessHeap(), 0, dv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS distinct_ops =
+{
+    DISTINCT_fetch_int,
+    DISTINCT_execute,
+    DISTINCT_close,
+    DISTINCT_get_dimensions,
+    DISTINCT_get_column_info,
+    DISTINCT_modify,
+    DISTINCT_delete
+};
+
+UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
+{
+    MSIDISTINCTVIEW *dv = NULL;
+    UINT count = 0, r;
+
+    TRACE("%p\n", dv );
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("can't get table dimensions\n");
+        return r;
+    }
+
+    dv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *dv );
+    if( !dv )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    dv->view.ops = &distinct_ops;
+    dv->db = db;
+    dv->table = table;
+    dv->translation = NULL;
+    dv->row_count = 0;
+    *view = (MSIVIEW*) dv;
+
+    return ERROR_SUCCESS;
+}
diff --git a/dlls/msi/handle.c b/dlls/msi/handle.c
new file mode 100644
index 00000000000..ee73b11f851
--- /dev/null
+++ b/dlls/msi/handle.c
@@ -0,0 +1,115 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+MSIHANDLEINFO *msihandletable[MSIMAXHANDLES];
+
+MSIHANDLE alloc_msihandle(UINT type, UINT size, msihandledestructor destroy)
+{
+    MSIHANDLEINFO *info;
+    UINT i;
+
+    /* find a slot */
+    for(i=0; i<MSIMAXHANDLES; i++)
+        if( !msihandletable[i] )
+            break;
+    if( (i>=MSIMAXHANDLES) || msihandletable[i] )
+        return 0;
+
+    size += sizeof (MSIHANDLEINFO);
+    info = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, size );
+    if( !info )
+        return 0;
+
+    info->magic = MSIHANDLE_MAGIC;
+    info->type = type;
+    info->destructor = destroy;
+
+    msihandletable[i] = info;
+
+    return (MSIHANDLE) (i+1);
+}
+
+void *msihandle2msiinfo(MSIHANDLE handle, UINT type)
+{
+    handle--;
+    if( handle<0 )
+        return NULL;
+    if( handle>=MSIMAXHANDLES )
+        return NULL;
+    if( !msihandletable[handle] )
+        return NULL;
+    if( msihandletable[handle]->magic != MSIHANDLE_MAGIC )
+        return NULL;
+    if( type && (msihandletable[handle]->type != type) )
+        return NULL;
+
+    return &msihandletable[handle][1];
+}
+
+UINT WINAPI MsiCloseHandle(MSIHANDLE handle)
+{
+    MSIHANDLEINFO *info = msihandle2msiinfo(handle, 0);
+
+    TRACE("%lx\n",handle);
+
+    if( !info )
+        return ERROR_INVALID_HANDLE;
+
+    info--;
+
+    if( info->magic != MSIHANDLE_MAGIC )
+    {
+        ERR("Invalid handle!\n");
+        return ERROR_INVALID_HANDLE;
+    }
+
+    if( info->destructor )
+        info->destructor( &info[1] );
+
+    HeapFree( GetProcessHeap(), 0, info );
+    msihandletable[handle-1] = NULL;
+
+    TRACE("Destroyed\n");
+
+    return 0;
+}
+
+UINT WINAPI MsiCloseAllHandles(void)
+{
+    UINT i;
+
+    TRACE("\n");
+
+    for(i=0; i<MSIMAXHANDLES; i++)
+        MsiCloseHandle( i+1 );
+
+    return 0;
+}
diff --git a/dlls/msi/msi.c b/dlls/msi/msi.c
new file mode 100644
index 00000000000..724cd4d7baf
--- /dev/null
+++ b/dlls/msi/msi.c
@@ -0,0 +1,640 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define NONAMELESSUNION
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "objidl.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+const WCHAR szInstaller[] = {
+'S','o','f','t','w','a','r','e','\\',
+'M','i','c','r','o','s','o','f','t','\\',
+'W','i','n','d','o','w','s','\\',
+'C','u','r','r','e','n','t','V','e','r','s','i','o','n','\\',
+'I','n','s','t','a','l','l','e','r',0 };
+
+const WCHAR szFeatures[] = {
+'F','e','a','t','u','r','e','s',0 };
+const WCHAR szComponents[] = {
+'C','o','m','p','o','n','e','n','t','s',0 };
+
+/*
+ *  .MSI  file format
+ *
+ *  A .msi file is a structured storage file.
+ *  It should contain a number of streams.
+ */
+
+BOOL unsquash_guid(LPCWSTR in, LPWSTR out)
+{
+    DWORD i,n=0;
+
+    out[n++]='{';
+    for(i=0; i<8; i++)
+        out[n++] = in[7-i];
+    out[n++]='-';
+    for(i=0; i<4; i++)
+        out[n++] = in[11-i];
+    out[n++]='-';
+    for(i=0; i<4; i++)
+        out[n++] = in[15-i];
+    out[n++]='-';
+    for(i=0; i<2; i++)
+    {
+        out[n++] = in[17+i*2];
+        out[n++] = in[16+i*2];
+    }
+    out[n++]='-';
+    for( ; i<8; i++)
+    {
+        out[n++] = in[17+i*2];
+        out[n++] = in[16+i*2];
+    }
+    out[n++]='}';
+    out[n]=0;
+    return TRUE;
+}
+
+BOOL squash_guid(LPCWSTR in, LPWSTR out)
+{
+    DWORD i,n=0;
+
+    if(in[n++] != '{')
+        return FALSE;
+    for(i=0; i<8; i++)
+        out[7-i] = in[n++];
+    if(in[n++] != '-')
+        return FALSE;
+    for(i=0; i<4; i++)
+        out[11-i] = in[n++];
+    if(in[n++] != '-')
+        return FALSE;
+    for(i=0; i<4; i++)
+        out[15-i] = in[n++];
+    if(in[n++] != '-')
+        return FALSE;
+    for(i=0; i<2; i++)
+    {
+        out[17+i*2] = in[n++];
+        out[16+i*2] = in[n++];
+    }
+    if(in[n++] != '-')
+        return FALSE;
+    for( ; i<8; i++)
+    {
+        out[17+i*2] = in[n++];
+        out[16+i*2] = in[n++];
+    }
+    out[32]=0;
+    if(in[n++] != '}')
+        return FALSE;
+    if(in[n])
+        return FALSE;
+    return TRUE;
+}
+
+VOID MSI_CloseDatabase( VOID *arg )
+{
+    MSIDATABASE *db = (MSIDATABASE *) arg;
+
+    free_cached_tables( db );
+    IStorage_Release( db->storage );
+}
+
+UINT WINAPI MsiOpenDatabaseA(
+               LPCSTR szDBPath, LPCSTR szPersist, MSIHANDLE *phDB)
+{
+    HRESULT r = ERROR_FUNCTION_FAILED;
+    LPWSTR szwDBPath = NULL, szwPersist = NULL;
+
+    TRACE("%s %s %p\n", debugstr_a(szDBPath), debugstr_a(szPersist), phDB);
+
+    if( szDBPath )
+    {
+        szwDBPath = HEAP_strdupAtoW( GetProcessHeap(), 0, szDBPath );
+        if( !szwDBPath )
+            goto end;
+    }
+
+    if( szPersist )
+    {
+        szwPersist = HEAP_strdupAtoW( GetProcessHeap(), 0, szPersist );
+        if( !szwPersist )
+            goto end;
+    }
+
+    r = MsiOpenDatabaseW( szwDBPath, szwPersist, phDB );
+
+end:
+    if( szwPersist )
+        HeapFree( GetProcessHeap(), 0, szwPersist );
+    if( szwDBPath )
+        HeapFree( GetProcessHeap(), 0, szwDBPath );
+
+    return r;
+}
+
+UINT WINAPI MsiOpenDatabaseW(
+              LPCWSTR szDBPath, LPCWSTR szPersist, MSIHANDLE *phDB)
+{
+    IStorage *stg = NULL;
+    HRESULT r;
+    MSIHANDLE handle;
+    MSIDATABASE *db;
+    UINT ret;
+
+    TRACE("%s %s %p\n",debugstr_w(szDBPath),debugstr_w(szPersist), phDB);
+
+    if( !phDB )
+        return ERROR_INVALID_PARAMETER;
+
+    r = StgOpenStorage( szDBPath, NULL, STGM_DIRECT|STGM_READ|STGM_SHARE_DENY_WRITE, NULL, 0, &stg);
+    if( FAILED( r ) )
+    {
+        FIXME("open failed r = %08lx!\n",r);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    handle = alloc_msihandle(MSIHANDLETYPE_DATABASE, sizeof (MSIDATABASE), MSI_CloseDatabase );
+    if( !handle )
+    {
+        FIXME("Failed to allocate a handle\n");
+        ret = ERROR_FUNCTION_FAILED;
+        goto end;
+    }
+
+    db = msihandle2msiinfo( handle, MSIHANDLETYPE_DATABASE );
+    if( !db )
+    {
+        FIXME("Failed to get handle pointer \n");
+        ret = ERROR_FUNCTION_FAILED;
+        goto end;
+    }
+    db->storage = stg;
+    ret = load_string_table( db, &db->strings);
+    if( ret != ERROR_SUCCESS )
+        goto end;
+
+    *phDB = handle;
+
+    IStorage_AddRef( stg );
+end:
+    if( stg )
+        IStorage_Release( stg );
+
+    return ret;
+}
+
+UINT WINAPI MsiOpenProductA(LPCSTR szProduct, MSIHANDLE *phProduct)
+{
+    FIXME("%s %p\n",debugstr_a(szProduct), phProduct);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiOpenProductW(LPCWSTR szProduct, MSIHANDLE *phProduct)
+{
+    FIXME("%s %p\n",debugstr_w(szProduct), phProduct);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiOpenPackageA(LPCSTR szPackage, MSIHANDLE *phPackage)
+{
+    FIXME("%s %p\n",debugstr_a(szPackage), phPackage);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage)
+{
+    FIXME("%s %p\n",debugstr_w(szPackage), phPackage);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiInstallProductA(LPCSTR szPackagePath, LPCSTR szCommandLine)
+{
+    LPWSTR szwPath = NULL, szwCommand = NULL;
+    UINT r = ERROR_FUNCTION_FAILED; /* FIXME: check return code */
+
+    TRACE("%s %s\n",debugstr_a(szPackagePath), debugstr_a(szCommandLine));
+
+    if( szPackagePath )
+    {
+        szwPath = HEAP_strdupAtoW(GetProcessHeap(),0,szPackagePath);
+        if( szwPath == NULL )
+            goto end; 
+    }
+
+    if( szCommandLine )
+    {
+        szwCommand = HEAP_strdupAtoW(GetProcessHeap(),0,szCommandLine);
+        if( szwCommand == NULL )
+            goto end; 
+    }
+ 
+    r = MsiInstallProductW( szwPath, szwCommand );
+
+end:
+    if( szwPath )
+        HeapFree( GetProcessHeap(), 0, szwPath );
+    
+    if( szwCommand )
+        HeapFree( GetProcessHeap(), 0, szwCommand );
+
+    return r;
+}
+
+UINT WINAPI MsiInstallProductW(LPCWSTR szPackagePath, LPCWSTR szCommandLine)
+{
+    FIXME("%s %s\n",debugstr_w(szPackagePath), debugstr_w(szCommandLine));
+
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiConfigureProductA(
+              LPCSTR szProduct, int iInstallLevel, INSTALLSTATE eInstallState)
+{
+    FIXME("%s %d %d\n",debugstr_a(szProduct), iInstallLevel, eInstallState);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiConfigureProductW(
+              LPCWSTR szProduct, int iInstallLevel, INSTALLSTATE eInstallState)
+{
+    FIXME("%s %d %d\n",debugstr_w(szProduct), iInstallLevel, eInstallState);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductCodeA(LPCSTR szComponent, LPSTR szBuffer)
+{
+    FIXME("%s %s\n",debugstr_a(szComponent), debugstr_a(szBuffer));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductCodeW(LPCWSTR szComponent, LPWSTR szBuffer)
+{
+    FIXME("%s %s\n",debugstr_w(szComponent), debugstr_w(szBuffer));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductInfoA(LPCSTR szProduct, LPCSTR szAttribute, LPSTR szBuffer, DWORD *pcchValueBuf)
+{
+    FIXME("%s %s %p %p\n",debugstr_a(szProduct), debugstr_a(szAttribute), szBuffer, pcchValueBuf);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiGetProductInfoW(LPCWSTR szProduct, LPCWSTR szAttribute, LPWSTR szBuffer, DWORD *pcchValueBuf)
+{
+    FIXME("%s %s %p %p\n",debugstr_w(szProduct), debugstr_w(szAttribute), szBuffer, pcchValueBuf);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseImportA(LPCSTR szFolderPath, LPCSTR szFilename)
+{
+    FIXME("%s %s\n",debugstr_a(szFolderPath), debugstr_a(szFilename));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseImportW(LPCWSTR szFolderPath, LPCWSTR szFilename)
+{
+    FIXME("%s %s\n",debugstr_w(szFolderPath), debugstr_w(szFilename));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiEnableLogA(DWORD dwLogMode, LPCSTR szLogFile, BOOL fAppend)
+{
+    FIXME("%08lx %s %d\n", dwLogMode, debugstr_a(szLogFile), fAppend);
+    return ERROR_SUCCESS;
+    /* return ERROR_CALL_NOT_IMPLEMENTED; */
+}
+
+UINT WINAPI MsiEnableLogW(DWORD dwLogMode, LPCWSTR szLogFile, BOOL fAppend)
+{
+    FIXME("%08lx %s %d\n", dwLogMode, debugstr_w(szLogFile), fAppend);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+INSTALLSTATE WINAPI MsiQueryProductStateA(LPCSTR szProduct)
+{
+    FIXME("%s\n", debugstr_a(szProduct));
+    return 0;
+}
+
+INSTALLSTATE WINAPI MsiQueryProductStateW(LPCWSTR szProduct)
+{
+    FIXME("%s\n", debugstr_w(szProduct));
+    return 0;
+}
+
+INSTALLUILEVEL WINAPI MsiSetInternalUI(INSTALLUILEVEL dwUILevel, HWND *phWnd)
+{
+    FIXME("%08x %p\n", dwUILevel, phWnd);
+    return dwUILevel;
+}
+
+UINT WINAPI MsiLoadStringA(DWORD a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
+{
+    FIXME("%08lx %08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e,f);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiLoadStringW(DWORD a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
+{
+    FIXME("%08lx %08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e,f);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiMessageBoxA(DWORD a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
+{
+    FIXME("%08lx %08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e,f);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiMessageBoxW(DWORD a, DWORD b, DWORD c, DWORD d, DWORD e, DWORD f)
+{
+    FIXME("%08lx %08lx %08lx %08lx %08lx %08lx\n",a,b,c,d,e,f);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid)
+{
+    DWORD r;
+    WCHAR szwGuid[GUID_SIZE];
+
+    TRACE("%ld %p\n",index,lpguid);
+
+    r = MsiEnumProductsW(index, szwGuid);
+    if( r == ERROR_SUCCESS )
+        WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid)
+{
+    HKEY hkey = 0, hkeyFeatures = 0;
+    DWORD r;
+    WCHAR szKeyName[33];
+
+    TRACE("%ld %p\n",index,lpguid);
+
+    r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    r = RegOpenKeyW(hkey, szFeatures, &hkeyFeatures);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    r = RegEnumKeyW(hkeyFeatures, index, szKeyName, GUID_SIZE);
+
+    unsquash_guid(szKeyName, lpguid);
+
+end:
+
+    if( hkeyFeatures )
+        RegCloseKey(hkeyFeatures);
+    if( hkey )
+        RegCloseKey(hkey);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumFeaturesA(LPCSTR szProduct, DWORD index, 
+      LPSTR szFeature, LPSTR szParent)
+{
+    DWORD r;
+    WCHAR szwFeature[GUID_SIZE], szwParent[GUID_SIZE];
+    LPWSTR szwProduct = NULL;
+
+    TRACE("%s %ld %p %p\n",debugstr_a(szProduct),index,szFeature,szParent);
+
+    if( szProduct )
+    {
+        szwProduct = HEAP_strdupAtoW(GetProcessHeap(),0,szProduct);
+        if( !szwProduct )
+            return ERROR_FUNCTION_FAILED;
+    }
+
+    r = MsiEnumFeaturesW(szProduct?szwProduct:NULL, 
+                         index, szwFeature, szwParent);
+    if( r == ERROR_SUCCESS )
+    {
+        WideCharToMultiByte(CP_ACP, 0, szwFeature, -1,
+                            szFeature, GUID_SIZE, NULL, NULL);
+        WideCharToMultiByte(CP_ACP, 0, szwParent, -1,
+                            szParent, GUID_SIZE, NULL, NULL);
+    }
+
+    if( szwProduct )
+        HeapFree( GetProcessHeap(), 0, szwProduct);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumFeaturesW(LPCWSTR szProduct, DWORD index, 
+      LPWSTR szFeature, LPWSTR szParent)
+{
+    HKEY hkey = 0, hkeyFeatures = 0, hkeyProduct = 0;
+    DWORD r, sz;
+    WCHAR szRegName[GUID_SIZE];
+
+    TRACE("%s %ld %p %p\n",debugstr_w(szProduct),index,szFeature,szParent);
+
+    if( !squash_guid(szProduct, szRegName) )
+        return ERROR_INVALID_PARAMETER;
+
+    r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    r = RegOpenKeyW(hkey, szFeatures, &hkeyFeatures);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    r = RegOpenKeyW(hkeyFeatures, szRegName, &hkeyProduct);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    sz = GUID_SIZE;
+    r = RegEnumValueW(hkeyProduct, index, szFeature, &sz, NULL, NULL, NULL, NULL);
+
+end:
+    if( hkeyProduct )
+        RegCloseKey(hkeyProduct);
+    if( hkeyFeatures )
+        RegCloseKey(hkeyFeatures);
+    if( hkey )
+        RegCloseKey(hkey);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumComponentsA(DWORD index, LPSTR lpguid)
+{
+    DWORD r;
+    WCHAR szwGuid[GUID_SIZE];
+
+    TRACE("%ld %p\n",index,lpguid);
+
+    r = MsiEnumComponentsW(index, szwGuid);
+    if( r == ERROR_SUCCESS )
+        WideCharToMultiByte(CP_ACP, 0, szwGuid, -1, lpguid, GUID_SIZE, NULL, NULL);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumComponentsW(DWORD index, LPWSTR lpguid)
+{
+    HKEY hkey = 0, hkeyComponents = 0;
+    DWORD r;
+    WCHAR szKeyName[33];
+
+    TRACE("%ld %p\n",index,lpguid);
+
+    r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    r = RegOpenKeyW(hkey, szComponents, &hkeyComponents);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    r = RegEnumKeyW(hkeyComponents, index, szKeyName, GUID_SIZE);
+
+    unsquash_guid(szKeyName, lpguid);
+
+end:
+
+    if( hkeyComponents )
+        RegCloseKey(hkeyComponents);
+    if( hkey )
+        RegCloseKey(hkey);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumClientsA(LPCSTR szComponent, DWORD index, LPSTR szProduct)
+{
+    DWORD r;
+    WCHAR szwProduct[GUID_SIZE];
+    LPWSTR szwComponent = NULL;
+
+    TRACE("%s %ld %p\n",debugstr_a(szComponent),index,szProduct);
+
+    if( szComponent )
+    {
+        szwComponent = HEAP_strdupAtoW(GetProcessHeap(),0,szComponent);
+        if( !szwComponent )
+            return ERROR_FUNCTION_FAILED;
+    }
+
+    r = MsiEnumClientsW(szComponent?szwComponent:NULL, index, szwProduct);
+    if( r == ERROR_SUCCESS )
+    {
+        WideCharToMultiByte(CP_ACP, 0, szwProduct, -1,
+                            szProduct, GUID_SIZE, NULL, NULL);
+    }
+
+    if( szwComponent )
+        HeapFree( GetProcessHeap(), 0, szwComponent);
+
+    return r;
+}
+
+UINT WINAPI MsiEnumClientsW(LPCWSTR szComponent, DWORD index, LPWSTR szProduct)
+{
+    HKEY hkey = 0, hkeyComponents = 0, hkeyComp = 0;
+    DWORD r, sz;
+    WCHAR szRegName[GUID_SIZE], szValName[GUID_SIZE];
+
+    TRACE("%s %ld %p\n",debugstr_w(szComponent),index,szProduct);
+
+    if( !squash_guid(szComponent, szRegName) )
+        return ERROR_INVALID_PARAMETER;
+
+    r = RegOpenKeyW(HKEY_LOCAL_MACHINE, szInstaller, &hkey);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    r = RegOpenKeyW(hkey, szComponents, &hkeyComponents);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    r = RegOpenKeyW(hkeyComponents, szRegName, &hkeyComp);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    sz = GUID_SIZE;
+    r = RegEnumValueW(hkeyComp, index, szValName, &sz, NULL, NULL, NULL, NULL);
+    if( r != ERROR_SUCCESS )
+        goto end;
+
+    unsquash_guid(szValName, szProduct);
+
+end:
+    if( hkeyComp )
+        RegCloseKey(hkeyComp);
+    if( hkeyComponents )
+        RegCloseKey(hkeyComponents);
+    if( hkey )
+        RegCloseKey(hkey);
+
+    return r;
+}
+
+UINT WINAPI MsiProvideComponentFromDescriptorA( LPCSTR szDescriptor, LPSTR szPath, DWORD *pcchPath, DWORD *pcchArgs )
+{
+    FIXME("%s %p %p %p\n", debugstr_a(szDescriptor), szPath, pcchPath, pcchArgs );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiProvideComponentFromDescriptorW( LPCWSTR szDescriptor, LPWSTR szPath, DWORD *pcchPath, DWORD *pcchArgs )
+{
+    FIXME("%s %p %p %p\n", debugstr_w(szDescriptor), szPath, pcchPath, pcchArgs );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+HRESULT WINAPI MSI_DllGetVersion(DLLVERSIONINFO *pdvi)
+{
+    TRACE("%p\n",pdvi);
+
+    if (pdvi->cbSize != sizeof(DLLVERSIONINFO))
+        return E_INVALIDARG;
+
+    pdvi->dwMajorVersion = MSI_MAJORVERSION;
+    pdvi->dwMinorVersion = MSI_MINORVERSION;
+    pdvi->dwBuildNumber = MSI_BUILDNUMBER;
+    pdvi->dwPlatformID = 1;
+
+    return S_OK;
+}
+
+BOOL WINAPI MSI_DllCanUnloadNow(void)
+{
+    return FALSE;
+}
diff --git a/dlls/msi/msi.spec b/dlls/msi/msi.spec
new file mode 100644
index 00000000000..56c4607328a
--- /dev/null
+++ b/dlls/msi/msi.spec
@@ -0,0 +1,218 @@
+1 stdcall DllCanUnloadNow() MSI_DllCanUnloadNow
+2 stub DllGetClassObject
+3 stub DllRegisterServer
+4 stub DllUnregisterServer
+5 stub MsiAdvertiseProductA
+6 stub MsiAdvertiseProductW
+7 stdcall MsiCloseAllHandles() MsiCloseAllHandles
+8 stdcall MsiCloseHandle(long) MsiCloseHandle
+9 stub MsiCollectUserInfoA
+10 stub MsiCollectUserInfoW
+11 stub MsiConfigureFeatureA
+12 stub MsiConfigureFeatureFromDescriptorA
+13 stub MsiConfigureFeatureFromDescriptorW
+14 stub MsiConfigureFeatureW
+15 stdcall MsiConfigureProductA(str long long) MsiConfigureProductA
+16 stdcall MsiConfigureProductW(wstr long long) MsiConfigureProductW
+17 stdcall MsiCreateRecord(long) MsiCreateRecord
+18 stdcall MsiDatabaseApplyTransformA(long str long) MsiDatabaseApplyTransformA
+19 stdcall MsiDatabaseApplyTransformW(long wstr long) MsiDatabaseApplyTransformW
+20 stdcall MsiDatabaseCommit(long) MsiDatabaseCommit
+21 stub MsiDatabaseExportA
+22 stub MsiDatabaseExportW
+23 stdcall MsiDatabaseGenerateTransformA(long long str long long) MsiDatabaseGenerateTransformA
+24 stdcall MsiDatabaseGenerateTransformW(long long wstr long long) MsiDatabaseGenerateTransformW
+25 stdcall MsiDatabaseGetPrimaryKeysA(long str ptr) MsiDatabaseGetPrimaryKeysA
+26 stdcall MsiDatabaseGetPrimaryKeysW(long wstr ptr) MsiDatabaseGetPrimaryKeysW
+27 stdcall MsiDatabaseImportA(str str) MsiDatabaseImportA
+28 stdcall MsiDatabaseImportW(wstr wstr) MsiDatabaseImportW
+29 stub MsiDatabaseMergeA
+30 stub MsiDatabaseMergeW
+31 stdcall MsiDatabaseOpenViewA(str ptr) MsiDatabaseOpenViewA
+32 stdcall MsiDatabaseOpenViewW(wstr ptr) MsiDatabaseOpenViewW
+33 stdcall MsiDoActionA(long str) MsiDoActionA
+34 stdcall MsiDoActionW(long wstr) MsiDoActionW
+35 stub MsiEnableUIPreview
+36 stdcall MsiEnumClientsA(long ptr) MsiEnumClientsA
+37 stdcall MsiEnumClientsW(long ptr) MsiEnumClientsW
+38 stub MsiEnumComponentQualifiersA
+39 stub MsiEnumComponentQualifiersW
+40 stdcall MsiEnumComponentsA(long ptr) MsiEnumComponentsA
+41 stdcall MsiEnumComponentsW(long ptr) MsiEnumComponentsW
+42 stdcall MsiEnumFeaturesA(str long ptr ptr) MsiEnumFeaturesA
+43 stdcall MsiEnumFeaturesW(wstr long ptr ptr) MsiEnumFeaturesW
+44 stdcall MsiEnumProductsA(long ptr) MsiEnumProductsA
+45 stdcall MsiEnumProductsW(long ptr) MsiEnumProductsW
+46 stub MsiEvaluateConditionA
+47 stub MsiEvaluateConditionW
+48 stub MsiGetLastErrorRecord
+49 stub MsiGetActiveDatabase
+50 stub MsiGetComponentStateA
+51 stub MsiGetComponentStateW
+52 stub MsiGetDatabaseState
+53 stub MsiGetFeatureCostA
+54 stub MsiGetFeatureCostW
+55 stub MsiGetFeatureInfoA
+56 stub MsiGetFeatureInfoW
+57 stub MsiGetFeatureStateA
+58 stub MsiGetFeatureStateW
+59 stub MsiGetFeatureUsageA
+60 stub MsiGetFeatureUsageW
+61 stub MsiGetFeatureValidStatesA
+62 stub MsiGetFeatureValidStatesW
+63 stub MsiGetLanguage
+64 stub MsiGetMode
+65 stdcall MsiGetProductCodeA(str str) MsiGetProductCodeA
+66 stdcall MsiGetProductCodeW(wstr wstr) MsiGetProductCodeW
+67 stdcall MsiGetProductInfoA(str str str long) MsiGetProductInfoA
+68 stub MsiGetProductInfoFromScriptA
+69 stub MsiGetProductInfoFromScriptW
+70 stdcall MsiGetProductInfoW(wstr wstr wstr long) MsiGetProductInfoW
+71 stub MsiGetProductPropertyA
+72 stub MsiGetProductPropertyW
+73 stub MsiGetPropertyA
+74 stub MsiGetPropertyW
+75 stub MsiGetSourcePathA
+76 stub MsiGetSourcePathW
+77 stdcall MsiGetSummaryInformationA(str long ptr) MsiGetSummaryInformationA
+78 stdcall MsiGetSummaryInformationW(wstr long ptr) MsiGetSummaryInformationW
+79 stub MsiGetTargetPathA
+80 stub MsiGetTargetPathW
+81 stub MsiGetUserInfoA
+82 stub MsiGetUserInfoW
+83 stub MsiInstallMissingComponentA
+84 stub MsiInstallMissingComponentW
+85 stub MsiInstallMissingFileA
+86 stub MsiInstallMissingFileW
+87 stdcall MsiInstallProductA(str str) MsiInstallProductA
+88 stdcall MsiInstallProductW(wstr wstr) MsiInstallProductW
+89 stub MsiLocateComponentA
+90 stub MsiLocateComponentW
+91 stdcall MsiOpenDatabaseA(str str ptr) MsiOpenDatabaseA
+92 stdcall MsiOpenDatabaseW(wstr wstr ptr) MsiOpenDatabaseW
+93 stdcall MsiOpenPackageA(str ptr) MsiOpenPackageA
+94 stdcall MsiOpenPackageW(wstr ptr) MsiOpenPackageW
+95 stdcall MsiOpenProductA(str ptr) MsiOpenProductA
+96 stdcall MsiOpenProductW(wstr ptr) MsiOpenProductW
+97 stub MsiPreviewBillboardA
+98 stub MsiPreviewBillboardW
+99 stub MsiPreviewDialogA
+100 stub MsiPreviewDialogW
+101 stub MsiProcessAdvertiseScriptA
+102 stub MsiProcessAdvertiseScriptW
+103 stub MsiProcessMessage
+104 stub MsiProvideComponentA
+105 stdcall MsiProvideComponentFromDescriptorA(str ptr ptr ptr) MsiProvideComponentFromDescriptorA
+106 stdcall MsiProvideComponentFromDescriptorW(wstr ptr ptr ptr) MsiProvideComponentFromDescriptorW
+107 stub MsiProvideComponentW
+108 stub MsiProvideQualifiedComponentA
+109 stub MsiProvideQualifiedComponentW
+110 stub MsiQueryFeatureStateA
+111 stub MsiQueryFeatureStateW
+112 stdcall MsiQueryProductStateA(str) MsiQueryProductStateA
+113 stdcall MsiQueryProductStateW(wstr) MsiQueryProductStateW
+114 stdcall MsiRecordDataSize(long long) MsiRecordDataSize
+115 stdcall MsiRecordGetFieldCount(long) MsiRecordGetFieldCount
+116 stdcall MsiRecordGetInteger(long long) MsiRecordGetInteger
+117 stdcall MsiRecordGetStringA(long long ptr ptr) MsiRecordGetStringA
+118 stdcall MsiRecordGetStringW(long long ptr ptr) MsiRecordGetStringW
+119 stdcall MsiRecordIsNull(long long) MsiRecordIsNull
+120 stdcall MsiRecordReadStream(long long ptr ptr) MsiRecordReadStream
+121 stdcall MsiRecordSetInteger(long long long) MsiRecordSetInteger
+122 stdcall MsiRecordSetStreamA(long long str) MsiRecordSetStreamA
+123 stdcall MsiRecordSetStreamW(long long wstr) MsiRecordSetStreamW
+124 stdcall MsiRecordSetStringA(long long str) MsiRecordSetStringA
+125 stdcall MsiRecordSetStringW(long long wstr) MsiRecordSetStringW
+126 stub MsiReinstallFeatureA
+127 stub MsiReinstallFeatureFromDescriptorA
+128 stub MsiReinstallFeatureFromDescriptorW
+129 stub MsiReinstallFeatureW
+130 stub MsiReinstallProductA
+131 stub MsiReinstallProductW
+132 stub MsiSequenceA
+133 stub MsiSequenceW
+134 stub MsiSetComponentStateA
+135 stub MsiSetComponentStateW
+136 stub MsiSetExternalUIA
+137 stub MsiSetExternalUIW
+138 stub MsiSetFeatureStateA
+139 stub MsiSetFeatureStateW
+140 stub MsiSetInstallLevel
+141 stdcall MsiSetInternalUI(long ptr) MsiSetInternalUI
+142 stub MsiVerifyDiskSpace
+143 stub MsiSetMode
+144 stub MsiSetPropertyA
+145 stub MsiSetPropertyW
+146 stub MsiSetTargetPathA
+147 stub MsiSetTargetPathW
+148 stdcall MsiSummaryInfoGetPropertyA(long long ptr ptr ptr ptr ptr) MsiSummaryInfoGetPropertyA
+149 stdcall MsiSummaryInfoGetPropertyCount(long ptr) MsiSummaryInfoGetPropertyCount
+150 stdcall MsiSummaryInfoGetPropertyW(long long ptr ptr ptr ptr ptr) MsiSummaryInfoGetPropertyW
+151 stub MsiSummaryInfoPersist
+152 stub MsiSummaryInfoSetPropertyA
+153 stub MsiSummaryInfoSetPropertyW
+154 stub MsiUseFeatureA
+155 stub MsiUseFeatureW
+156 stub MsiVerifyPackageA
+157 stub MsiVerifyPackageW
+158 stdcall MsiViewClose(long) MsiViewClose
+159 stdcall MsiViewExecute(long long) MsiViewExecute
+160 stdcall MsiViewFetch(long ptr) MsiViewFetch
+161 stub MsiViewGetErrorA
+162 stub MsiViewGetErrorW
+163 stub MsiViewModify
+164 stdcall MsiDatabaseIsTablePersistentA(long str) MsiDatabaseIsTablePersistentA
+165 stdcall MsiDatabaseIsTablePersistentW(long wstr) MsiDatabaseIsTablePersistentW
+166 stdcall MsiViewGetColumnInfo(long long ptr) MsiViewGetColumnInfo
+167 stdcall MsiRecordClearData(long) MsiRecordClearData
+168 stdcall MsiEnableLogA(long str long) MsiEnableLogA
+169 stdcall MsiEnableLogW(long wstr long) MsiEnableLogW
+170 stdcall MsiFormatRecordA(long long ptr ptr) MsiFormatRecordA
+171 stdcall MsiFormatRecordW(long long ptr ptr) MsiFormatRecordW
+172 stub MsiGetComponentPathA
+173 stub MsiGetComponentPathW
+174 stub MsiApplyPatchA
+175 stub MsiApplyPatchW
+176 stub MsiAdvertiseScriptA
+177 stub MsiAdvertiseScriptW
+178 stub MsiGetPatchInfoA
+179 stub MsiGetPatchInfoW
+180 stub MsiEnumPatchesA
+181 stub MsiEnumPatchesW
+182 stdcall DllGetVersion(ptr) MSI_DllGetVersion
+183 stub MsiGetProductCodeFromPackageCodeA
+184 stub MsiGetProductCodeFromPackageCodeW
+185 stub MsiCreateTransformSummaryInfoA
+186 stub MsiCreateTransformSummaryInfoW
+187 stub MsiQueryFeatureStateFromDescriptorA
+188 stub MsiQueryFeatureStateFromDescriptorW
+189 stub MsiConfigureProductExA
+190 stub MsiConfigureProductExW
+191 stub MsiInvalidateFeatureCache
+192 stub MsiUseFeatureExA
+193 stub MsiUseFeatureExW
+194 stub MsiGetFileVersionA
+195 stub MsiGetFileVersionW
+196 stdcall MsiLoadStringA(long long long long long long) MsiLoadStringA
+197 stdcall MsiLoadStringW(long long long long long long) MsiLoadStringW
+198 stdcall MsiMessageBoxA(long long long long long long) MsiMessageBoxA
+199 stdcall MsiMessageBoxW(long long long long long long) MsiMessageBoxW
+200 stub MsiDecomposeDescriptorA
+201 stub MsiDecomposeDescriptorW
+202 stub MsiProvideQualifiedComponentExA
+203 stub MsiProvideQualifiedComponentExW
+204 stub MsiEnumRelatedProductsA
+205 stub MsiEnumRelatedProductsW
+206 stub MsiSetFeatureAttributesA
+207 stub MsiSetFeatureAttributesW
+208 stub MsiSourceListClearAllA
+209 stub MsiSourceListClearAllW
+210 stub MsiSourceListAddSourceA
+211 stub MsiSourceListAddSourceW
+212 stub MsiSourceListForceResolutionA
+213 stub MsiSourceListForceResolutionW
+214 stub MsiIsProductElevatedA
+215 stub MsiIsProductElevatedW
+216 stub MsiGetShortcutTargetA
+217 stub MsiGetShortcutTargetW
+
diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h
new file mode 100644
index 00000000000..e1e2eb3aeee
--- /dev/null
+++ b/dlls/msi/msipriv.h
@@ -0,0 +1,186 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __WINE_MSI_PRIVATE__
+#define __WINE_MSI_PRIVATE__
+
+#include "msi.h"
+#include "msiquery.h"
+#include "objidl.h"
+
+#define MSI_DATASIZEMASK 0x00ff
+#define MSITYPE_VALID    0x0100
+#define MSITYPE_STRING   0x0800
+#define MSITYPE_NULLABLE 0x1000
+#define MSITYPE_KEY      0x2000
+
+struct tagMSITABLE;
+typedef struct tagMSITABLE MSITABLE;
+
+typedef struct pool_data_tag
+{
+    USHORT *data;
+    UINT size;
+} pool_data;
+
+typedef struct string_data_tag
+{
+    CHAR *data;
+    UINT size;
+} string_data;
+
+typedef struct string_table_tag
+{
+    pool_data   pool;
+    string_data info;
+} string_table;
+
+typedef struct tagMSIDATABASE
+{
+    IStorage *storage;
+    string_table strings;
+    MSITABLE *first_table, *last_table;
+} MSIDATABASE;
+
+struct tagMSIVIEW;
+
+typedef struct tagMSIVIEWOPS
+{
+    /*
+     * fetch_int - reads one integer from {row,col} in the table
+     *
+     *  This function should be called after the execute method.
+     *  Data returned by the function should not change until 
+     *   close or delete is called.
+     *  To get a string value, query the database's string table with
+     *   the integer value returned from this function.
+     */
+    UINT (*fetch_int)( struct tagMSIVIEW *, UINT row, UINT col, UINT *val );
+
+    /*
+     * execute - loads the underlying data into memory so it can be read
+     */
+    UINT (*execute)( struct tagMSIVIEW *, MSIHANDLE );
+
+    /*
+     * close - clears the data read by execute from memory
+     */
+    UINT (*close)( struct tagMSIVIEW * );
+
+    /*
+     * get_dimensions - returns the number of rows or columns in a table.
+     *
+     *  The number of rows can only be queried after the execute method
+     *   is called. The number of columns can be queried at any time.
+     */
+    UINT (*get_dimensions)( struct tagMSIVIEW *, UINT *rows, UINT *cols );
+
+    /*
+     * get_column_info - returns the name and type of a specific column
+     *
+     *  The name is HeapAlloc'ed by this function and should be freed by
+     *   the caller.
+     *  The column information can be queried at any time.
+     */
+    UINT (*get_column_info)( struct tagMSIVIEW *, UINT n, LPWSTR *name, UINT *type );
+
+    /*
+     * modify - not yet implemented properly
+     */
+    UINT (*modify)( struct tagMSIVIEW *, MSIMODIFY, MSIHANDLE );
+
+    /*
+     * delete - destroys the structure completely
+     */
+    UINT (*delete)( struct tagMSIVIEW * );
+
+} MSIVIEWOPS;
+
+typedef struct tagMSIVIEW
+{
+    MSIVIEWOPS   *ops;
+} MSIVIEW;
+
+typedef struct tagMSISUMMARYINFO
+{
+    IPropertyStorage *propstg;
+} MSISUMMARYINFO;
+
+typedef VOID (*msihandledestructor)( VOID * );
+
+typedef struct tagMSIHANDLEINFO
+{
+    UINT magic;
+    UINT type;
+    msihandledestructor destructor;
+    struct tagMSIHANDLEINFO *next;
+    struct tagMSIHANDLEINFO *prev;
+} MSIHANDLEINFO;
+
+#define MSIHANDLETYPE_ANY 0
+#define MSIHANDLETYPE_DATABASE 1
+#define MSIHANDLETYPE_SUMMARYINFO 2
+#define MSIHANDLETYPE_VIEW 3
+#define MSIHANDLETYPE_RECORD 4
+
+#define MSI_MAJORVERSION 1
+#define MSI_MINORVERSION 10
+#define MSI_BUILDNUMBER 1029
+
+#define GUID_SIZE 39
+
+#define MSIHANDLE_MAGIC 0x4d434923
+#define MSIMAXHANDLES 0x80
+
+#define MSISUMINFO_OFFSET 0x30LL
+
+extern void *msihandle2msiinfo(MSIHANDLE handle, UINT type);
+
+MSIHANDLE alloc_msihandle(UINT type, UINT extra, msihandledestructor destroy);
+
+/* add this table to the list of cached tables in the database */
+extern void add_table(MSIDATABASE *db, MSITABLE *table);
+extern void remove_table( MSIDATABASE *db, MSITABLE *table );
+extern void free_table( MSIDATABASE *db, MSITABLE *table );
+extern void free_cached_tables( MSIDATABASE *db );
+extern UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **table);
+extern UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **table);
+extern UINT dump_string_table(MSIDATABASE *db);
+extern UINT load_string_table( MSIDATABASE *db, string_table *pst);
+extern UINT msi_id2string( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz );
+extern LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid);
+
+UINT VIEW_find_column( MSIVIEW *view, LPWSTR name, UINT *n );
+
+/* FIXME! should get rid of that */
+#include "winnls.h"
+inline static LPWSTR HEAP_strdupAtoW( HANDLE heap, DWORD flags, LPCSTR str )
+{
+    LPWSTR ret;
+    INT len;
+
+    if (!str) return NULL;
+    len = MultiByteToWideChar( CP_ACP, 0, str, -1, NULL, 0 );
+    ret = HeapAlloc( heap, flags, len * sizeof(WCHAR) );
+    if (ret) MultiByteToWideChar( CP_ACP, 0, str, -1, ret, len );
+    return ret;
+}
+
+#endif /* __WINE_MSI_PRIVATE__ */
diff --git a/dlls/msi/msiquery.c b/dlls/msi/msiquery.c
new file mode 100644
index 00000000000..ac4b3a92515
--- /dev/null
+++ b/dlls/msi/msiquery.c
@@ -0,0 +1,376 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tagMSIQUERY
+{
+    MSIVIEW *view;
+    UINT row;
+    MSIDATABASE *db;
+} MSIQUERY;
+
+UINT WINAPI MsiDatabaseIsTablePersistentA(
+              MSIHANDLE hDatabase, LPSTR szTableName)
+{
+    FIXME("%lx %s\n", hDatabase, debugstr_a(szTableName));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseIsTablePersistentW(
+              MSIHANDLE hDatabase, LPWSTR szTableName)
+{
+    FIXME("%lx %s\n", hDatabase, debugstr_w(szTableName));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+void MSI_CloseView( VOID *arg )
+{
+    MSIQUERY *query = arg;
+
+    if( query->view && query->view->ops->delete )
+        query->view->ops->delete( query->view );
+}
+
+UINT VIEW_find_column( MSIVIEW *table, LPWSTR name, UINT *n )
+{
+    LPWSTR col_name;
+    UINT i, count, r;
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    for( i=1; i<=count; i++ )
+    {
+        INT x;
+
+        col_name = NULL;
+        r = table->ops->get_column_info( table, i, &col_name, NULL );
+        if( r != ERROR_SUCCESS )
+            return r;
+        x = lstrcmpW( name, col_name );
+        HeapFree( GetProcessHeap(), 0, col_name );
+        if( !x )
+        {
+            *n = i;
+            return ERROR_SUCCESS;
+        }
+    }
+
+    return ERROR_INVALID_PARAMETER;
+}
+
+UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE hdb,
+              LPCSTR szQuery, MSIHANDLE *phView)
+{
+    UINT r;
+    LPCWSTR szwQuery;
+
+    TRACE("%ld %s %p\n", hdb, debugstr_a(szQuery), phView);
+
+    if( szQuery )
+    {
+        szwQuery = HEAP_strdupAtoW( GetProcessHeap(), 0, szQuery );
+        if( !szwQuery )
+            return ERROR_FUNCTION_FAILED;
+    }
+    else
+        szwQuery = NULL;
+
+    r = MsiDatabaseOpenViewW( hdb, szwQuery, phView);
+
+    return r;
+}
+
+UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE hdb,
+              LPCWSTR szQuery, MSIHANDLE *phView)
+{
+    MSIDATABASE *db;
+    MSIHANDLE handle;
+    MSIQUERY *query;
+    UINT r;
+
+    TRACE("%s %p\n", debugstr_w(szQuery), phView);
+
+    if( !szQuery)
+        return ERROR_INVALID_PARAMETER;
+
+    db = msihandle2msiinfo( hdb, MSIHANDLETYPE_DATABASE );
+    if( !db )
+        return ERROR_INVALID_HANDLE;
+
+    /* pre allocate a handle to hold a pointer to the view */
+    handle = alloc_msihandle( MSIHANDLETYPE_VIEW, sizeof (MSIQUERY), MSI_CloseView );
+    if( !handle )
+        return ERROR_FUNCTION_FAILED;
+    query = msihandle2msiinfo( handle, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_FUNCTION_FAILED;
+
+    query->row = 0;
+    query->db = db;
+    query->view = NULL;
+
+    r = MSI_ParseSQL( db, szQuery, &query->view );
+    if( r != ERROR_SUCCESS )
+    {
+        MsiCloseHandle( handle );
+        return r;
+    }
+
+    *phView = handle;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiViewFetch(MSIHANDLE hView, MSIHANDLE *record)
+{
+    MSIQUERY *query;
+    MSIVIEW *view;
+    MSIHANDLE handle;
+    UINT row_count = 0, col_count = 0, i, ival, ret, type;
+
+    TRACE("%ld %p\n", hView, record);
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+    view = query->view;
+    if( !view )
+        return ERROR_FUNCTION_FAILED;
+
+    ret = view->ops->get_dimensions( view, &row_count, &col_count );
+    if( ret )
+        return ret;
+    if( !col_count )
+        return ERROR_INVALID_PARAMETER;
+
+    if( query->row >= row_count )
+        return ERROR_NO_MORE_ITEMS;
+
+    handle = MsiCreateRecord( col_count );
+    if( !handle )
+        return ERROR_FUNCTION_FAILED;
+
+    for( i=1; i<=col_count; i++ )
+    {
+        ret = view->ops->get_column_info( view, i, NULL, &type );
+        if( ret )
+        {
+            ERR("Error getting column type for %d\n", i );
+            continue;
+        }
+        ret = view->ops->fetch_int( view, query->row, i, &ival );
+        if( ret )
+        {
+            ERR("Error fetching data for %d\n", i );
+            continue;
+        }
+        if( ! (type & MSITYPE_VALID ) )
+            ERR("Invalid type!\n");
+
+        /* check if it's nul (0) - if so, don't set anything */
+        if( !ival )
+            continue;
+
+        if( type & MSITYPE_STRING )
+        {
+            LPWSTR sval = MSI_makestring( query->db, ival );
+            MsiRecordSetStringW( handle, i, sval );
+            HeapFree( GetProcessHeap(), 0, sval );
+        }
+        else
+        {
+            if( (type & MSI_DATASIZEMASK) == 2 )
+                MsiRecordSetInteger( handle, i, ival - (1<<15) );
+            else
+                MsiRecordSetInteger( handle, i, ival - (1<<31) );
+        }
+    }
+    query->row ++;
+
+    *record = handle;
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiViewClose(MSIHANDLE hView)
+{
+    MSIQUERY *query;
+    MSIVIEW *view;
+
+    TRACE("%ld\n", hView );
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+
+    view = query->view;
+    if( !view )
+        return ERROR_FUNCTION_FAILED;
+    if( !view->ops->close )
+        return ERROR_FUNCTION_FAILED;
+
+    return view->ops->close( view );
+}
+
+UINT WINAPI MsiViewExecute(MSIHANDLE hView, MSIHANDLE hRec)
+{
+    MSIQUERY *query;
+    MSIVIEW *view;
+
+    TRACE("%ld %ld\n", hView, hRec);
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+
+    view = query->view;
+    if( !view )
+        return ERROR_FUNCTION_FAILED;
+    if( !view->ops->execute )
+        return ERROR_FUNCTION_FAILED;
+    query->row = 0;
+
+    return view->ops->execute( view, hRec );
+}
+
+UINT WINAPI MsiViewGetColumnInfo(MSIHANDLE hView, MSICOLINFO info, MSIHANDLE *hRec)
+{
+    MSIVIEW *view;
+    MSIQUERY *query;
+    MSIHANDLE handle;
+    UINT ret, i, count = 0, type;
+    LPWSTR name;
+
+    TRACE("%ld %d %p\n", hView, info, hRec);
+
+    query = msihandle2msiinfo( hView, MSIHANDLETYPE_VIEW );
+    if( !query )
+        return ERROR_INVALID_HANDLE;
+
+    view = query->view;
+    if( !view )
+        return ERROR_FUNCTION_FAILED;
+
+    if( !view->ops->get_dimensions )
+        return ERROR_FUNCTION_FAILED;
+
+    ret = view->ops->get_dimensions( view, NULL, &count );
+    if( ret )
+        return ret;
+    if( !count )
+        return ERROR_INVALID_PARAMETER;
+
+    handle = MsiCreateRecord( count );
+    if( !handle )
+        return ERROR_FUNCTION_FAILED;
+
+    for( i=0; i<count; i++ )
+    {
+        name = NULL;
+        ret = view->ops->get_column_info( view, i+1, &name, &type );
+        if( ret != ERROR_SUCCESS )
+            continue;
+        MsiRecordSetStringW( handle, i+1, name );
+        HeapFree( GetProcessHeap(), 0, name );
+    }
+
+    *hRec = handle;
+
+    return ERROR_SUCCESS;
+}
+
+
+UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
+{
+    FIXME("%ld %s\n", hInstall, debugstr_a(szAction) );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction )
+{
+    FIXME("%ld %s\n", hInstall, debugstr_w(szAction) );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseApplyTransformA( MSIHANDLE hdb, 
+                 LPCSTR szTransformFile, int iErrorCond)
+{
+    FIXME("%ld %s %d\n", hdb, debugstr_a(szTransformFile), iErrorCond);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseApplyTransformW( MSIHANDLE hdb, 
+                 LPCWSTR szTransformFile, int iErrorCond)
+{
+    FIXME("%ld %s %d\n", hdb, debugstr_w(szTransformFile), iErrorCond);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseGenerateTransformA( MSIHANDLE hdb, MSIHANDLE hdbref,
+                 LPCSTR szTransformFile, int iReserved1, int iReserved2 )
+{
+    FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
+           debugstr_a(szTransformFile), iReserved1, iReserved2);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseGenerateTransformW( MSIHANDLE hdb, MSIHANDLE hdbref,
+                 LPCWSTR szTransformFile, int iReserved1, int iReserved2 )
+{
+    FIXME("%ld %ld %s %d %d\n", hdb, hdbref, 
+           debugstr_w(szTransformFile), iReserved1, iReserved2);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseCommit( MSIHANDLE hdb )
+{
+    FIXME("%ld\n", hdb);
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE hdb, 
+                    LPCSTR table, MSIHANDLE* rec)
+{
+    FIXME("%ld %s %p\n", hdb, debugstr_a(table), rec);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiDatabaseGetPrimaryKeysW(MSIHANDLE hdb,
+                    LPCWSTR table, MSIHANDLE* rec)
+{
+    FIXME("%ld %s %p\n", hdb, debugstr_w(table), rec);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
diff --git a/dlls/msi/order.c b/dlls/msi/order.c
new file mode 100644
index 00000000000..ba900472293
--- /dev/null
+++ b/dlls/msi/order.c
@@ -0,0 +1,328 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSIORDERVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSIVIEW       *table;
+    UINT          *reorder;
+    UINT           num_cols;
+    UINT           cols[1];
+} MSIORDERVIEW;
+
+static UINT ORDER_compare( MSIORDERVIEW *ov, UINT a, UINT b, UINT *swap )
+{
+    UINT r, i, a_val = 0, b_val = 0;
+
+    *swap = 0;
+    for( i=0; i<ov->num_cols; i++ )
+    {
+        r = ov->table->ops->fetch_int( ov->table, a, ov->cols[i], &a_val );
+        if( r != ERROR_SUCCESS )
+            return r;
+
+        r = ov->table->ops->fetch_int( ov->table, b, ov->cols[i], &b_val );
+        if( r != ERROR_SUCCESS )
+            return r;
+
+        if( a_val != b_val )
+        {
+            if( a_val > b_val )
+                *swap = 1;
+            break;
+        }
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ORDER_mergesort( MSIORDERVIEW *ov, UINT left, UINT right )
+{
+    UINT r, centre = (left + right)/2, temp, swap = 0, i, j;
+    UINT *array = ov->reorder;
+
+    if( left == right )
+        return ERROR_SUCCESS;
+
+    /* sort the left half */
+    r = ORDER_mergesort( ov, left, centre );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* sort the right half */
+    r = ORDER_mergesort( ov, centre+1, right );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    for( i=left, j=centre+1; (i<=centre) && (j<=right); i++ )
+    {
+        r = ORDER_compare( ov, array[i], array[j], &swap );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( swap )
+        { 
+            temp = array[j];
+            memmove( &array[i+1], &array[i], (j-i)*sizeof (UINT) );
+            array[i] = temp;
+            j++;
+            centre++;
+        }
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ORDER_verify( MSIORDERVIEW *ov, UINT num_rows )
+{
+    UINT i, swap, r;
+
+    for( i=1; i<num_rows; i++ )
+    {
+        r = ORDER_compare( ov, ov->reorder[i-1], ov->reorder[i], &swap );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( !swap )
+            continue;
+        ERR("Bad order! %d\n", i);
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ORDER_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p %d %d %p\n", ov, row, col, val );
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    row = ov->reorder[ row ];
+
+    return ov->table->ops->fetch_int( ov->table, row, col, val );
+}
+
+static UINT ORDER_execute( struct tagMSIVIEW *view, MSIHANDLE record )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+    UINT r, num_rows = 0, i;
+
+    TRACE("%p %ld\n", ov, record);
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    r = ov->table->ops->execute( ov->table, record );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = ov->table->ops->get_dimensions( ov->table, &num_rows, NULL );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    ov->reorder = HeapAlloc( GetProcessHeap(), 0, num_rows*sizeof(UINT) );
+    if( !ov->reorder )
+        return ERROR_FUNCTION_FAILED;
+
+    for( i=0; i<num_rows; i++ )
+        ov->reorder[i] = i;
+
+    r = ORDER_mergesort( ov, 0, num_rows - 1 );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = ORDER_verify( ov, num_rows );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    return ERROR_SUCCESS;
+}
+
+static UINT ORDER_close( struct tagMSIVIEW *view )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p\n", ov );
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( ov->reorder )
+        HeapFree( GetProcessHeap(), 0, ov->reorder );
+    ov->reorder = NULL;
+
+    return ov->table->ops->close( ov->table );
+}
+
+static UINT ORDER_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p %p %p\n", ov, rows, cols );
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return ov->table->ops->get_dimensions( ov->table, rows, cols );
+}
+
+static UINT ORDER_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p %d %p %p\n", ov, n, name, type );
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return ov->table->ops->get_column_info( ov->table, n, name, type );
+}
+
+static UINT ORDER_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p %d %ld\n", ov, eModifyMode, hrec );
+
+    if( !ov->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return ov->table->ops->modify( ov->table, eModifyMode, hrec );
+}
+
+static UINT ORDER_delete( struct tagMSIVIEW *view )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+
+    TRACE("%p\n", ov );
+
+    if( ov->table )
+        ov->table->ops->delete( ov->table );
+
+    if( ov->reorder )
+        HeapFree( GetProcessHeap(), 0, ov->reorder );
+    ov->reorder = NULL;
+
+    HeapFree( GetProcessHeap(), 0, ov );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS order_ops =
+{
+    ORDER_fetch_int,
+    ORDER_execute,
+    ORDER_close,
+    ORDER_get_dimensions,
+    ORDER_get_column_info,
+    ORDER_modify,
+    ORDER_delete
+};
+
+UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
+{
+    MSIORDERVIEW *ov = NULL;
+    UINT count = 0, r;
+
+    TRACE("%p\n", ov );
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("can't get table dimensions\n");
+        return r;
+    }
+
+    ov = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
+                    sizeof *ov + sizeof (UINT) * count );
+    if( !ov )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    ov->view.ops = &order_ops;
+    ov->db = db;
+    ov->table = table;
+    ov->reorder = NULL;
+    ov->num_cols = 0;
+    *view = (MSIVIEW*) ov;
+
+    return ERROR_SUCCESS;
+}
+
+UINT ORDER_AddColumn( MSIVIEW *view, LPWSTR name )
+{
+    MSIORDERVIEW *ov = (MSIORDERVIEW*)view;
+    UINT n, count, r;
+    MSIVIEW *table;
+
+    TRACE("%p adding %s\n", ov, debugstr_w( name ) );
+
+    if( ov->view.ops != &order_ops )
+        return ERROR_FUNCTION_FAILED;
+
+    table = ov->table;
+    if( !table )
+        return ERROR_FUNCTION_FAILED;
+    if( !table->ops->get_dimensions )
+        return ERROR_FUNCTION_FAILED;
+    if( !table->ops->get_column_info )
+        return ERROR_FUNCTION_FAILED;
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    if( ov->num_cols >= count )
+        return ERROR_FUNCTION_FAILED;
+
+    r = VIEW_find_column( table, name, &n );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    ov->cols[ov->num_cols] = n;
+    TRACE("Ordering by column %s (%d)\n", debugstr_w( name ), n);
+
+    ov->num_cols++;
+
+    return ERROR_SUCCESS;
+}
diff --git a/dlls/msi/query.h b/dlls/msi/query.h
new file mode 100644
index 00000000000..663a6737737
--- /dev/null
+++ b/dlls/msi/query.h
@@ -0,0 +1,88 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __WINE_MSI_QUERY_H
+#define __WINE_MSI_QUERY_H
+
+#include "winbase.h"
+#include "objidl.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+
+
+#define OP_EQ       1
+#define OP_AND      2
+#define OP_OR       3
+#define OP_GT       4
+#define OP_LT       5
+#define OP_LE       6
+#define OP_GE       7
+#define OP_NE       8
+#define OP_ISNULL   9
+#define OP_NOTNULL  10
+
+#define EXPR_COMPLEX  1
+#define EXPR_COLUMN   2
+#define EXPR_COL_NUMBER 3
+#define EXPR_IVAL     4
+#define EXPR_SVAL     5
+#define EXPR_UVAL     6
+
+struct complex_expr
+{
+    UINT op;
+    struct expr *left;
+    struct expr *right;
+};
+
+struct expr
+{
+    int type;
+    union
+    {
+        struct complex_expr expr;
+        INT   ival;
+        UINT  uval;
+        LPWSTR sval;
+        LPWSTR column;
+        UINT col_number;
+    } u;
+};
+
+
+UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phView);
+
+UINT TABLE_CreateView( MSIDATABASE *db, LPWSTR name, MSIVIEW **view );
+
+UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table );
+UINT SELECT_AddColumn( MSIVIEW *select, LPWSTR name );
+
+UINT DISTINCT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table );
+
+UINT ORDER_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table );
+UINT ORDER_AddColumn( MSIVIEW *group, LPWSTR name );
+
+UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table );
+UINT WHERE_AddCondition( MSIVIEW *view, struct expr *condition );
+
+int sqliteGetToken(const WCHAR *z, int *tokenType);
+
+#endif /* __WINE_MSI_QUERY_H */
diff --git a/dlls/msi/record.c b/dlls/msi/record.c
new file mode 100644
index 00000000000..4bce8d3b470
--- /dev/null
+++ b/dlls/msi/record.c
@@ -0,0 +1,442 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "winuser.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "objidl.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+#define MSIFIELD_NULL   0
+#define MSIFIELD_INT    1
+#define MSIFIELD_STR    2
+#define MSIFIELD_WSTR   3
+#define MSIFIELD_STREAM 4
+
+/* maybe we can use a Variant instead of doing it ourselves? */
+typedef struct tagMSIFIELD
+{
+    UINT type;
+    union
+    {
+        INT iVal;
+        LPSTR szVal;
+        LPWSTR szwVal;
+        IStream *stream;
+    } u;
+} MSIFIELD;
+
+typedef struct tagMSIRECORD
+{
+    UINT count;       /* as passed to MsiCreateRecord */
+    MSIFIELD fields[1]; /* nb. array size is count+1 */
+} MSIRECORD;
+
+void MSI_FreeField( MSIFIELD *field )
+{
+    switch( field->type )
+    {
+    case MSIFIELD_NULL:
+    case MSIFIELD_INT:
+        break;
+    case MSIFIELD_STR:
+        HeapFree( GetProcessHeap(), 0, field->u.szVal);
+        break;
+    case MSIFIELD_WSTR:
+        HeapFree( GetProcessHeap(), 0, field->u.szwVal);
+        break;
+    case MSIFIELD_STREAM:
+        IStream_Release( field->u.stream );
+        break;
+    default:
+        ERR("Invalid field type %d\n", field->type);
+    }
+}
+
+void MSI_CloseRecord( VOID *arg )
+{
+    MSIRECORD *rec = (MSIRECORD *) arg;
+    UINT i;
+
+    for( i=0; i<rec->count; i++ )
+        MSI_FreeField( &rec->fields[i] );
+}
+
+MSIHANDLE WINAPI MsiCreateRecord( unsigned int cParams )
+{
+    MSIHANDLE handle = 0;
+    UINT sz;
+    MSIRECORD *rec;
+
+    TRACE("%d\n", cParams);
+
+    sz = sizeof (MSIRECORD) + sizeof(MSIFIELD)*(cParams+1) ;
+    handle = alloc_msihandle( MSIHANDLETYPE_RECORD, sz, MSI_CloseRecord );
+    if( !handle )
+        return 0;
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return 0;
+
+    rec->count = cParams;
+
+    return handle;
+}
+
+unsigned int WINAPI MsiRecordGetFieldCount( MSIHANDLE handle )
+{
+    MSIRECORD *rec;
+
+    TRACE("%ld\n", handle );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+    {
+        ERR("Record not found!\n");
+        return 0;
+    }
+
+    return rec->count;
+}
+
+static BOOL string2intA( LPCSTR str, int *out )
+{
+    int x = 0;
+    LPCSTR p = str;
+
+    if( *p == '-' ) /* skip the minus sign */
+        p++;
+    while ( *p )
+    {
+        if( (*p < '0') || (*p > '9') )
+            return FALSE;
+        x *= 10;
+        x += (*p - '0');
+        p++;
+    }
+
+    if( str[0] == '-' ) /* check if it's negative */
+        x = -x;
+    *out = x; 
+
+    return TRUE;
+}
+
+static BOOL string2intW( LPCWSTR str, int *out )
+{
+    int x = 0;
+    LPCWSTR p = str;
+
+    if( *p == '-' ) /* skip the minus sign */
+        p++;
+    while ( *p )
+    {
+        if( (*p < '0') || (*p > '9') )
+            return FALSE;
+        x *= 10;
+        x += (*p - '0');
+        p++;
+    }
+
+    if( str[0] == '-' ) /* check if it's negative */
+        x = -x;
+    *out = x; 
+
+    return TRUE;
+}
+
+int WINAPI MsiRecordGetInteger( MSIHANDLE handle, unsigned int iField)
+{
+    MSIRECORD *rec;
+    int ret = 0;
+
+    TRACE("%ld %d\n", handle, iField );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return MSI_NULL_INTEGER;
+
+    if( iField > rec->count )
+        return MSI_NULL_INTEGER;
+
+    switch( rec->fields[iField].type )
+    {
+    case MSIFIELD_INT:
+        return rec->fields[iField].u.iVal;
+    case MSIFIELD_STR:
+        if( string2intA( rec->fields[iField].u.szVal, &ret ) )
+            return ret;
+        return MSI_NULL_INTEGER;
+    case MSIFIELD_WSTR:
+        if( string2intW( rec->fields[iField].u.szwVal, &ret ) )
+            return ret;
+        return MSI_NULL_INTEGER;
+    default:
+        break;
+    }
+
+    return MSI_NULL_INTEGER;
+}
+
+UINT WINAPI MsiRecordClearData( MSIHANDLE handle )
+{
+    MSIRECORD *rec;
+    UINT i;
+
+    TRACE("%ld\n", handle );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    for( i=0; i<=rec->count; i++)
+    {
+        MSI_FreeField( &rec->fields[i] );
+        rec->fields[i].type = MSIFIELD_NULL;
+        rec->fields[i].u.iVal = 0;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiRecordSetInteger( MSIHANDLE handle, unsigned int iField, int iVal )
+{
+    MSIRECORD *rec;
+
+    TRACE("%ld %u %d\n", handle,iField, iVal);
+    
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    MSI_FreeField( &rec->fields[iField] );
+    rec->fields[iField].type = MSIFIELD_INT;
+    rec->fields[iField].u.iVal = iVal;
+
+    return ERROR_SUCCESS;
+}
+
+BOOL WINAPI MsiRecordIsNull( MSIHANDLE handle, unsigned int iField )
+{
+    MSIRECORD *rec;
+
+    TRACE("%ld %d\n", handle,iField );
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    if( iField > rec->count ) 
+        return TRUE;
+
+    if( rec->fields[iField].type == MSIFIELD_NULL )
+        return TRUE;
+
+    return FALSE;
+}
+
+UINT WINAPI MsiRecordGetStringA(MSIHANDLE handle, unsigned int iField, 
+               LPSTR szValue, DWORD *pcchValue)
+{
+    MSIRECORD *rec;
+    UINT len=0, ret;
+    CHAR buffer[16];
+
+    TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    if( iField > rec->count )
+        return ERROR_INVALID_PARAMETER;
+
+    ret = ERROR_SUCCESS;
+    switch( rec->fields[iField].type )
+    {
+    case MSIFIELD_INT:
+        wsprintfA(buffer, "%d", rec->fields[iField].u.iVal);
+        len = lstrlenA( buffer );
+        lstrcpynA(szValue, buffer, *pcchValue);
+        break;
+    case MSIFIELD_STR:
+        len = lstrlenA( rec->fields[iField].u.szVal );
+        lstrcpynA(szValue, rec->fields[iField].u.szVal, *pcchValue);
+        break;
+    case MSIFIELD_WSTR:
+        len = WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
+                             NULL, 0 , NULL, NULL);
+        WideCharToMultiByte( CP_ACP, 0, rec->fields[iField].u.szwVal, -1,
+                             szValue, *pcchValue, NULL, NULL);
+        break;
+    default:
+        ret = ERROR_INVALID_PARAMETER;
+        break;
+    }
+
+    if( *pcchValue < len )
+        ret = ERROR_MORE_DATA;
+    *pcchValue = len;
+
+    return ret;
+}
+
+UINT WINAPI MsiRecordGetStringW(MSIHANDLE handle, unsigned int iField,
+               LPWSTR szValue, DWORD *pcchValue)
+{
+    MSIRECORD *rec;
+    UINT len=0, ret;
+    WCHAR buffer[16];
+    const WCHAR szFormat[] = { '%','d',0 };
+
+    TRACE("%ld %d %p %p\n", handle, iField, szValue, pcchValue);
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    if( iField > rec->count )
+        return ERROR_INVALID_PARAMETER;
+
+    ret = ERROR_SUCCESS;
+    switch( rec->fields[iField].type )
+    {
+    case MSIFIELD_INT:
+        wsprintfW(buffer, szFormat, rec->fields[iField].u.iVal);
+        len = lstrlenW( buffer );
+        lstrcpynW(szValue, buffer, *pcchValue);
+        break;
+    case MSIFIELD_WSTR:
+        len = lstrlenW( rec->fields[iField].u.szwVal );
+        lstrcpynW(szValue, rec->fields[iField].u.szwVal, *pcchValue);
+        break;
+    case MSIFIELD_STR:
+        len = MultiByteToWideChar( CP_ACP, 0, rec->fields[iField].u.szVal, -1,
+                             NULL, 0 );
+        MultiByteToWideChar( CP_ACP, 0, rec->fields[iField].u.szVal, -1,
+                             szValue, *pcchValue);
+        break;
+    default:
+        break;
+    }
+
+    if( *pcchValue < len )
+        ret = ERROR_MORE_DATA;
+    *pcchValue = len;
+
+    return ret;
+}
+
+UINT WINAPI MsiRecordDataSize(MSIHANDLE hRecord, unsigned int iField)
+{
+    FIXME("%ld %d\n", hRecord, iField);
+    return 0;
+}
+
+UINT WINAPI MsiRecordSetStringA( MSIHANDLE handle, unsigned int iField, LPCSTR szValue )
+{
+    MSIRECORD *rec;
+    LPSTR str;
+
+    TRACE("%ld %d %s\n", handle, iField, debugstr_a(szValue));
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    str = HeapAlloc( GetProcessHeap(), 0, (lstrlenA(szValue) + 1)*sizeof str[0]);
+    lstrcpyA( str, szValue );
+
+    MSI_FreeField( &rec->fields[iField] );
+    rec->fields[iField].type = MSIFIELD_STR;
+    rec->fields[iField].u.szVal = str;
+
+    return 0;
+}
+
+UINT WINAPI MsiRecordSetStringW( MSIHANDLE handle, unsigned int iField, LPCWSTR szValue )
+{
+    MSIRECORD *rec;
+    LPWSTR str;
+
+    TRACE("%ld %d %s\n", handle, iField, debugstr_w(szValue));
+
+    rec = msihandle2msiinfo( handle, MSIHANDLETYPE_RECORD );
+    if( !rec )
+        return ERROR_INVALID_HANDLE;
+
+    if( iField > rec->count )
+        return ERROR_INVALID_FIELD;
+
+    str = HeapAlloc( GetProcessHeap(), 0, (lstrlenW(szValue) + 1)*sizeof str[0]);
+    lstrcpyW( str, szValue );
+
+    MSI_FreeField( &rec->fields[iField] );
+    rec->fields[iField].type = MSIFIELD_WSTR;
+    rec->fields[iField].u.szwVal = str;
+
+    return 0;
+}
+
+UINT WINAPI MsiFormatRecordA(MSIHANDLE hInstall, MSIHANDLE hRecord, LPSTR szResult, DWORD *sz)
+{
+    FIXME("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiFormatRecordW(MSIHANDLE hInstall, MSIHANDLE hRecord, LPWSTR szResult, DWORD *sz)
+{
+    FIXME("%ld %ld %p %p\n", hInstall, hRecord, szResult, sz);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiRecordSetStreamA(MSIHANDLE hRecord, unsigned int iField, LPCSTR szFilename)
+{
+    FIXME("%ld %d %s\n", hRecord, iField, debugstr_a(szFilename));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiRecordSetStreamW(MSIHANDLE hRecord, unsigned int iField, LPCWSTR szFilename)
+{
+    FIXME("%ld %d %s\n", hRecord, iField, debugstr_w(szFilename));
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiRecordReadStream(MSIHANDLE handle, unsigned int iField, char *buf, DWORD *sz)
+{
+    FIXME("%ld %d %p %p\n",handle,iField,buf,sz);
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
diff --git a/dlls/msi/select.c b/dlls/msi/select.c
new file mode 100644
index 00000000000..587707574c4
--- /dev/null
+++ b/dlls/msi/select.c
@@ -0,0 +1,224 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSISELECTVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSIVIEW       *table;
+    UINT           num_cols;
+    UINT           max_cols;
+    UINT           cols[1];
+} MSISELECTVIEW;
+
+static UINT SELECT_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %d %d %p\n", sv, row, col, val );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( (col==0) || (col>sv->num_cols) )
+         return ERROR_FUNCTION_FAILED;
+
+    col = sv->cols[ col - 1 ];
+
+    return sv->table->ops->fetch_int( sv->table, row, col, val );
+}
+
+static UINT SELECT_execute( struct tagMSIVIEW *view, MSIHANDLE record )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %ld\n", sv, record);
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return sv->table->ops->execute( sv->table, record );
+}
+
+static UINT SELECT_close( struct tagMSIVIEW *view )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p\n", sv );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return sv->table->ops->close( sv->table );
+}
+
+static UINT SELECT_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %p %p\n", sv, rows, cols );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( cols )
+        *cols = sv->num_cols;
+
+    return sv->table->ops->get_dimensions( sv->table, rows, NULL );
+}
+
+static UINT SELECT_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %d %p %p\n", sv, n, name, type );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( (n==0) || (n>sv->num_cols) )
+         return ERROR_FUNCTION_FAILED;
+
+    n = sv->cols[ n - 1 ];
+
+    return sv->table->ops->get_column_info( sv->table, n, name, type );
+}
+
+static UINT SELECT_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p %d %ld\n", sv, eModifyMode, hrec );
+
+    if( !sv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return sv->table->ops->modify( sv->table, eModifyMode, hrec );
+}
+
+static UINT SELECT_delete( struct tagMSIVIEW *view )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+
+    TRACE("%p\n", sv );
+
+    if( sv->table )
+        sv->table->ops->delete( sv->table );
+
+    HeapFree( GetProcessHeap(), 0, sv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS select_ops =
+{
+    SELECT_fetch_int,
+    SELECT_execute,
+    SELECT_close,
+    SELECT_get_dimensions,
+    SELECT_get_column_info,
+    SELECT_modify,
+    SELECT_delete
+};
+
+UINT SELECT_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
+{
+    MSISELECTVIEW *sv = NULL;
+    UINT count = 0, r;
+
+    TRACE("%p\n", sv );
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("can't get table dimensions\n");
+        return r;
+    }
+
+    sv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, 
+                    sizeof *sv + count*sizeof (UINT) );
+    if( !sv )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    sv->view.ops = &select_ops;
+    sv->db = db;
+    sv->table = table;
+    sv->num_cols = 0;
+    sv->max_cols = count;
+    *view = (MSIVIEW*) sv;
+
+    return ERROR_SUCCESS;
+}
+
+UINT SELECT_AddColumn( MSIVIEW *view, LPWSTR name )
+{
+    MSISELECTVIEW *sv = (MSISELECTVIEW*)view;
+    UINT r, n=0;
+    MSIVIEW *table;
+
+    TRACE("%p adding %s\n", sv, debugstr_w( name ) );
+
+    if( sv->view.ops != &select_ops )
+        return ERROR_FUNCTION_FAILED;
+
+    table = sv->table;
+    if( !table )
+        return ERROR_FUNCTION_FAILED;
+    if( !table->ops->get_dimensions )
+        return ERROR_FUNCTION_FAILED;
+    if( !table->ops->get_column_info )
+        return ERROR_FUNCTION_FAILED;
+
+    if( sv->num_cols >= sv->max_cols )
+        return ERROR_FUNCTION_FAILED;
+
+    r = VIEW_find_column( table, name, &n );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    sv->cols[sv->num_cols] = n;
+    TRACE("Translating column %s from %d -> %d\n", 
+          debugstr_w( name ), sv->num_cols, n);
+
+    sv->num_cols++;
+
+    return ERROR_SUCCESS;
+}
diff --git a/dlls/msi/sql.y b/dlls/msi/sql.y
new file mode 100644
index 00000000000..94409a3b0a7
--- /dev/null
+++ b/dlls/msi/sql.y
@@ -0,0 +1,513 @@
+%{
+
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+#include "config.h"
+#include "winbase.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include "query.h"
+#include "wine/debug.h"
+#include "wine/unicode.h"
+
+#define YYLEX_PARAM info
+#define YYPARSE_PARAM info
+
+extern int yyerror(char *str);
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tag_yyinput
+{
+    MSIDATABASE *db;
+    LPCWSTR command;
+    DWORD n, len;
+    MSIVIEW **view;  /* view structure for the resulting query */
+} yyinput;
+
+struct string_list
+{
+    LPWSTR string;
+    struct string_list *next;
+};
+
+static LPWSTR yygetstring( yyinput *info );
+static INT yygetint( yyinput *sql );
+static int yylex( void *yylval, yyinput *info);
+
+static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, 
+                        struct string_list *columns );
+static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, 
+                               struct string_list *columns );
+
+static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r );
+static struct expr * EXPR_column( LPWSTR column );
+static struct expr * EXPR_ival( INT ival );
+static struct expr * EXPR_sval( LPWSTR string );
+
+%}
+
+%pure-parser
+
+%union
+{
+    LPWSTR string;
+    struct string_list *column_list;
+    MSIVIEW *table;
+    struct expr *expr;
+}
+
+%token TK_ABORT TK_AFTER TK_AGG_FUNCTION TK_ALL TK_AND TK_AS TK_ASC
+%token TK_BEFORE TK_BEGIN TK_BETWEEN TK_BITAND TK_BITNOT TK_BITOR TK_BY
+%token TK_CASCADE TK_CASE TK_CHECK TK_CLUSTER TK_COLLATE TK_COLUMN TK_COMMA
+%token TK_COMMENT TK_COMMIT TK_CONCAT TK_CONFLICT 
+%token TK_CONSTRAINT TK_COPY TK_CREATE
+%token TK_DEFAULT TK_DEFERRABLE TK_DEFERRED TK_DELETE TK_DELIMITERS TK_DESC
+%token TK_DISTINCT TK_DOT TK_DROP TK_EACH
+%token TK_ELSE TK_END TK_END_OF_FILE TK_EQ TK_EXCEPT TK_EXPLAIN
+%token TK_FAIL TK_FLOAT TK_FOR TK_FOREIGN TK_FROM TK_FUNCTION
+%token TK_GE TK_GLOB TK_GROUP TK_GT
+%token TK_HAVING
+%token TK_IGNORE TK_ILLEGAL TK_IMMEDIATE TK_IN TK_INDEX TK_INITIALLY
+%token <string> TK_ID 
+%token TK_INSERT TK_INSTEAD TK_INTEGER TK_INTERSECT TK_INTO TK_IS TK_ISNULL
+%token TK_JOIN TK_JOIN_KW
+%token TK_KEY
+%token TK_LE TK_LIKE TK_LIMIT TK_LP TK_LSHIFT TK_LT
+%token TK_MATCH TK_MINUS
+%token TK_NE TK_NOT TK_NOTNULL TK_NULL
+%token TK_OF TK_OFFSET TK_ON TK_OR TK_ORACLE_OUTER_JOIN TK_ORDER
+%token TK_PLUS TK_PRAGMA TK_PRIMARY
+%token TK_RAISE TK_REFERENCES TK_REM TK_REPLACE TK_RESTRICT TK_ROLLBACK
+%token TK_ROW TK_RP TK_RSHIFT
+%token TK_SELECT TK_SEMI TK_SET TK_SLASH TK_SPACE TK_STAR TK_STATEMENT 
+%token <string> TK_STRING
+%token TK_TABLE TK_TEMP TK_THEN TK_TRANSACTION TK_TRIGGER
+%token TK_UMINUS TK_UNCLOSED_STRING TK_UNION TK_UNIQUE
+%token TK_UPDATE TK_UPLUS TK_USING
+%token TK_VACUUM TK_VALUES TK_VIEW
+%token TK_WHEN TK_WHERE
+
+// These are extra tokens used by the lexer but never seen by the
+// parser.  We put them in a rule so that the parser generator will
+// add them to the parse.h output file.
+//
+%nonassoc END_OF_FILE ILLEGAL SPACE UNCLOSED_STRING COMMENT FUNCTION
+          COLUMN AGG_FUNCTION.
+
+%type <query> oneselect
+%type <string> column table string_or_id
+%type <column_list> selcollist
+%type <table> from unorderedsel
+%type <expr> expr val column_val
+
+%%
+
+oneselect:
+    unorderedsel TK_ORDER TK_BY selcollist
+        {
+            yyinput* sql = (yyinput*) info;
+
+            if( !$1 )
+                YYABORT;
+            if( $4 )
+                *sql->view = do_order_by( sql->db, $1, $4 );
+            else
+                *sql->view = $1;
+        }
+  | unorderedsel
+        {
+            yyinput* sql = (yyinput*) info;
+
+            *sql->view = $1;
+        }
+    ;
+
+unorderedsel:
+    TK_SELECT selcollist from 
+        {
+            yyinput* sql = (yyinput*) info;
+            if( !$3 )
+                YYABORT;
+            if( $2 )
+                $$ = do_one_select( sql->db, $3, $2 );
+            else
+                $$ = $3;
+        }
+  | TK_SELECT TK_DISTINCT selcollist from 
+        {
+            yyinput* sql = (yyinput*) info;
+            MSIVIEW *view = $4;
+
+            if( !view )
+                YYABORT;
+            if( $3 )
+                view = do_one_select( sql->db, view, $3 );
+            DISTINCT_CreateView( sql->db, & $$, view );
+        }
+    ;
+
+selcollist:
+    column 
+        { 
+            struct string_list *list;
+
+            list = HeapAlloc( GetProcessHeap(), 0, sizeof *list );
+            if( !list )
+                YYABORT;
+            list->string = $1;
+            list->next = NULL;
+
+            $$ = list;
+            TRACE("Collist %s\n",debugstr_w($$->string));
+        }
+  | column TK_COMMA selcollist
+        { 
+            struct string_list *list;
+
+            list = HeapAlloc( GetProcessHeap(), 0, sizeof *list );
+            if( !list )
+                YYABORT;
+            list->string = $1;
+            list->next = $3;
+
+            $$ = list;
+            TRACE("From table: %s\n",debugstr_w($$->string));
+        }
+  | TK_STAR
+        {
+            $$ = NULL;
+        }
+    ;
+
+from:
+    TK_FROM table
+        { 
+            yyinput* sql = (yyinput*) info;
+
+            $$ = NULL;
+            TRACE("From table: %s\n",debugstr_w($2));
+            TABLE_CreateView( sql->db, $2, & $$ );
+        }
+  | TK_FROM table TK_WHERE expr
+        { 
+            yyinput* sql = (yyinput*) info;
+            MSIVIEW *view = NULL;
+            UINT r;
+
+            $$ = NULL;
+            TRACE("From table: %s\n",debugstr_w($2));
+            r = TABLE_CreateView( sql->db, $2, &view );
+            if( r != ERROR_SUCCESS )
+                YYABORT;
+            r = WHERE_CreateView( sql->db, &view, view );
+            if( r != ERROR_SUCCESS )
+                YYABORT;
+            r = WHERE_AddCondition( view, $4 );
+            if( r != ERROR_SUCCESS )
+                YYABORT;
+            $$ = view;
+        }
+    ;
+
+expr:
+    TK_LP expr TK_RP
+        {
+            $$ = $2;
+        }
+  | column_val TK_EQ column_val
+        {
+            $$ = EXPR_complex( $1, OP_EQ, $3 );
+        }
+  | expr TK_AND expr
+        {
+            $$ = EXPR_complex( $1, OP_AND, $3 );
+        }
+  | expr TK_OR expr
+        {
+            $$ = EXPR_complex( $1, OP_OR, $3 );
+        }
+  | column_val TK_EQ val
+        {
+            $$ = EXPR_complex( $1, OP_EQ, $3 );
+        }
+  | column_val TK_GT val
+        {
+            $$ = EXPR_complex( $1, OP_GT, $3 );
+        }
+  | column_val TK_LT val
+        {
+            $$ = EXPR_complex( $1, OP_LT, $3 );
+        }
+  | column_val TK_LE val
+        {
+            $$ = EXPR_complex( $1, OP_LE, $3 );
+        }
+  | column_val TK_GE val
+        {
+            $$ = EXPR_complex( $1, OP_GE, $3 );
+        }
+  | column_val TK_NE val
+        {
+            $$ = EXPR_complex( $1, OP_NE, $3 );
+        }
+  | column_val TK_IS TK_NULL
+        {
+            $$ = EXPR_complex( $1, OP_ISNULL, NULL );
+        }
+  | column_val TK_IS TK_NOT TK_NULL
+        {
+            $$ = EXPR_complex( $1, OP_NOTNULL, NULL );
+        }
+    ;
+
+val:
+    column_val
+        {
+            $$ = $1;
+        }
+  | TK_INTEGER
+        {
+            yyinput* sql = (yyinput*) info;
+            $$ = EXPR_ival( yygetint(sql) );
+        }
+  | TK_STRING
+        {
+            $$ = EXPR_sval( $1 );
+        }
+    ;
+
+column_val:
+    column 
+        {
+            $$ = EXPR_column( $1 );
+        }
+    ;
+
+column:
+    table TK_DOT string_or_id
+        {
+            $$ = $3;  /* FIXME */
+        }
+  | string_or_id
+        {
+            $$ = $1;
+        }
+    ;
+
+table:
+    string_or_id
+        {
+            $$ = $1;
+        }
+    ;
+
+string_or_id:
+    TK_ID
+        {
+            yyinput* sql = (yyinput*) info;
+            $$ = yygetstring(sql);
+        }
+  | TK_STRING
+        {
+            yyinput* sql = (yyinput*) info;
+            $$ = yygetstring(sql);
+        }
+    ;
+
+%%
+
+int yylex( void *yylval, yyinput *sql)
+{
+    int token;
+
+    do
+    {
+        sql->n += sql->len;
+        if( ! sql->command[sql->n] )
+            return 0;  /* end of input */
+
+        TRACE("string : %s\n", debugstr_w(&sql->command[sql->n]));
+        sql->len = sqliteGetToken( &sql->command[sql->n], &token );
+        if( sql->len==0 )
+            break;
+    }
+    while( token == TK_SPACE );
+
+    TRACE("token : %d (%s)\n", token, debugstr_wn(&sql->command[sql->n], sql->len));
+    
+    return token;
+}
+
+LPWSTR yygetstring( yyinput *sql )
+{
+    LPCWSTR p = &sql->command[sql->n];
+    LPWSTR str;
+    UINT len = sql->len;
+
+    /* if there's quotes, remove them */
+    if( (p[0]=='`') && (p[len-1]=='`') )
+    {
+        p++;
+        len -= 2;
+    }
+    str = HeapAlloc( GetProcessHeap(), 0, (len + 1)*sizeof(WCHAR));
+    if(!str )
+        return str;
+    memcpy(str, p, len*sizeof(WCHAR) );
+    str[len]=0;
+
+    return str;
+}
+
+INT yygetint( yyinput *sql )
+{
+    LPCWSTR p = &sql->command[sql->n];
+
+    return atoiW( p );
+}
+
+int yyerror(char *str)
+{
+    return 0;
+}
+
+static MSIVIEW *do_one_select( MSIDATABASE *db, MSIVIEW *in, 
+                               struct string_list *columns )
+{
+    MSIVIEW *view = NULL;
+
+    SELECT_CreateView( db, &view, in );
+    if( view )
+    {
+        struct string_list *x = columns;
+
+        while( x )
+        {
+            struct string_list *t = x->next;
+            SELECT_AddColumn( view, x->string );
+            HeapFree( GetProcessHeap(), 0, x->string );
+            HeapFree( GetProcessHeap(), 0, x );
+            x = t;
+        }
+    }
+    else
+        ERR("Error creating select query\n");
+    return view;
+}
+
+static MSIVIEW *do_order_by( MSIDATABASE *db, MSIVIEW *in, 
+                               struct string_list *columns )
+{
+    MSIVIEW *view = NULL;
+
+    ORDER_CreateView( db, &view, in );
+    if( view )
+    {
+        struct string_list *x = columns;
+
+        while( x )
+        {
+            struct string_list *t = x->next;
+            ORDER_AddColumn( view, x->string );
+            HeapFree( GetProcessHeap(), 0, x->string );
+            HeapFree( GetProcessHeap(), 0, x );
+            x = t;
+        }
+    }
+    else
+        ERR("Error creating select query\n");
+    return view;
+}
+
+static struct expr * EXPR_complex( struct expr *l, UINT op, struct expr *r )
+{
+    struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_COMPLEX;
+        e->u.expr.left = l;
+        e->u.expr.op = op;
+        e->u.expr.right = r;
+    }
+    return e;
+}
+
+static struct expr * EXPR_column( LPWSTR column )
+{
+    struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_COLUMN;
+        e->u.column = column;
+    }
+    return e;
+}
+
+static struct expr * EXPR_ival( INT ival )
+{
+    struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_IVAL;
+        e->u.ival = ival;
+    }
+    return e;
+}
+
+static struct expr * EXPR_sval( LPWSTR string )
+{
+    struct expr *e = HeapAlloc( GetProcessHeap(), 0, sizeof *e );
+    if( e )
+    {
+        e->type = EXPR_SVAL;
+        e->u.sval = string;
+    }
+    return e;
+}
+
+UINT MSI_ParseSQL( MSIDATABASE *db, LPCWSTR command, MSIVIEW **phview )
+{
+    yyinput sql;
+    int r;
+
+    *phview = NULL;
+
+    sql.db = db;
+    sql.command = command;
+    sql.n = 0;
+    sql.len = 0;
+    sql.view = phview;
+
+    r = yyparse(&sql);
+
+    TRACE("Parse returned %d\n", r);
+    if( r )
+    {
+        if( *sql.view )
+            (*sql.view)->ops->delete( *sql.view );
+        return ERROR_BAD_QUERY_SYNTAX;
+    }
+
+    return ERROR_SUCCESS;
+}
+
diff --git a/dlls/msi/suminfo.c b/dlls/msi/suminfo.c
new file mode 100644
index 00000000000..3e54818f6c3
--- /dev/null
+++ b/dlls/msi/suminfo.c
@@ -0,0 +1,223 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#define NONAMELESSUNION
+
+#include "windef.h"
+#include "winbase.h"
+#include "winreg.h"
+#include "shlwapi.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "msipriv.h"
+#include "objidl.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+const WCHAR szSumInfo[] = { 5 ,'S','u','m','m','a','r','y',
+                       'I','n','f','o','r','m','a','t','i','o','n',0 };
+
+static void MSI_CloseSummaryInfo( VOID *arg )
+{
+    MSISUMMARYINFO *suminfo = (MSISUMMARYINFO *) arg;
+    IPropertyStorage_Release( suminfo->propstg );
+}
+
+UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE hDatabase, 
+              LPCSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *phSummaryInfo)
+{
+    LPWSTR szwDatabase = NULL;
+    UINT ret;
+
+    TRACE("%ld %s %d %p\n", hDatabase, debugstr_a(szDatabase), 
+          uiUpdateCount, phSummaryInfo);
+
+    if( szDatabase )
+    {
+        szwDatabase = HEAP_strdupAtoW( GetProcessHeap(), 0, szDatabase );
+        if( !szwDatabase )
+            return ERROR_FUNCTION_FAILED;
+    }
+
+    ret = MsiGetSummaryInformationW(hDatabase, szwDatabase, uiUpdateCount, phSummaryInfo);
+
+    if( szwDatabase )
+        HeapFree( GetProcessHeap(), 0, szwDatabase );
+
+    return ret;
+}
+
+UINT WINAPI MsiGetSummaryInformationW(MSIHANDLE hDatabase, 
+              LPCWSTR szDatabase, UINT uiUpdateCount, MSIHANDLE *phSummaryInfo)
+{
+    HRESULT r;
+    MSIHANDLE handle, hdb = hDatabase;
+    MSISUMMARYINFO *suminfo;
+    MSIDATABASE *db;
+    UINT ret = ERROR_SUCCESS;
+    IPropertySetStorage *psstg = NULL;
+    IPropertyStorage *ps = NULL;
+    DWORD grfMode;
+
+    TRACE("%ld %s %d %p\n", hDatabase, debugstr_w(szDatabase),
+           uiUpdateCount, phSummaryInfo);
+
+    if( !phSummaryInfo )
+        return ERROR_INVALID_PARAMETER;
+
+    if( szDatabase )
+    {
+        UINT res;
+
+        res = MsiOpenDatabaseW(szDatabase, NULL, &hdb);
+        if( res != ERROR_SUCCESS )
+            return res;
+    }
+
+    db = msihandle2msiinfo(hdb, MSIHANDLETYPE_DATABASE);
+    if( !db )
+        return ERROR_INVALID_PARAMETER;
+
+    r = IStorage_QueryInterface( db->storage, 
+             &IID_IPropertySetStorage, (LPVOID)&psstg);
+    if( FAILED( r ) )
+    {
+        ERR("IStorage -> IPropertySetStorage failed\n");
+        return ERROR_FUNCTION_FAILED;
+    }
+    ERR("storage = %p propertysetstorage = %p\n", db->storage, psstg);
+
+    grfMode = STGM_READ | STGM_SHARE_EXCLUSIVE;
+    r = IPropertySetStorage_Open( psstg, &FMTID_SummaryInformation, grfMode, &ps );
+    if( FAILED( r ) )
+    {
+        ERR("failed to get IPropertyStorage r=%08lx\n",r);
+        ret = ERROR_FUNCTION_FAILED;
+        goto end;
+    }
+
+    handle = alloc_msihandle( MSIHANDLETYPE_SUMMARYINFO, 
+                  sizeof (MSISUMMARYINFO), MSI_CloseSummaryInfo );
+    if( !handle )
+    {
+        ret = ERROR_FUNCTION_FAILED;
+        goto end;
+    }
+
+    suminfo = msihandle2msiinfo( handle, MSIHANDLETYPE_SUMMARYINFO );
+    if( !suminfo )
+    {
+        ret = ERROR_FUNCTION_FAILED;
+        goto end;
+    }
+
+    IPropertyStorage_AddRef(ps);
+    suminfo->propstg = ps;
+    *phSummaryInfo = handle;
+
+end:
+    if( ps )
+        IPropertyStorage_Release(ps);
+    if( psstg )
+        IPropertySetStorage_Release(psstg);
+    if( !hDatabase )
+        MsiCloseHandle( hdb );
+
+    return ret;
+}
+
+UINT WINAPI MsiSummaryInfoGetPropertyCount(MSIHANDLE hSummaryInfo, UINT *pCount)
+{
+    MSISUMMARYINFO *suminfo;
+
+    FIXME("%ld %p\n",hSummaryInfo, pCount);
+
+    suminfo = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO );
+    if( !suminfo )
+        return ERROR_INVALID_HANDLE;
+
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+UINT WINAPI MsiSummaryInfoGetPropertyA(
+      MSIHANDLE hSummaryInfo, UINT uiProperty, UINT *puiDataType, INT *piValue,
+      FILETIME *pftValue, LPSTR szValueBuf, DWORD *pcchValueBuf)
+{
+    MSISUMMARYINFO *suminfo;
+    HRESULT r;
+    PROPSPEC spec;
+    PROPVARIANT var;
+
+    TRACE("%ld %d %p %p %p %p %p\n",
+        hSummaryInfo, uiProperty, puiDataType, piValue,
+        pftValue, szValueBuf, pcchValueBuf);
+
+    suminfo = msihandle2msiinfo( hSummaryInfo, MSIHANDLETYPE_SUMMARYINFO );
+    if( !suminfo )
+        return ERROR_INVALID_HANDLE;
+
+    spec.ulKind = PRSPEC_PROPID;
+    spec.DUMMYUNIONNAME.propid = uiProperty;
+
+    r = IPropertyStorage_ReadMultiple( suminfo->propstg, 1, &spec, &var);
+    if( FAILED(r) )
+        return ERROR_FUNCTION_FAILED;
+
+    if( puiDataType )
+        *puiDataType = var.vt;
+
+    switch( var.vt )
+    {
+    case VT_I4:
+        if( piValue )
+            *piValue = var.DUMMYUNIONNAME.lVal;
+        break;
+    case VT_LPSTR:
+        if( pcchValueBuf && szValueBuf )
+        {
+            lstrcpynA(szValueBuf, var.DUMMYUNIONNAME.pszVal, *pcchValueBuf );
+            *pcchValueBuf = lstrlenA( var.DUMMYUNIONNAME.pszVal );
+        }
+        break;
+    case VT_FILETIME:
+        if( pftValue )
+            memcpy(pftValue, &var.DUMMYUNIONNAME.filetime, sizeof (FILETIME) );
+        break;
+    case VT_EMPTY:
+        break;
+    default:
+        FIXME("Unknown property variant type\n");
+        break;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT WINAPI MsiSummaryInfoGetPropertyW(
+      MSIHANDLE hSummaryInfo, UINT uiProperty, UINT *puiDataType, INT *piValue,
+      FILETIME *pftValue, LPWSTR szValueBuf, DWORD *pcchValueBuf)
+{
+    FIXME("%ld %d %p %p %p %p %p\n",
+        hSummaryInfo, uiProperty, puiDataType, piValue,
+        pftValue, szValueBuf, pcchValueBuf);
+    
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
diff --git a/dlls/msi/table.c b/dlls/msi/table.c
new file mode 100644
index 00000000000..f0f7cbbbc46
--- /dev/null
+++ b/dlls/msi/table.c
@@ -0,0 +1,887 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+typedef struct tagMSICOLUMNINFO
+{
+    LPWSTR tablename;
+    UINT   number;
+    LPWSTR colname;
+    UINT   type;
+    UINT   offset;
+} MSICOLUMNINFO;
+
+struct tagMSITABLE
+{
+    USHORT *data;
+    UINT size;
+    UINT ref_count;
+    /* MSICOLUMNINFO *columns; */
+    /* UINT num_cols; */
+    struct tagMSITABLE *next;
+    struct tagMSITABLE *prev;
+    WCHAR name[1];
+} ;
+
+#define MAX_STREAM_NAME 0x1f
+
+static int utf2mime(int x)
+{
+    if( (x>='0') && (x<='9') )
+        return x-'0';
+    if( (x>='A') && (x<='Z') )
+        return x-'A'+10;
+    if( (x>='a') && (x<='z') )
+        return x-'a'+10+26;
+    if( x=='.' )
+        return 10+26+26;
+    if( x=='_' )
+        return 10+26+26+1;
+    return -1;
+}
+
+static BOOL encode_streamname(BOOL bTable, LPCWSTR in, LPWSTR out)
+{
+    DWORD count = MAX_STREAM_NAME;
+    DWORD ch, next;
+
+    if( bTable )
+    {
+         *out++ = 0x4840;
+         count --;
+    }
+    while( count -- ) 
+    {
+        ch = *in++;
+        if( !ch )
+        {
+            *out = ch;
+            return TRUE;
+        }
+        if( ( ch < 0x80 ) && ( utf2mime(ch) >= 0 ) )
+        {
+            ch = utf2mime(ch) + 0x4800;
+            next = *in;
+            if( next && (next<0x80) )
+            {
+                next = utf2mime(next);
+                if( next >= 0  )
+                {
+                     next += 0x3ffffc0;
+                     ch += (next<<6);
+                     in++;
+                }
+            }
+        }
+        *out++ = ch;
+    }
+    return FALSE;
+}
+
+#if 0
+static int mime2utf(int x)
+{
+    if( x<10 )
+        return x + '0';
+    if( x<(10+26))
+        return x - 10 + 'A';
+    if( x<(10+26+26))
+        return x - 10 - 26 + 'a';
+    if( x == (10+26+26) )
+        return '.';
+    return '_';
+}
+
+static BOOL decode_streamname(LPWSTR in, LPWSTR out)
+{
+    WCHAR ch;
+    DWORD count = 0;
+
+    while ( (ch = *in++) )
+    {
+        if( (ch >= 0x3800 ) && (ch < 0x4840 ) )
+        {
+            if( ch >= 0x4800 )
+                ch = mime2utf(ch-0x4800);
+            else
+            {
+                ch -= 0x3800;
+                *out++ = mime2utf(ch&0x3f);
+                count++;
+                ch = mime2utf((ch>>6)&0x3f);
+            }
+        }
+        *out++ = ch;
+        count++;
+    }
+    *out = 0;
+    return count;
+}
+#endif
+
+UINT read_table_from_storage(IStorage *stg, LPCWSTR name, MSITABLE **ptable)
+{
+    WCHAR buffer[0x20];
+    HRESULT r;
+    IStream *stm = NULL;
+    STATSTG stat;
+    UINT ret = ERROR_FUNCTION_FAILED;
+    VOID *data;
+    ULONG sz, count;
+    MSITABLE *t;
+
+    encode_streamname(TRUE, name, buffer);
+
+    TRACE("%s -> %s\n",debugstr_w(name),debugstr_w(buffer));
+
+    r = IStorage_OpenStream(stg, buffer, NULL, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm);
+    if( FAILED( r ) )
+    {
+        ERR("open stream failed r = %08lx!\n",r);
+        return r;
+    }
+
+    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);
+        goto end;
+    }
+        
+    r = IStream_Read(stm, data, sz, &count );
+    if( FAILED( r ) )
+    {
+        HeapFree( GetProcessHeap(), 0, data );
+        ERR("read stream failed r = %08lx!\n",r);
+        goto end;
+    }
+
+    t = HeapAlloc( GetProcessHeap(), 0, sizeof (MSITABLE) + lstrlenW(name)*sizeof (WCHAR) );
+    if( !t )
+    {
+        HeapFree( GetProcessHeap(), 0, data );
+        ERR("malloc failed!\n");
+        goto end;
+    }
+
+    if( count == sz )
+    {
+        ret = ERROR_SUCCESS;
+        t->size = sz;
+        t->data = data;
+        lstrcpyW( t->name, name );
+        t->ref_count = 1;
+        *ptable = t;
+    }
+    else
+    {
+        HeapFree( GetProcessHeap(), 0, data );
+        ERR("Count != sz\n");
+    }
+
+end:
+    if( stm )
+        IStream_Release( stm );
+
+    return ret;
+}
+
+/* add this table to the list of cached tables in the database */
+void add_table(MSIDATABASE *db, MSITABLE *table)
+{
+    table->next = db->first_table;
+    table->prev = NULL;
+    if( db->first_table )
+        db->first_table->prev = table;
+    else
+        db->last_table = table;
+    db->first_table = table;
+}
+ 
+/* remove from the list of cached tables */
+void remove_table( MSIDATABASE *db, MSITABLE *table )
+{
+    if( table->next )
+        table->next->prev = table->prev;
+    else
+        db->last_table = table->prev;
+    if( table->prev )
+        table->prev->next = table->next;
+    else
+        db->first_table = table->next;
+    table->next = NULL;
+    table->prev = NULL;
+}
+
+void release_table( MSIDATABASE *db, MSITABLE *table )
+{
+    if( !table->ref_count )
+        ERR("Trying to destroy table with refcount 0\n");
+    table->ref_count --;
+    if( !table->ref_count )
+    {
+        remove_table( db, table );
+        HeapFree( GetProcessHeap(), 0, table->data );
+        HeapFree( GetProcessHeap(), 0, table );
+        TRACE("Destroyed table %s\n", debugstr_w(table->name));
+    }
+}
+
+void free_cached_tables( MSIDATABASE *db )
+{
+    while( db->first_table )
+    {
+        MSITABLE *t = db->first_table;
+
+        if ( --t->ref_count )
+            ERR("table ref count not zero for %s\n", debugstr_w(t->name));
+        remove_table( db, t );
+        HeapFree( GetProcessHeap(), 0, t->data );
+        HeapFree( GetProcessHeap(), 0, t );
+    }
+}
+
+UINT find_cached_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
+{
+    MSITABLE *t;
+
+    for( t = db->first_table; t; t=t->next )
+    {
+        if( !lstrcmpW( name, t->name ) )
+        {
+            *ptable = t;
+            return ERROR_SUCCESS;
+        }
+    }
+
+    return ERROR_FUNCTION_FAILED;
+}
+
+UINT get_table(MSIDATABASE *db, LPCWSTR name, MSITABLE **ptable)
+{
+    UINT r;
+
+    *ptable = NULL;
+
+    /* first, see if the table is cached */
+    r = find_cached_table( db, name, ptable );
+    if( r == ERROR_SUCCESS )
+    {
+        (*ptable)->ref_count++;
+        return r;
+    }
+
+    r = read_table_from_storage( db->storage, name, ptable );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* add the table to the list */
+    add_table( db, *ptable );
+    (*ptable)->ref_count++;
+
+    return ERROR_SUCCESS;
+}
+
+UINT dump_string_table(MSIDATABASE *db)
+{
+    DWORD i, count, offset, len;
+    string_table *st = &db->strings;
+
+    MESSAGE("%d,%d bytes\n",st->pool.size,st->info.size);
+
+    count = st->pool.size/4;
+
+    offset = 0;
+    for(i=0; i<count; i++)
+    {
+        len = st->pool.data[i*2];
+        MESSAGE("[%2ld] = %s\n",i, debugstr_an(st->info.data+offset,len));
+        offset += len;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+UINT load_string_table( MSIDATABASE *db, string_table *pst)
+{
+    MSITABLE *pool = NULL, *info = NULL;
+    UINT r, ret = ERROR_FUNCTION_FAILED;
+    const WCHAR szStringData[] = { 
+        '_','S','t','r','i','n','g','D','a','t','a',0 };
+    const WCHAR szStringPool[] = { 
+        '_','S','t','r','i','n','g','P','o','o','l',0 };
+
+    r = get_table( db, szStringPool, &pool );
+    if( r != ERROR_SUCCESS)
+        goto end;
+    r = get_table( db, szStringData, &info );
+    if( r != ERROR_SUCCESS)
+        goto end;
+
+    pst->pool.size = pool->size;
+    pst->pool.data = pool->data;
+    pst->info.size = info->size;
+    pst->info.data = (CHAR *)info->data;
+
+    TRACE("Loaded %d,%d bytes\n",pst->pool.size,pst->info.size);
+
+    ret = ERROR_SUCCESS;
+
+end:
+    if( info )
+        release_table( db, info );
+    if( pool )
+        release_table( db, pool );
+
+    return ret;
+}
+
+UINT msi_id2string( string_table *st, UINT string_no, LPWSTR buffer, UINT *sz )
+{
+    DWORD i, count, offset, len;
+
+    count = st->pool.size/4;
+    TRACE("Finding string %d of %ld\n", string_no, count);
+    if(string_no >= count)
+        return ERROR_FUNCTION_FAILED;
+
+    offset = 0;
+    for(i=0; i<string_no; i++)
+    {
+        len = st->pool.data[i*2];
+        offset += len;
+    }
+
+    len = st->pool.data[i*2];
+
+    if( !buffer )
+    {
+        *sz = len;
+        return ERROR_SUCCESS;
+    }
+
+    if( (offset+len) > st->info.size )
+        return ERROR_FUNCTION_FAILED;
+
+    len = MultiByteToWideChar(CP_ACP,0,&st->info.data[offset],len,buffer,*sz-1); 
+    buffer[len] = 0;
+    *sz = len+1;
+
+    return ERROR_SUCCESS;
+}
+
+static UINT msi_string2id( string_table *st, LPCWSTR buffer, UINT *id )
+{
+    DWORD i, count, offset, len, sz;
+    UINT r = ERROR_INVALID_PARAMETER;
+    LPSTR str;
+
+    count = st->pool.size/4;
+    TRACE("Finding string %s in %ld strings\n", debugstr_w(buffer), count);
+
+    sz = WideCharToMultiByte( CP_ACP, 0, buffer, -1, NULL, 0, NULL, NULL );
+    str = HeapAlloc( GetProcessHeap(), 0, sz );
+    WideCharToMultiByte( CP_ACP, 0, buffer, -1, str, sz, NULL, NULL );
+
+    offset = 0;
+    for(i=0; i<count; i++)
+    {
+        len = st->pool.data[i*2];
+        if ( ( sz == len ) && !memcmp( str, st->info.data+offset, sz ) )
+        {
+            *id = i;
+            r = ERROR_SUCCESS;
+            break;
+        }
+        offset += len;
+    }
+
+    if( str )
+        HeapFree( GetProcessHeap(), 0, str );
+
+    return r;
+}
+
+static LPWSTR strdupW( LPCWSTR str )
+{
+    UINT len = lstrlenW( str );
+    LPWSTR ret = HeapAlloc( GetProcessHeap(), 0, len*sizeof (WCHAR) );
+    if( ret )
+        lstrcpyW( ret, str );
+    return ret;
+}
+
+static inline UINT bytes_per_column( MSICOLUMNINFO *col )
+{
+    if( col->type & MSITYPE_STRING )
+        return 2;
+    if( (col->type & 0xff) > 4 )
+        ERR("Invalid column size!\n");
+    return col->type & 0xff;
+}
+
+/* information for default tables */
+const WCHAR szTables[]  = { '_','T','a','b','l','e','s',0 };
+const WCHAR szTable[]  = { 'T','a','b','l','e',0 };
+const WCHAR szName[]    = { 'N','a','m','e',0 };
+const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+const WCHAR szColumn[]  = { 'C','o','l','u','m','n',0 };
+const WCHAR szNumber[]  = { 'N','u','m','b','e','r',0 };
+const WCHAR szType[]    = { 'T','y','p','e',0 };
+
+struct standard_table {
+    LPCWSTR tablename;
+    LPCWSTR columnname;
+    UINT number;
+    UINT type;
+} MSI_standard_tables[] =
+{
+  { szTables,  szName,   1, MSITYPE_VALID | MSITYPE_STRING | 32},
+  { szColumns, szTable,  1, MSITYPE_VALID | MSITYPE_STRING | 32},
+  { szColumns, szNumber, 2, MSITYPE_VALID | 2},
+  { szColumns, szName,   3, MSITYPE_VALID | MSITYPE_STRING | 32},
+  { szColumns, szType,   4, MSITYPE_VALID | 2},
+};
+
+#define STANDARD_TABLE_COUNT \
+     (sizeof(MSI_standard_tables)/sizeof(struct standard_table))
+
+UINT get_defaulttablecolumns( LPCWSTR szTable, MSICOLUMNINFO *colinfo, UINT *sz)
+{
+    DWORD i, n=0;
+
+    for(i=0; i<STANDARD_TABLE_COUNT; i++)
+    {
+        if( lstrcmpW( szTable, MSI_standard_tables[i].tablename ) )
+            continue;
+        if(colinfo && (n < *sz) )
+        {
+            colinfo[n].tablename = strdupW(MSI_standard_tables[i].tablename);
+            colinfo[n].colname = strdupW(MSI_standard_tables[i].columnname);
+            colinfo[n].number = MSI_standard_tables[i].number;
+            colinfo[n].type = MSI_standard_tables[i].type;
+            /* ERR("Table %s has column %s\n",debugstr_w(colinfo[n].tablename),
+                    debugstr_w(colinfo[n].colname)); */
+            if( n )
+                colinfo[n].offset = colinfo[n-1].offset
+                                  + bytes_per_column( &colinfo[n-1] );
+            else
+                colinfo[n].offset = 0;
+        }
+        n++;
+        if( colinfo && (n >= *sz) )
+            break;
+    }
+    *sz = n;
+    return ERROR_SUCCESS;
+}
+
+LPWSTR MSI_makestring( MSIDATABASE *db, UINT stringid)
+{
+    UINT sz=0, r;
+    LPWSTR str;
+
+    r = msi_id2string( &db->strings, stringid, NULL, &sz );
+    if( r != ERROR_SUCCESS )
+        return NULL;
+    sz ++; /* space for NUL char */
+    str = HeapAlloc( GetProcessHeap(), 0, sz*sizeof (WCHAR));
+    if( !str )
+        return str;
+    r = msi_id2string( &db->strings, stringid, str, &sz );
+    if( r == ERROR_SUCCESS )
+        return str;
+    HeapFree(  GetProcessHeap(), 0, str );
+    return NULL;
+}
+
+UINT get_tablecolumns( MSIDATABASE *db, 
+       LPCWSTR szTableName, MSICOLUMNINFO *colinfo, UINT *sz)
+{
+    UINT r, i, n=0, table_id, count, maxcount = *sz;
+    MSITABLE *table = NULL;
+    const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+
+    /* first check if there is a default table with that name */
+    r = get_defaulttablecolumns( szTableName, colinfo, sz );
+    if( ( r == ERROR_SUCCESS ) && *sz )
+        return r;
+
+    r = get_table( db, szColumns, &table);
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("table %s not available\n", debugstr_w(szColumns));
+        return r;
+    }
+
+    /* convert table and column names to IDs from the string table */
+    r = msi_string2id( &db->strings, szTableName, &table_id );
+    if( r != ERROR_SUCCESS )
+    {
+        release_table( db, table );
+        ERR("Couldn't find id for %s\n", debugstr_w(szTableName));
+        return r;
+    }
+
+    TRACE("Table id is %d\n", table_id);
+
+    count = table->size/8;
+    for( i=0; i<count; i++ )
+    {
+        if( table->data[ i ] != table_id )
+            continue;
+        if( colinfo )
+        {
+            UINT id = table->data[ i + count*2 ];
+            colinfo[n].tablename = MSI_makestring( db, table_id );
+            colinfo[n].number = table->data[ i + count ] - (1<<15);
+            colinfo[n].colname = MSI_makestring( db, id );
+            colinfo[n].type = table->data[ i + count*3 ];
+            /* this assumes that columns are in order in the table */
+            if( n )
+                colinfo[n].offset = colinfo[n-1].offset
+                                  + bytes_per_column( &colinfo[n-1] );
+            else
+                colinfo[n].offset = 0;
+            TRACE("table %s column %d is [%s] (%d) with type %08x "
+                  "offset %d at row %d\n", debugstr_w(szTableName),
+                   colinfo[n].number, debugstr_w(colinfo[n].colname),
+                   id, colinfo[n].type, colinfo[n].offset, i);
+            if( n != (colinfo[n].number-1) )
+            {
+                ERR("oops. data in the _Columns table isn't in the right "
+                    "order for table %s\n", debugstr_w(szTableName));
+                return ERROR_FUNCTION_FAILED;
+            }
+        }
+        n++;
+        if( colinfo && ( n >= maxcount ) )
+            break;
+    }
+    *sz = n;
+
+    release_table( db, table );
+
+    return ERROR_SUCCESS;
+}
+
+/* try to find the table name in the _Tables table */
+BOOL TABLE_Exists( MSIDATABASE *db, LPWSTR name )
+{
+    const WCHAR szTables[] = { '_','T','a','b','l','e','s',0 };
+    const WCHAR szColumns[] = { '_','C','o','l','u','m','n','s',0 };
+    UINT r, table_id = 0, i, count;
+    MSITABLE *table = NULL;
+
+    if( !lstrcmpW( name, szTables ) )
+        return TRUE;
+    if( !lstrcmpW( name, szColumns ) )
+        return TRUE;
+
+    r = msi_string2id( &db->strings, name, &table_id );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("Couldn't find id for %s\n", debugstr_w(name));
+        return FALSE;
+    }
+
+    r = get_table( db, szTables, &table);
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("table %s not available\n", debugstr_w(szTables));
+        return FALSE;
+    }
+
+    count = table->size/2;
+    for( i=0; i<count; i++ )
+        if( table->data[ i ] == table_id )
+            break;
+
+    release_table( db, table );
+
+    if (i!=count)
+        return TRUE;
+
+    return FALSE;
+}
+
+/* below is the query interface to a table */
+
+typedef struct tagMSITABLEVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSITABLE      *table;
+    MSICOLUMNINFO *columns;
+    UINT           num_cols;
+    UINT           row_size;
+    WCHAR          name[1];
+} MSITABLEVIEW;
+
+
+static UINT TABLE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    UINT offset, num_rows, n;
+
+    if( !tv->table )
+        return ERROR_INVALID_PARAMETER;
+
+    if( (col==0) || (col>tv->num_cols) )
+        return ERROR_INVALID_PARAMETER;
+
+    /* how many rows are there ? */
+    num_rows = tv->table->size / tv->row_size;
+    if( row >= num_rows )
+        return ERROR_NO_MORE_ITEMS;
+
+    if( tv->columns[col-1].offset >= tv->row_size )
+    {
+        ERR("Stuffed up %d >= %d\n", tv->columns[col-1].offset, tv->row_size );
+        ERR("%p %p\n", tv, tv->columns );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    offset = row + (tv->columns[col-1].offset/2) * num_rows;
+    n = bytes_per_column( &tv->columns[col-1] );
+    switch( n )
+    {
+    case 4:
+        offset = row*2 + (tv->columns[col-1].offset/2) * num_rows;
+        *val = tv->table->data[offset] + (tv->table->data[offset + 1] << 16);
+        break;
+    case 2:
+        offset = row + (tv->columns[col-1].offset/2) * num_rows;
+        *val = tv->table->data[offset];
+        break;
+    default:
+        ERR("oops! what is %d bytes per column?\n", n );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    TRACE("Data [%d][%d] = %d \n", row, col, *val );
+
+    return ERROR_SUCCESS;
+}
+
+static UINT TABLE_execute( struct tagMSIVIEW *view, MSIHANDLE record )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+    UINT r;
+
+    TRACE("%p %ld\n", tv, record);
+
+    if( tv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    r = get_table( tv->db, tv->name, &tv->table );
+    if( r != ERROR_SUCCESS )
+        return r;
+    
+    return ERROR_SUCCESS;
+}
+
+static UINT TABLE_close( struct tagMSIVIEW *view )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+    TRACE("%p\n", view );
+
+    if( !tv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    release_table( tv->db, tv->table );
+    tv->table = NULL;
+    
+    return ERROR_SUCCESS;
+}
+
+static UINT TABLE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols)
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+    TRACE("%p %p %p\n", view, rows, cols );
+
+    if( cols )
+        *cols = tv->num_cols;
+    if( rows )
+    {
+        if( !tv->table )
+            return ERROR_INVALID_PARAMETER;
+        *rows = tv->table->size / tv->row_size;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT TABLE_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+    TRACE("%p %d %p %p\n", tv, n, name, type );
+
+    if( ( n == 0 ) || ( n > tv->num_cols ) )
+        return ERROR_INVALID_PARAMETER;
+
+    if( name )
+    {
+        *name = strdupW( tv->columns[n-1].colname );
+        if( !*name )
+            return ERROR_FUNCTION_FAILED;
+    }
+    if( type )
+        *type = tv->columns[n-1].type;
+
+    return ERROR_SUCCESS;
+}
+
+static UINT TABLE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+    FIXME("%p %d %ld\n", view, eModifyMode, hrec );
+    return ERROR_CALL_NOT_IMPLEMENTED;
+}
+
+static UINT TABLE_delete( struct tagMSIVIEW *view )
+{
+    MSITABLEVIEW *tv = (MSITABLEVIEW*)view;
+
+    TRACE("%p\n", view );
+
+    if( tv->table )
+        release_table( tv->db, tv->table );
+    tv->table = NULL;
+
+    if( tv->columns )
+    {
+        UINT i;
+        for( i=0; i<tv->num_cols; i++)
+        {
+            HeapFree( GetProcessHeap(), 0, tv->columns[i].colname );
+            HeapFree( GetProcessHeap(), 0, tv->columns[i].tablename );
+        }
+        HeapFree( GetProcessHeap(), 0, tv->columns );
+    }
+    tv->columns = NULL;
+
+    HeapFree( GetProcessHeap(), 0, tv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS table_ops =
+{
+    TABLE_fetch_int,
+    TABLE_execute,
+    TABLE_close,
+    TABLE_get_dimensions,
+    TABLE_get_column_info,
+    TABLE_modify,
+    TABLE_delete
+};
+
+UINT TABLE_CreateView( MSIDATABASE *db, LPWSTR name, MSIVIEW **view )
+{
+    MSITABLEVIEW *tv ;
+    UINT r, sz, column_count;
+    MSICOLUMNINFO *columns, *last_col;
+
+    TRACE("%p %s %p\n", db, debugstr_w(name), view );
+
+    /* get the number of columns in this table */
+    column_count = 0;
+    r = get_tablecolumns( db, name, NULL, &column_count );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    /* if there's no columns, there's no table */
+    if( column_count == 0 )
+        return ERROR_INVALID_PARAMETER;
+
+    TRACE("Table found\n");
+
+    sz = sizeof *tv + lstrlenW(name)*sizeof name[0] ;
+    tv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sz );
+    if( !tv )
+        return ERROR_FUNCTION_FAILED;
+    
+    columns = HeapAlloc( GetProcessHeap(), 0, column_count*sizeof (MSICOLUMNINFO));
+    if( !columns )
+    {
+        HeapFree( GetProcessHeap(), 0, tv );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    r = get_tablecolumns( db, name, columns, &column_count );
+    if( r != ERROR_SUCCESS )
+    {
+        HeapFree( GetProcessHeap(), 0, columns );
+        HeapFree( GetProcessHeap(), 0, tv );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    TRACE("Table has %d columns\n", column_count);
+
+    last_col = &columns[column_count-1];
+
+    /* fill the structure */
+    tv->view.ops = &table_ops;
+    tv->db = db;
+    tv->columns = columns;
+    tv->num_cols = column_count;
+    tv->table = NULL;
+    tv->row_size = last_col->offset + bytes_per_column( last_col );
+
+    TRACE("one row is %d bytes\n", tv->row_size );
+
+    *view = (MSIVIEW*) tv;
+    lstrcpyW( tv->name, name );
+
+    return ERROR_SUCCESS;
+}
diff --git a/dlls/msi/tokenize.c b/dlls/msi/tokenize.c
new file mode 100644
index 00000000000..3816e510f35
--- /dev/null
+++ b/dlls/msi/tokenize.c
@@ -0,0 +1,379 @@
+/*
+** 2001 September 15
+**
+** The author disclaims copyright to this source code.  In place of
+** a legal notice, here is a blessing:
+**
+**    May you do good and not evil.
+**    May you find forgiveness for yourself and forgive others.
+**    May you share freely, never taking more than you give.
+**
+*************************************************************************
+** An tokenizer for SQL
+**
+** This file contains C code that splits an SQL input string up into
+** individual tokens and sends those tokens one-by-one over to the
+** parser for analysis.
+*/
+
+#include "winbase.h"
+#include <ctype.h>
+#include <stdlib.h>
+#include "wine/debug.h"
+#include "winnls.h"
+#include "query.h"
+#include "y.tab.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+** All the keywords of the SQL language are stored as in a hash
+** table composed of instances of the following structure.
+*/
+typedef struct Keyword Keyword;
+struct Keyword {
+  const char *zName;             /* The keyword name */
+  int tokenType;           /* The token value for this keyword */
+};
+
+/*
+** These are the keywords
+*/
+static const Keyword aKeywordTable[] = {
+  { "ABORT", TK_ABORT },
+  { "AFTER", TK_AFTER },
+  { "ALL", TK_ALL },
+  { "AND", TK_AND },
+  { "AS", TK_AS },
+  { "ASC", TK_ASC },
+  { "BEFORE", TK_BEFORE },
+  { "BEGIN", TK_BEGIN },
+  { "BETWEEN", TK_BETWEEN },
+  { "BY", TK_BY },
+  { "CASCADE", TK_CASCADE },
+  { "CASE", TK_CASE },
+  { "CHECK", TK_CHECK },
+  { "CLUSTER", TK_CLUSTER },
+  { "COLLATE", TK_COLLATE },
+  { "COMMIT", TK_COMMIT },
+  { "CONFLICT", TK_CONFLICT },
+  { "CONSTRAINT", TK_CONSTRAINT },
+  { "COPY", TK_COPY },
+  { "CREATE", TK_CREATE },
+  { "CROSS", TK_JOIN_KW },
+  { "DEFAULT", TK_DEFAULT },
+  { "DEFERRED", TK_DEFERRED },
+  { "DEFERRABLE", TK_DEFERRABLE },
+  { "DELETE", TK_DELETE },
+  { "DELIMITERS", TK_DELIMITERS },
+  { "DESC", TK_DESC },
+  { "DISTINCT", TK_DISTINCT },
+  { "DROP", TK_DROP },
+  { "END", TK_END },
+  { "EACH", TK_EACH },
+  { "ELSE", TK_ELSE },
+  { "EXCEPT", TK_EXCEPT },
+  { "EXPLAIN", TK_EXPLAIN },
+  { "FAIL", TK_FAIL },
+  { "FOR", TK_FOR },
+  { "FOREIGN", TK_FOREIGN },
+  { "FROM", TK_FROM },
+  { "FULL", TK_JOIN_KW },
+  { "GLOB", TK_GLOB },
+  { "GROUP", TK_GROUP },
+  { "HAVING", TK_HAVING },
+  { "IGNORE", TK_IGNORE },
+  { "IMMEDIATE", TK_IMMEDIATE },
+  { "IN", TK_IN },
+  { "INDEX", TK_INDEX },
+  { "INITIALLY", TK_INITIALLY },
+  { "INNER", TK_JOIN_KW },
+  { "INSERT", TK_INSERT },
+  { "INSTEAD", TK_INSTEAD },
+  { "INTERSECT", TK_INTERSECT },
+  { "INTO", TK_INTO },
+  { "IS", TK_IS },
+  { "ISNULL", TK_ISNULL },
+  { "JOIN", TK_JOIN },
+  { "KEY", TK_KEY },
+  { "LEFT", TK_JOIN_KW },
+  { "LIKE", TK_LIKE },
+  { "LIMIT", TK_LIMIT },
+  { "MATCH", TK_MATCH },
+  { "NATURAL", TK_JOIN_KW },
+  { "NOT", TK_NOT },
+  { "NOTNULL", TK_NOTNULL },
+  { "NULL", TK_NULL },
+  { "OF", TK_OF },
+  { "OFFSET", TK_OFFSET },
+  { "ON", TK_ON },
+  { "OR", TK_OR },
+  { "ORDER", TK_ORDER },
+  { "OUTER", TK_JOIN_KW },
+  { "PRAGMA", TK_PRAGMA },
+  { "PRIMARY", TK_PRIMARY },
+  { "RAISE", TK_RAISE },
+  { "REFERENCES", TK_REFERENCES },
+  { "REPLACE", TK_REPLACE },
+  { "RESTRICT", TK_RESTRICT },
+  { "RIGHT", TK_JOIN_KW },
+  { "ROLLBACK", TK_ROLLBACK },
+  { "ROW", TK_ROW },
+  { "SELECT", TK_SELECT },
+  { "SET", TK_SET },
+  { "STATEMENT", TK_STATEMENT },
+  { "TABLE", TK_TABLE },
+  { "TEMP", TK_TEMP },
+  { "TEMPORARY", TK_TEMP },
+  { "THEN", TK_THEN },
+  { "TRANSACTION", TK_TRANSACTION },
+  { "TRIGGER", TK_TRIGGER },
+  { "UNION", TK_UNION },
+  { "UNIQUE", TK_UNIQUE },
+  { "UPDATE", TK_UPDATE },
+  { "USING", TK_USING },
+  { "VACUUM", TK_VACUUM },
+  { "VALUES", TK_VALUES },
+  { "VIEW", TK_VIEW },
+  { "WHEN", TK_WHEN },
+  { "WHERE", TK_WHERE },
+};
+
+#define KEYWORD_COUNT ( sizeof aKeywordTable/sizeof (Keyword) )
+
+/*
+** This function looks up an identifier to determine if it is a
+** keyword.  If it is a keyword, the token code of that keyword is 
+** returned.  If the input is not a keyword, TK_ID is returned.
+*/
+int sqliteKeywordCode(const WCHAR *z, int n){
+  int i, len;
+  char buffer[0x10];
+
+  len = WideCharToMultiByte( CP_ACP, 0, z, n, buffer, sizeof buffer, NULL, NULL );
+  for(i=0; i<KEYWORD_COUNT; i++)
+  {
+      if(memcmp(buffer, aKeywordTable[i].zName, len))
+          continue;
+      if(strlen(aKeywordTable[i].zName) == len )
+          return aKeywordTable[i].tokenType;
+  }
+  return TK_ID;
+}
+
+
+/*
+** If X is a character that can be used in an identifier then
+** isIdChar[X] will be 1.  Otherwise isIdChar[X] will be 0.
+**
+** In this implementation, an identifier can be a string of
+** alphabetic characters, digits, and "_" plus any character
+** with the high-order bit set.  The latter rule means that
+** any sequence of UTF-8 characters or characters taken from
+** an extended ISO8859 character set can form an identifier.
+*/
+static const char isIdChar[] = {
+/* x0 x1 x2 x3 x4 x5 x6 x7 x8 x9 xA xB xC xD xE xF */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 0x */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 1x */
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,  /* 2x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0,  /* 3x */
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 4x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1,  /* 5x */
+    0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 6x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0,  /* 7x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 8x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* 9x */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Ax */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Bx */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Cx */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Dx */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Ex */
+    1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,  /* Fx */
+};
+
+
+/*
+** Return the length of the token that begins at z[0].  Return
+** -1 if the token is (or might be) incomplete.  Store the token
+** type in *tokenType before returning.
+*/
+int sqliteGetToken(const WCHAR *z, int *tokenType){
+  int i;
+  switch( *z ){
+    case ' ': case '\t': case '\n': case '\f': case '\r': {
+      for(i=1; isspace(z[i]); i++){}
+      *tokenType = TK_SPACE;
+      return i;
+    }
+    case '-': {
+      if( z[1]==0 ) return -1;
+      if( z[1]=='-' ){
+        for(i=2; z[i] && z[i]!='\n'; i++){}
+        *tokenType = TK_COMMENT;
+        return i;
+      }
+      *tokenType = TK_MINUS;
+      return 1;
+    }
+    case '(': {
+      if( z[1]=='+' && z[2]==')' ){
+        *tokenType = TK_ORACLE_OUTER_JOIN;
+        return 3;
+      }else{
+        *tokenType = TK_LP;
+        return 1;
+      }
+    }
+    case ')': {
+      *tokenType = TK_RP;
+      return 1;
+    }
+    case ';': {
+      *tokenType = TK_SEMI;
+      return 1;
+    }
+    case '+': {
+      *tokenType = TK_PLUS;
+      return 1;
+    }
+    case '*': {
+      *tokenType = TK_STAR;
+      return 1;
+    }
+    case '/': {
+      if( z[1]!='*' || z[2]==0 ){
+        *tokenType = TK_SLASH;
+        return 1;
+      }
+      for(i=3; z[i] && (z[i]!='/' || z[i-1]!='*'); i++){}
+      if( z[i] ) i++;
+      *tokenType = TK_COMMENT;
+      return i;
+    }
+    case '%': {
+      *tokenType = TK_REM;
+      return 1;
+    }
+    case '=': {
+      *tokenType = TK_EQ;
+      return 1 + (z[1]=='=');
+    }
+    case '<': {
+      if( z[1]=='=' ){
+        *tokenType = TK_LE;
+        return 2;
+      }else if( z[1]=='>' ){
+        *tokenType = TK_NE;
+        return 2;
+      }else if( z[1]=='<' ){
+        *tokenType = TK_LSHIFT;
+        return 2;
+      }else{
+        *tokenType = TK_LT;
+        return 1;
+      }
+    }
+    case '>': {
+      if( z[1]=='=' ){
+        *tokenType = TK_GE;
+        return 2;
+      }else if( z[1]=='>' ){
+        *tokenType = TK_RSHIFT;
+        return 2;
+      }else{
+        *tokenType = TK_GT;
+        return 1;
+      }
+    }
+    case '!': {
+      if( z[1]!='=' ){
+        *tokenType = TK_ILLEGAL;
+        return 2;
+      }else{
+        *tokenType = TK_NE;
+        return 2;
+      }
+    }
+    case '|': {
+      if( z[1]!='|' ){
+        *tokenType = TK_BITOR;
+        return 1;
+      }else{
+        *tokenType = TK_CONCAT;
+        return 2;
+      }
+    }
+    case ',': {
+      *tokenType = TK_COMMA;
+      return 1;
+    }
+    case '&': {
+      *tokenType = TK_BITAND;
+      return 1;
+    }
+    case '~': {
+      *tokenType = TK_BITNOT;
+      return 1;
+    }
+    case '`': case '\'': case '"': {
+      int delim = z[0];
+      for(i=1; z[i]; i++){
+        if( z[i]==delim ){
+          if( z[i+1]==delim ){
+            i++;
+          }else{
+            break;
+          }
+        }
+      }
+      if( z[i] ) i++;
+      *tokenType = TK_STRING;
+      return i;
+    }
+    case '.': {
+      if( !isdigit(z[1]) ){
+        *tokenType = TK_DOT;
+        return 1;
+      }
+      /* Fall thru into the next case */
+    }
+    case '0': case '1': case '2': case '3': case '4':
+    case '5': case '6': case '7': case '8': case '9': {
+      *tokenType = TK_INTEGER;
+      for(i=1; isdigit(z[i]); i++){}
+      if( z[i]=='.' ){
+        i++;
+        while( isdigit(z[i]) ){ i++; }
+        *tokenType = TK_FLOAT;
+      }
+      if( (z[i]=='e' || z[i]=='E') &&
+           ( isdigit(z[i+1]) 
+            || ((z[i+1]=='+' || z[i+1]=='-') && isdigit(z[i+2]))
+           )
+      ){
+        i += 2;
+        while( isdigit(z[i]) ){ i++; }
+        *tokenType = TK_FLOAT;
+      }else if( z[0]=='.' ){
+        *tokenType = TK_FLOAT;
+      }
+      return i;
+    }
+    case '[': {
+      for(i=1; z[i] && z[i-1]!=']'; i++){}
+      *tokenType = TK_ID;
+      return i;
+    }
+    default: {
+      if( !isIdChar[*z] ){
+        break;
+      }
+      for(i=1; isIdChar[z[i]]; i++){}
+      *tokenType = sqliteKeywordCode(z, i);
+      return i;
+    }
+  }
+  *tokenType = TK_ILLEGAL;
+  return 1;
+}
diff --git a/dlls/msi/where.c b/dlls/msi/where.c
new file mode 100644
index 00000000000..520293f09e9
--- /dev/null
+++ b/dlls/msi/where.c
@@ -0,0 +1,384 @@
+/*
+ * Implementation of the Microsoft Installer (msi.dll)
+ *
+ * Copyright 2002 Mike McCormack for Codeweavers
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include "windef.h"
+#include "winbase.h"
+#include "winerror.h"
+#include "wine/debug.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "msipriv.h"
+#include "winnls.h"
+
+#include "query.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+
+/* below is the query interface to a table */
+
+typedef struct tagMSIWHEREVIEW
+{
+    MSIVIEW        view;
+    MSIDATABASE   *db;
+    MSIVIEW       *table;
+    UINT           row_count;
+    UINT          *reorder;
+    struct expr   *cond;
+} MSIWHEREVIEW;
+
+static UINT WHERE_fetch_int( struct tagMSIVIEW *view, UINT row, UINT col, UINT *val )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %d %d %p\n", wv, row, col, val );
+
+    if( !wv->table )
+        return ERROR_FUNCTION_FAILED;
+
+    if( row > wv->row_count )
+        return ERROR_NO_MORE_ITEMS;
+
+    row = wv->reorder[ row ];
+
+    return wv->table->ops->fetch_int( wv->table, row, col, val );
+}
+
+static UINT INT_evaluate( UINT lval, UINT op, UINT rval )
+{
+    switch( op )
+    {
+    case OP_EQ:
+        return ( lval == rval );
+    case OP_AND:
+        return ( lval && rval );
+    case OP_OR:
+        return ( lval || rval );
+    case OP_GT:
+        return ( lval > rval );
+    case OP_LT:
+        return ( lval < rval );
+    case OP_LE:
+        return ( lval <= rval );
+    case OP_GE:
+        return ( lval >= rval );
+    case OP_NE:
+        return ( lval != rval );
+    case OP_ISNULL:
+        return ( !lval );
+    case OP_NOTNULL:
+        return ( lval );
+    default:
+        ERR("Unknown operator %d\n", op );
+    }
+    return 0;
+}
+
+static UINT WHERE_evaluate( MSIVIEW *table, UINT row, 
+                             struct expr *cond, UINT *val )
+{
+    UINT r, lval, rval;
+
+    if( !cond )
+        return ERROR_SUCCESS;
+
+    switch( cond->type )
+    {
+    case EXPR_COL_NUMBER:
+        return table->ops->fetch_int( table, row, cond->u.col_number, val );
+
+    /* case EXPR_IVAL:
+        *val = cond->u.ival;
+        return ERROR_SUCCESS; */
+
+    case EXPR_UVAL:
+        *val = cond->u.uval;
+        return ERROR_SUCCESS;
+
+    case EXPR_COMPLEX:
+        r = WHERE_evaluate( table, row, cond->u.expr.left, &lval );
+        if( r != ERROR_SUCCESS )
+            return r;
+        r = WHERE_evaluate( table, row, cond->u.expr.right, &rval );
+        if( r != ERROR_SUCCESS )
+            return r;
+        *val = INT_evaluate( lval, cond->u.expr.op, rval );
+        return ERROR_SUCCESS;
+
+    default:
+        ERR("Invalid expression type\n");
+        break;
+    } 
+
+    return ERROR_SUCCESS;
+
+}
+
+static UINT WHERE_execute( struct tagMSIVIEW *view, MSIHANDLE record )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+    UINT count = 0, r, val, i;
+    MSIVIEW *table = wv->table;
+
+    TRACE("%p %ld\n", wv, record);
+
+    if( !table )
+         return ERROR_FUNCTION_FAILED;
+
+    r = table->ops->execute( table, record );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    r = table->ops->get_dimensions( table, &count, NULL );
+    if( r != ERROR_SUCCESS )
+        return r;
+
+    wv->reorder = HeapAlloc( GetProcessHeap(), 0, count*sizeof(UINT) );
+    if( !wv->reorder )
+        return ERROR_FUNCTION_FAILED;
+
+    for( i=0; i<count; i++ )
+    {
+        val = 0;
+        r = WHERE_evaluate( table, i, wv->cond, &val );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( val )
+            wv->reorder[ wv->row_count ++ ] = i;
+    }
+
+    return ERROR_SUCCESS;
+}
+
+static UINT WHERE_close( struct tagMSIVIEW *view )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p\n", wv );
+
+    if( !wv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( wv->reorder )
+        HeapFree( GetProcessHeap(), 0, wv->reorder );
+    wv->reorder = NULL;
+
+    return wv->table->ops->close( wv->table );
+}
+
+static UINT WHERE_get_dimensions( struct tagMSIVIEW *view, UINT *rows, UINT *cols )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %p %p\n", wv, rows, cols );
+
+    if( !wv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    if( rows )
+    {
+        if( !wv->reorder )
+            return ERROR_FUNCTION_FAILED;
+        *rows = wv->row_count;
+    }
+
+    return wv->table->ops->get_dimensions( wv->table, NULL, cols );
+}
+
+static UINT WHERE_get_column_info( struct tagMSIVIEW *view,
+                UINT n, LPWSTR *name, UINT *type )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %d %p %p\n", wv, n, name, type );
+
+    if( !wv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return wv->table->ops->get_column_info( wv->table, n, name, type );
+}
+
+static UINT WHERE_modify( struct tagMSIVIEW *view, MSIMODIFY eModifyMode, MSIHANDLE hrec)
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p %d %ld\n", wv, eModifyMode, hrec );
+
+    if( !wv->table )
+         return ERROR_FUNCTION_FAILED;
+
+    return wv->table->ops->modify( wv->table, eModifyMode, hrec );
+}
+
+static void WHERE_delete_expr( struct expr *e )
+{
+    if( !e )
+        return;
+    if( e->type == EXPR_COMPLEX )
+    {
+        WHERE_delete_expr( e->u.expr.left );
+        WHERE_delete_expr( e->u.expr.right );
+    }
+    HeapFree( GetProcessHeap(), 0, e );
+}
+
+static UINT WHERE_delete( struct tagMSIVIEW *view )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW*)view;
+
+    TRACE("%p\n", wv );
+
+    if( wv->table )
+        wv->table->ops->delete( wv->table );
+
+    if( wv->reorder )
+        HeapFree( GetProcessHeap(), 0, wv->reorder );
+    wv->reorder = NULL;
+    wv->row_count = 0;
+
+    WHERE_delete_expr( wv->cond );
+
+    HeapFree( GetProcessHeap(), 0, wv );
+
+    return ERROR_SUCCESS;
+}
+
+
+MSIVIEWOPS where_ops =
+{
+    WHERE_fetch_int,
+    WHERE_execute,
+    WHERE_close,
+    WHERE_get_dimensions,
+    WHERE_get_column_info,
+    WHERE_modify,
+    WHERE_delete
+};
+
+UINT WHERE_CreateView( MSIDATABASE *db, MSIVIEW **view, MSIVIEW *table )
+{
+    MSIWHEREVIEW *wv = NULL;
+    UINT count = 0, r;
+
+    TRACE("%p\n", wv );
+
+    r = table->ops->get_dimensions( table, NULL, &count );
+    if( r != ERROR_SUCCESS )
+    {
+        ERR("can't get table dimensions\n");
+        return r;
+    }
+
+    wv = HeapAlloc( GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof *wv );
+    if( !wv )
+        return ERROR_FUNCTION_FAILED;
+    
+    /* fill the structure */
+    wv->view.ops = &where_ops;
+    wv->db = db;
+    wv->table = table;
+    wv->row_count = 0;
+    wv->reorder = NULL;
+    *view = (MSIVIEW*) wv;
+
+    return ERROR_SUCCESS;
+}
+
+static UINT WHERE_VerifyCondition( MSIVIEW *table, struct expr *cond,
+                                   UINT *valid )
+{
+    UINT r, col = 0;
+
+    switch( cond->type )
+    {
+    case EXPR_COLUMN:
+        r = VIEW_find_column( table, cond->u.column, &col );
+        if( r == ERROR_SUCCESS )
+        {
+            *valid = 1;
+            cond->type = EXPR_COL_NUMBER;
+            cond->u.col_number = col;
+        }
+        else
+        {
+            *valid = 0;
+            ERR("Couldn't find column %s\n", debugstr_w( cond->u.column ) );
+        }
+        break;
+    case EXPR_COMPLEX:
+        r = WHERE_VerifyCondition( table, cond->u.expr.left, valid );
+        if( r != ERROR_SUCCESS )
+            return r;
+        if( !*valid )
+            return ERROR_SUCCESS;
+        r = WHERE_VerifyCondition( table, cond->u.expr.right, valid );
+        if( r != ERROR_SUCCESS )
+            return r;
+        break;
+    case EXPR_IVAL:
+        *valid = 1;
+        cond->type = EXPR_UVAL;
+        cond->u.uval = cond->u.ival + (1<<15);
+        break;
+    case EXPR_SVAL:
+        *valid = 0;
+        FIXME("can't deal with string values yet\n");
+        break;
+    default:
+        ERR("Invalid expression type\n");
+        *valid = 0;
+        break;
+    } 
+
+    return ERROR_SUCCESS;
+}
+
+UINT WHERE_AddCondition( MSIVIEW *view, struct expr *cond )
+{
+    MSIWHEREVIEW *wv = (MSIWHEREVIEW *) view;
+    UINT r, valid = 0;
+
+    if( wv->view.ops != &where_ops )
+        return ERROR_FUNCTION_FAILED;
+    if( !wv->table )
+        return ERROR_INVALID_PARAMETER;
+    
+    if( !cond )
+        return ERROR_SUCCESS;
+
+    TRACE("Adding condition\n");
+
+    r = WHERE_VerifyCondition( wv->table, cond, &valid );
+    if( r != ERROR_SUCCESS )
+        ERR("condition evaluation failed\n");
+
+    TRACE("condition is %s\n", valid ? "valid" : "invalid" );
+    if( !valid )
+    {
+        WHERE_delete_expr( cond );
+        return ERROR_FUNCTION_FAILED;
+    }
+
+    wv->cond = cond;
+
+    return ERROR_SUCCESS;
+}
diff --git a/include/Makefile.in b/include/Makefile.in
index ade9121e89b..274ccf37181 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -107,6 +107,8 @@ WINDOWS_INCLUDES = \
 	mmsystem.h \
 	msacm.h \
 	msacmdlg.h \
+	msi.h \
+	msiquery.h \
 	mssip.h \
 	mswsock.h \
 	nb30.h \
diff --git a/include/msi.h b/include/msi.h
new file mode 100644
index 00000000000..b7500cbb90c
--- /dev/null
+++ b/include/msi.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2002 Mike McCormack
+ *
+ * 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
+ */
+
+#ifndef __WINE_MSI_H
+#define __WINE_MSI_H
+
+typedef unsigned long MSIHANDLE;
+typedef enum tagINSTALLSTATE
+{
+    INSTALLSTATE_BADCONFIG = -6,
+    INSTALLSTATE_INCOMPLETE = -5,
+    INSTALLSTATE_SOURCEABSENT = -4,
+    INSTALLSTATE_MOREDATA = -3,
+    INSTALLSTATE_INVALIDARG = -2,
+    INSTALLSTATE_UNKNOWN = -1,
+    INSTALLSTATE_BROKEN = 0,
+    INSTALLSTATE_ADVERTISED = 1,
+    INSTALLSTATE_ABSENT = 2,
+    INSTALLSTATE_LOCAL = 3,
+    INSTALLSTATE_SOURCE = 4,
+    INSTALLSTATE_DEFAULT = 5
+} INSTALLSTATE;
+
+typedef enum tagINSTALLUILEVEL
+{
+    INSTALLUILEVEL_NOCHANGE = 0,
+    INSTALLUILEVEL_DEFAULT = 1,
+    INSTALLUILEVEL_NONE = 2,
+    INSTALLUILEVEL_BASIC = 3,
+    INSTALLUILEVEL_REDUCED = 4,
+    INSTALLUILEVEL_FULL = 5
+} INSTALLUILEVEL;
+
+#define MSIDBOPEN_READONLY 0
+#define MSIDBOPEN_TRANSACT 1
+#define MSIDBOPEN_DIRECT 2
+#define MSIDBOPEN_CREATE 3
+
+
+UINT WINAPI MsiInstallProductA(LPCSTR, LPCSTR);
+UINT WINAPI MsiInstallProductW(LPCWSTR, LPCWSTR);
+
+UINT WINAPI MsiEnumProductsA(DWORD index, LPSTR lpguid);
+UINT WINAPI MsiEnumProductsW(DWORD index, LPWSTR lpguid);
+
+UINT WINAPI MsiEnumFeaturesA(LPCSTR, DWORD, LPSTR, LPSTR);
+UINT WINAPI MsiEnumFeaturesW(LPCWSTR, DWORD, LPWSTR, LPWSTR);
+
+UINT WINAPI MsiEnumComponentsA(DWORD, LPSTR);
+UINT WINAPI MsiEnumComponentsW(DWORD, LPWSTR);
+
+UINT WINAPI MsiEnumClientsA(LPCSTR, DWORD, LPSTR);
+UINT WINAPI MsiEnumClientsW(LPCWSTR, DWORD, LPWSTR);
+
+UINT WINAPI MsiOpenDatabaseA(LPCSTR, LPCSTR, MSIHANDLE *);
+UINT WINAPI MsiOpenDatabaseW(LPCWSTR, LPCWSTR, MSIHANDLE *);
+
+UINT WINAPI MsiGetSummaryInformationA(MSIHANDLE, LPCSTR, UINT, MSIHANDLE *);
+UINT WINAPI MsiGetSummaryInformationW(MSIHANDLE, LPCWSTR, UINT, MSIHANDLE *);
+
+UINT WINAPI MsiSummaryInfoGetPropertyA(MSIHANDLE,UINT,UINT*,INT*,FILETIME*,LPSTR,DWORD*);
+UINT WINAPI MsiSummaryInfoGetPropertyW(MSIHANDLE,UINT,UINT*,INT*,FILETIME*,LPWSTR,DWORD*);
+
+UINT WINAPI MsiCloseHandle(MSIHANDLE);
+UINT WINAPI MsiCloseAllHandles();
+
+UINT WINAPI MsiProvideComponentFromDescriptorA(LPCSTR,LPSTR,DWORD*,DWORD*);
+UINT WINAPI MsiProvideComponentFromDescriptorW(LPCWSTR,LPWSTR,DWORD*,DWORD*);
+
+#endif /* __WINE_MSI_H */
diff --git a/include/msiquery.h b/include/msiquery.h
new file mode 100644
index 00000000000..994f5b7f059
--- /dev/null
+++ b/include/msiquery.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2002 Mike McCormack
+ *
+ * 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
+ */
+
+#ifndef __WINE_MSIQUERY_H
+#define __WINE_MSIQUERY_H
+
+#include "msi.h"
+
+typedef enum tagMSICONDITION
+{
+    MSICONDITION_FALSE = 0,
+    MSICONDITION_TRUE  = 1,
+    MSICONDITION_NONE  = 2,
+    MSICONDITION_ERROR = 3,
+} MSICONDITION;
+
+#define MSI_NULL_INTEGER 0x80000000
+
+typedef enum tagMSICOLINFO
+{
+    MSICOLINFO_NAMES = 0,
+    MSICOLINFO_TYPES = 1
+} MSICOLINFO;
+
+typedef enum tagMSIMODIFY
+{
+    MSIMODIFY_REFRESH = 0,
+    MSIMODIFY_INSERT = 1,
+    MSIMODIFY_UPDATE = 2,
+    MSIMODIFY_ASSIGN = 3,
+    MSIMODIFY_REPLACE = 4,
+    MSIMODIFY_MERGE = 5,
+    MSIMODIFY_DELETE = 6,
+    MSIMODIFY_INSERT_TEMPORARY = 7,
+    MSIMODIFY_VALIDATE = 8,
+    MSIMODIFY_VALIDATE_NEW = 9,
+    MSIMODIFY_VALIDATE_FIELD = 10,
+    MSIMODIFY_VALIDATE_DELETE = 11
+} MSIMODIFY;
+
+#define MSI_NULL_INTEGER 0x80000000
+
+/* view manipulation */
+UINT WINAPI MsiViewFetch(MSIHANDLE,MSIHANDLE*);
+UINT WINAPI MsiViewExecute(MSIHANDLE,MSIHANDLE);
+UINT WINAPI MsiViewClose(MSIHANDLE);
+UINT WINAPI MsiDatabaseOpenViewA(MSIHANDLE,LPCSTR,MSIHANDLE*);
+UINT WINAPI MsiDatabaseOpenViewW(MSIHANDLE,LPCWSTR,MSIHANDLE*);
+
+/* record manipulation */
+MSIHANDLE WINAPI MsiCreateRecord(unsigned int);
+UINT WINAPI MsiRecordClearData(MSIHANDLE);
+UINT WINAPI MsiRecordSetInteger(MSIHANDLE,unsigned int,int);
+UINT WINAPI MsiRecordSetStringA(MSIHANDLE,unsigned int,LPCSTR);
+UINT WINAPI MsiRecordSetStringW(MSIHANDLE,unsigned int,LPCWSTR);
+UINT WINAPI MsiRecordGetStringA(MSIHANDLE,unsigned int,LPSTR,DWORD*);
+UINT WINAPI MsiRecordGetStringW(MSIHANDLE,unsigned int,LPWSTR,DWORD*);
+UINT WINAPI MsiRecordGetFieldCount(MSIHANDLE);
+int WINAPI MsiRecordGetInteger(MSIHANDLE,unsigned int);
+UINT WINAPI MsiRecordDataSize(MSIHANDLE,unsigned int);
+BOOL WINAPI MsiRecordIsNull(MSIHANDLE,unsigned int);
+UINT WINAPI MsiFormatRecordA(MSIHANDLE,MSIHANDLE,LPSTR,DWORD*);
+UINT WINAPI MsiFormatRecordW(MSIHANDLE,MSIHANDLE,LPWSTR,DWORD*);
+UINT WINAPI MsiRecordSetStreamA(MSIHANDLE,unsigned int,LPCSTR);
+UINT WINAPI MsiRecordSetStreamW(MSIHANDLE,unsigned int,LPCWSTR);
+UINT WINAPI MsiRecordReadStream(MSIHANDLE,unsigned int,char*,DWORD *);
+
+UINT WINAPI MsiDatabaseGetPrimaryKeysA(MSIHANDLE,LPCSTR,MSIHANDLE*);
+UINT WINAPI MsiDatabaseGetPrimaryKeysW(MSIHANDLE,LPCWSTR,MSIHANDLE*);
+
+/* installing */
+UINT WINAPI MsiDoActionA(MSIHANDLE,LPCSTR );
+UINT WINAPI MsiDoActionW(MSIHANDLE,LPCWSTR );
+
+/* database transforms */
+UINT WINAPI MsiDatabaseApplyTransformA(MSIHANDLE,LPCSTR,int);
+UINT WINAPI MsiDatabaseApplyTransformW(MSIHANDLE,LPCWSTR,int);
+UINT WINAPI MsiDatabaseGenerateTransformA(MSIHANDLE,MSIHANDLE,LPCSTR,int,int);
+UINT WINAPI MsiDatabaseGenerateTransformW(MSIHANDLE,MSIHANDLE,LPCWSTR,int,int);
+
+UINT WINAPI MsiDatabaseCommit(MSIHANDLE);
+
+#endif /* __WINE_MSIQUERY_H */
-- 
GitLab