Activate features and components based on their various conditions.
[wine] / dlls / msi / package.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2004 Aric Stewart for CodeWeavers
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #define NONAMELESSUNION
22
23 #include <stdarg.h>
24 #include <stdio.h>
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winreg.h"
28 #include "winnls.h"
29 #include "shlwapi.h"
30 #include "wine/debug.h"
31 #include "msi.h"
32 #include "msiquery.h"
33 #include "msipriv.h"
34 #include "objidl.h"
35 #include "wincrypt.h"
36 #include "winuser.h"
37 #include "shlobj.h"
38 #include "wine/unicode.h"
39 #include "objbase.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(msi);
42
43 /*
44  * The MSVC headers define the MSIDBOPEN_* macros cast to LPCTSTR,
45  *  which is a problem because LPCTSTR isn't defined when compiling wine.
46  * To work around this problem, we need to define LPCTSTR as LPCWSTR here,
47  *  and make sure to only use it in W functions.
48  */
49 #define LPCTSTR LPCWSTR
50
51 void MSI_FreePackage( VOID *arg);
52
53 void MSI_FreePackage( VOID *arg)
54 {
55     MSIPACKAGE *package= arg;
56
57     MsiCloseHandle(package->db);
58
59     ACTION_remove_tracked_tempfiles(package);
60
61     if (package->features && package->loaded_features > 0)
62         HeapFree(GetProcessHeap(),0,package->features);
63
64     if (package->folders && package->loaded_folders > 0)
65         HeapFree(GetProcessHeap(),0,package->folders);
66     
67     if (package->components && package->loaded_components > 0)
68         HeapFree(GetProcessHeap(),0,package->components);
69
70     if (package->files && package->loaded_files > 0)
71         HeapFree(GetProcessHeap(),0,package->files);
72 }
73
74 UINT WINAPI MsiOpenPackageA(LPCSTR szPackage, MSIHANDLE *phPackage)
75 {
76     LPWSTR szwPack = NULL;
77     UINT len, ret;
78
79     TRACE("%s %p\n",debugstr_a(szPackage), phPackage);
80
81     if( szPackage )
82     {
83         len = MultiByteToWideChar( CP_ACP, 0, szPackage, -1, NULL, 0 );
84         szwPack = HeapAlloc( GetProcessHeap(), 0, len * sizeof (WCHAR) );
85         if( szwPack )
86             MultiByteToWideChar( CP_ACP, 0, szPackage, -1, szwPack, len );
87     }
88
89     ret = MsiOpenPackageW( szwPack, phPackage );
90
91     if( szwPack )
92         HeapFree( GetProcessHeap(), 0, szwPack );
93
94     return ret;
95 }
96
97
98 static const void clone_properties(MSIHANDLE db)
99 {
100     MSIHANDLE view;
101     UINT rc;
102     static const CHAR CreateSql[] = "CREATE TABLE `_Property` ( `_Property` "
103 "CHAR(56) NOT NULL, `Value` CHAR(98) NOT NULL PRIMARY KEY `_Property`)";
104     static const CHAR Query[] = "SELECT * from Property";
105     static const CHAR Insert[] = 
106       "INSERT into `_Property` (`_Property`,`Value`) VALUES (?)";
107
108     /* create the temporary properties table */
109     MsiDatabaseOpenViewA(db, CreateSql, &view);
110     MsiViewExecute(view,0);   
111     MsiViewClose(view);
112     MsiCloseHandle(view); 
113
114     /* clone the existing properties */
115     MsiDatabaseOpenViewA(db, Query, &view);
116
117     MsiViewExecute(view, 0);
118     while (1)
119     {
120         MSIHANDLE row;
121         MSIHANDLE view2;
122
123         rc = MsiViewFetch(view,&row);
124         if (rc != ERROR_SUCCESS)
125             break;
126
127         MsiDatabaseOpenViewA(db,Insert,&view2);  
128         MsiViewExecute(view2,row);
129         MsiViewClose(view2);
130         MsiCloseHandle(view2);
131  
132         MsiCloseHandle(row); 
133     }
134     MsiViewClose(view);
135     MsiCloseHandle(view);
136     
137 }
138
139 /*
140  * There are a whole slew of these we need to set
141  *
142  *
143 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/msi/setup/properties.asp
144  */
145 static VOID set_installer_properties(MSIHANDLE hPackage)
146 {
147     WCHAR pth[MAX_PATH];
148     OSVERSIONINFOA OSVersion;
149     DWORD verval;
150     CHAR verstr[10];
151
152     static const WCHAR cszbs[]={'\\',0};
153     static const WCHAR CFF[] = 
154 {'C','o','m','m','o','n','F','i','l','e','s','F','o','l','d','e','r',0};
155     static const WCHAR PFF[] = 
156 {'P','r','o','g','r','a','m','F','i','l','e','s','F','o','l','d','e','r',0};
157     static const WCHAR CADF[] = 
158 {'C','o','m','m','o','n','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
159     static const WCHAR ATF[] = 
160 {'A','d','m','i','n','T','o','o','l','s','F','o','l','d','e','r',0};
161     static const WCHAR ADF[] = 
162 {'A','p','p','D','a','t','a','F','o','l','d','e','r',0};
163     static const WCHAR SF[] = 
164 {'S','y','s','t','e','m','F','o','l','d','e','r',0};
165     static const WCHAR LADF[] = 
166 {'L','o','c','a','l','A','p','p','D','a','t','a','F','o','l','d','e','r',0};
167     static const WCHAR MPF[] = 
168 {'M','y','P','i','c','t','u','r','e','s','F','o','l','d','e','r',0};
169     static const WCHAR PF[] = 
170 {'P','e','r','s','o','n','a','l','F','o','l','d','e','r',0};
171     static const WCHAR WF[] = 
172 {'W','i','n','d','o','w','s','F','o','l','d','e','r',0};
173     static const WCHAR TF[]=
174 {'T','e','m','p','F','o','l','d','e','r',0};
175
176 /* Not yet set ...  but needed by iTunes
177  *
178 DesktopFolder
179 FavoritesFolder
180 FontsFolder
181 PrimaryVolumePath
182 ProgramFiles64Folder
183 ProgramMenuFolder
184 SendToFolder
185 StartMenuFolder
186 StartupFolder
187 System16Folder
188 System64Folder
189 TemplateFolder
190  */
191
192 /* asked for by iTunes ... but are they installer set? 
193  *
194  *  GlobalAssemblyCache
195  */
196
197 /*
198  * Other things i notice set
199  *
200 ScreenY
201 ScreenX
202 SystemLanguageID
203 ComputerName
204 UserLanguageID
205 LogonUser
206 VirtualMemory
207 PhysicalMemory
208 Intel
209 ShellAdvSupport
210 DefaultUIFont
211 VersionMsi
212 VersionDatabase
213 PackagecodeChanging
214 ProductState
215 CaptionHeight
216 BorderTop
217 BorderSide
218 TextHeight
219 ColorBits
220 RedirectedDllSupport
221 Time
222 Date
223 Privilaged
224 */
225
226     SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES_COMMON,NULL,0,pth);
227     strcatW(pth,cszbs);
228     MsiSetPropertyW(hPackage, CFF, pth);
229
230     SHGetFolderPathW(NULL,CSIDL_PROGRAM_FILES,NULL,0,pth);
231     strcatW(pth,cszbs);
232     MsiSetPropertyW(hPackage, PFF, pth);
233
234     SHGetFolderPathW(NULL,CSIDL_COMMON_APPDATA,NULL,0,pth);
235     strcatW(pth,cszbs);
236     MsiSetPropertyW(hPackage, CADF, pth);
237
238     SHGetFolderPathW(NULL,CSIDL_ADMINTOOLS,NULL,0,pth);
239     strcatW(pth,cszbs);
240     MsiSetPropertyW(hPackage, ATF, pth);
241
242     SHGetFolderPathW(NULL,CSIDL_APPDATA,NULL,0,pth);
243     strcatW(pth,cszbs);
244     MsiSetPropertyW(hPackage, ADF, pth);
245
246     SHGetFolderPathW(NULL,CSIDL_SYSTEM,NULL,0,pth);
247     strcatW(pth,cszbs);
248     MsiSetPropertyW(hPackage, SF, pth);
249
250     SHGetFolderPathW(NULL,CSIDL_LOCAL_APPDATA,NULL,0,pth);
251     strcatW(pth,cszbs);
252     MsiSetPropertyW(hPackage, LADF, pth);
253
254     SHGetFolderPathW(NULL,CSIDL_MYPICTURES,NULL,0,pth);
255     strcatW(pth,cszbs);
256     MsiSetPropertyW(hPackage, MPF, pth);
257
258     SHGetFolderPathW(NULL,CSIDL_PERSONAL,NULL,0,pth);
259     strcatW(pth,cszbs);
260     MsiSetPropertyW(hPackage, PF, pth);
261
262     SHGetFolderPathW(NULL,CSIDL_WINDOWS,NULL,0,pth);
263     strcatW(pth,cszbs);
264     MsiSetPropertyW(hPackage, WF, pth);
265
266     GetTempPathW(MAX_PATH,pth);
267     MsiSetPropertyW(hPackage, TF, pth);
268
269     /* in a wine enviroment the user is always admin and privlaged */
270     MsiSetPropertyA(hPackage,"AdminUser","1");
271     MsiSetPropertyA(hPackage,"Privileged","1");
272
273     /* set the os things */
274     OSVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
275     GetVersionExA(&OSVersion);
276     verval = OSVersion.dwMinorVersion+OSVersion.dwMajorVersion*100;
277     sprintf(verstr,"%li",verval);
278     switch (OSVersion.dwPlatformId)
279     {
280         case VER_PLATFORM_WIN32_WINDOWS:    
281             MsiSetPropertyA(hPackage,"Version9X",verstr);
282             break;
283         case VER_PLATFORM_WIN32_NT:
284             MsiSetPropertyA(hPackage,"VersionNT",verstr);
285             break;
286     }
287     sprintf(verstr,"%li",OSVersion.dwBuildNumber);
288     MsiSetPropertyA(hPackage,"WindowsBuild",verstr);
289     /* just fudge this */
290     MsiSetPropertyA(hPackage,"ServicePackLevel","6");
291 }
292
293
294 UINT WINAPI MsiOpenPackageW(LPCWSTR szPackage, MSIHANDLE *phPackage)
295 {
296     UINT rc;
297     MSIHANDLE handle;
298     MSIHANDLE db;
299     MSIPACKAGE *package;
300     CHAR uilevel[10];
301
302     static const WCHAR OriginalDatabase[] =
303 {'O','r','i','g','i','n','a','l','D','a','t','a','b','a','s','e',0};
304     static const WCHAR Database[] =
305 {'D','A','T','A','B','A','S','E',0};
306
307     TRACE("%s %p\n",debugstr_w(szPackage), phPackage);
308
309     rc = MsiOpenDatabaseW(szPackage, MSIDBOPEN_READONLY, &db);
310
311     if (rc != ERROR_SUCCESS)
312         return ERROR_FUNCTION_FAILED;
313
314     handle = alloc_msihandle(MSIHANDLETYPE_PACKAGE, sizeof (MSIPACKAGE),
315                              MSI_FreePackage, (void**)&package);
316
317     if (!handle)
318     {
319         MsiCloseHandle(db);
320         return ERROR_FUNCTION_FAILED;
321     }
322
323     package->db = db;
324     package->features = NULL;
325     package->folders = NULL;
326     package->components = NULL;
327     package->files = NULL;
328     package->loaded_features = 0;
329     package->loaded_folders = 0;
330     package->loaded_components= 0;
331     package->loaded_files = 0;
332
333     /* ok here is where we do a slew of things to the database to 
334      * prep for all that is to come as a package */
335
336     clone_properties(db);
337     set_installer_properties(handle);
338     MsiSetPropertyW(handle, OriginalDatabase, szPackage);
339     MsiSetPropertyW(handle, Database, szPackage);
340     sprintf(uilevel,"%i",gUILevel);
341     MsiSetPropertyA(handle, "UILevel", uilevel);
342
343     *phPackage = handle;
344
345     return ERROR_SUCCESS;
346 }
347
348 UINT WINAPI MsiOpenPackageExA(LPCSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
349 {
350     FIXME("%s 0x%08lx %p\n",debugstr_a(szPackage), dwOptions, phPackage);
351     return ERROR_CALL_NOT_IMPLEMENTED;
352 }
353
354 UINT WINAPI MsiOpenPackageExW(LPCWSTR szPackage, DWORD dwOptions, MSIHANDLE *phPackage)
355 {
356     FIXME("%s 0x%08lx %p\n",debugstr_w(szPackage), dwOptions, phPackage);
357     return ERROR_CALL_NOT_IMPLEMENTED;
358 }
359
360 MSIHANDLE WINAPI MsiGetActiveDatabase(MSIHANDLE hInstall)
361 {
362     MSIPACKAGE *package;
363
364     TRACE("(%ld)\n",hInstall);
365
366     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
367
368     if( !package)
369         return ERROR_INVALID_HANDLE;
370
371     msihandle_addref(package->db);
372     return package->db;
373 }
374
375 INT WINAPI MsiProcessMessage( MSIHANDLE hInstall, INSTALLMESSAGE eMessageType,
376                               MSIHANDLE hRecord)
377 {
378     DWORD log_type = 0;
379     LPSTR message;
380     DWORD sz;
381     DWORD total_size = 0;
382     INT msg_field=1;
383     INT i;
384     FIXME("STUB: %x \n",eMessageType);
385
386     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ERROR)
387         log_type |= INSTALLLOGMODE_ERROR;
388     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_WARNING)
389         log_type |= INSTALLLOGMODE_WARNING;
390     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_USER)
391         log_type |= INSTALLLOGMODE_USER;
392     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_INFO)
393         log_type |= INSTALLLOGMODE_INFO;
394     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_COMMONDATA)
395         log_type |= INSTALLLOGMODE_COMMONDATA;
396     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONSTART)
397         log_type |= INSTALLLOGMODE_ACTIONSTART;
398     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_ACTIONDATA)
399         log_type |= INSTALLLOGMODE_ACTIONDATA;
400     /* just a guess */
401     if ((eMessageType & 0xff000000) == INSTALLMESSAGE_PROGRESS)
402         log_type |= 0x800;
403
404     message = HeapAlloc(GetProcessHeap(),0,1);
405     message[0]=0;
406     msg_field = MsiRecordGetFieldCount(hRecord);
407     for (i = 1; i <= msg_field; i++)
408     {
409         LPSTR tmp;
410         CHAR number[3];
411         sz = 0;
412         MsiRecordGetStringA(hRecord,i,NULL,&sz);
413         sz+=4;
414         total_size+=sz;
415         tmp = HeapAlloc(GetProcessHeap(),0,sz);
416         message = HeapReAlloc(GetProcessHeap(),0,message,total_size);
417
418         MsiRecordGetStringA(hRecord,i,tmp,&sz);
419
420         if (msg_field > 1)
421         {
422             sprintf(number," %i: ",i);
423             strcat(message,number);
424         }
425         strcat(message,tmp);
426         HeapFree(GetProcessHeap(),0,tmp);
427     }
428
429     TRACE("(%p %lx %lx %s)\n",gUIHandler, gUIFilter, log_type,
430                              debugstr_a(message));
431
432     if (gUIHandler && (gUIFilter & log_type))
433         gUIHandler(gUIContext,eMessageType,message);
434
435     HeapFree(GetProcessHeap(),0,message);
436     return ERROR_SUCCESS;
437 }
438
439 /* property code */
440 UINT WINAPI MsiSetPropertyA( MSIHANDLE hInstall, LPCSTR szName, LPCSTR szValue)
441 {
442     LPWSTR szwName = NULL, szwValue = NULL;
443     UINT hr = ERROR_INSTALL_FAILURE;
444     UINT len;
445
446     if (0 == hInstall) {
447       return ERROR_INVALID_HANDLE;
448     }
449     if (NULL == szName) {
450       return ERROR_INVALID_PARAMETER;
451     }
452     if (NULL == szValue) {
453       return ERROR_INVALID_PARAMETER;
454     }
455
456     len = MultiByteToWideChar( CP_ACP, 0, szName, -1, NULL, 0 );
457     szwName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
458     if( !szwName )
459         goto end;
460     MultiByteToWideChar( CP_ACP, 0, szName, -1, szwName, len );
461
462     len = MultiByteToWideChar( CP_ACP, 0, szValue, -1, NULL, 0 );
463     szwValue = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
464     if( !szwValue)
465         goto end;
466     MultiByteToWideChar( CP_ACP, 0, szValue , -1, szwValue, len );
467
468     hr = MsiSetPropertyW( hInstall, szwName, szwValue);
469
470 end:
471     if( szwName )
472         HeapFree( GetProcessHeap(), 0, szwName );
473     if( szwValue )
474         HeapFree( GetProcessHeap(), 0, szwValue );
475
476     return hr;
477 }
478
479 UINT WINAPI MsiSetPropertyW( MSIHANDLE hInstall, LPCWSTR szName, LPCWSTR szValue)
480 {
481     MSIPACKAGE *package;
482     MSIHANDLE view,row;
483     UINT rc;
484     DWORD sz = 0;
485     static const WCHAR Insert[]=
486      {'I','N','S','E','R','T',' ','i','n','t','o',' ','`','_','P','r','o','p'
487 ,'e','r','t','y','`',' ','(','`','_','P','r','o','p','e','r','t','y','`'
488 ,',','`','V','a','l','u','e','`',')',' ','V','A','L','U','E','S'
489 ,' ','(','?',')',0};
490     static const WCHAR Update[]=
491      {'U','P','D','A','T','E',' ','_','P','r','o','p','e'
492 ,'r','t','y',' ','s','e','t',' ','`','V','a','l','u','e','`',' ','='
493 ,' ','?',' ','w','h','e','r','e',' ','`','_','P','r','o','p'
494 ,'e','r','t','y','`',' ','=',' ','\'','%','s','\'',0};
495     WCHAR Query[1024];
496
497     TRACE("Setting property (%s %s)\n",debugstr_w(szName),
498           debugstr_w(szValue));
499
500     if (!hInstall)
501         return ERROR_INVALID_HANDLE;
502
503     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
504     if( !package)
505         return ERROR_INVALID_HANDLE;
506
507     rc = MsiGetPropertyW(hInstall,szName,0,&sz);
508     if (rc==ERROR_MORE_DATA || rc == ERROR_SUCCESS)
509     {
510         sprintfW(Query,Update,szName);
511
512         row = MsiCreateRecord(1);
513         MsiRecordSetStringW(row,1,szValue);
514
515     }
516     else
517     {
518        strcpyW(Query,Insert);
519
520         row = MsiCreateRecord(2);
521         MsiRecordSetStringW(row,1,szName);
522         MsiRecordSetStringW(row,2,szValue);
523     }
524
525
526     rc = MsiDatabaseOpenViewW(package->db,Query,&view);
527     if (rc!= ERROR_SUCCESS)
528     {
529         MsiCloseHandle(row);
530         return rc;
531     }
532
533     rc = MsiViewExecute(view,row);
534
535     MsiCloseHandle(row);
536     MsiViewClose(view);
537     MsiCloseHandle(view);
538
539     return rc;
540 }
541
542 UINT WINAPI MsiGetPropertyA(MSIHANDLE hInstall, LPCSTR szName, LPSTR szValueBuf, DWORD* pchValueBuf) 
543 {
544     LPWSTR szwName = NULL, szwValueBuf = NULL;
545     UINT hr = ERROR_INSTALL_FAILURE;
546
547     if (0 == hInstall) {
548       return ERROR_INVALID_HANDLE;
549     }
550     if (NULL == szName) {
551       return ERROR_INVALID_PARAMETER;
552     }
553
554     TRACE("%lu %s %lu\n", hInstall, debugstr_a(szName), *pchValueBuf);
555
556     if (NULL != szValueBuf && NULL == pchValueBuf) {
557       return ERROR_INVALID_PARAMETER;
558     }
559     if( szName )
560     {
561         UINT len = MultiByteToWideChar( CP_ACP, 0, szName, -1, NULL, 0 );
562         szwName = HeapAlloc( GetProcessHeap(), 0, len * sizeof(WCHAR) );
563         if( !szwName )
564             goto end;
565         MultiByteToWideChar( CP_ACP, 0, szName, -1, szwName, len );
566     } else {
567       return ERROR_INVALID_PARAMETER;
568     }
569     if( szValueBuf )
570     {
571         szwValueBuf = HeapAlloc( GetProcessHeap(), 0, (*pchValueBuf) * sizeof(WCHAR) );
572         if( !szwValueBuf )       
573             goto end;
574     }
575
576     hr = MsiGetPropertyW( hInstall, szwName, szwValueBuf, pchValueBuf );
577
578     if(  *pchValueBuf > 0 )
579     {
580         WideCharToMultiByte(CP_ACP, 0, szwValueBuf, -1, szValueBuf, *pchValueBuf, NULL, NULL);
581     }
582
583 end:
584     if( szwName )
585         HeapFree( GetProcessHeap(), 0, szwName );
586     if( szwValueBuf )
587         HeapFree( GetProcessHeap(), 0, szwValueBuf );
588
589     return hr;
590 }
591
592 UINT WINAPI MsiGetPropertyW(MSIHANDLE hInstall, LPCWSTR szName, 
593                            LPWSTR szValueBuf, DWORD* pchValueBuf)
594 {
595     MSIHANDLE view,row;
596     UINT rc;
597     WCHAR Query[1024]=
598     {'s','e','l','e','c','t',' ','V','a','l','u','e',' ','f','r','o','m',' '
599      ,'_','P','r','o','p','e','r','t','y',' ','w','h','e','r','e',' '
600      ,'_','P','r','o','p','e','r','t','y','=','`',0};
601
602     static const WCHAR szEnd[]={'`',0};
603     MSIPACKAGE *package;
604
605     if (0 == hInstall) {
606       return ERROR_INVALID_HANDLE;
607     }
608     if (NULL == szName) {
609       return ERROR_INVALID_PARAMETER;
610     }
611
612     package = msihandle2msiinfo( hInstall, MSIHANDLETYPE_PACKAGE);
613     if( !package)
614         return ERROR_INVALID_HANDLE;
615
616     strcatW(Query,szName);
617     strcatW(Query,szEnd);
618     
619     rc = MsiDatabaseOpenViewW(package->db, Query, &view);
620     if (rc == ERROR_SUCCESS)
621     {
622         DWORD sz;
623         WCHAR value[0x100];
624
625         rc = MsiViewExecute(view, 0);
626         if (rc != ERROR_SUCCESS)
627         {
628             MsiViewClose(view);
629             MsiCloseHandle(view);
630             return rc;
631         }
632
633         rc = MsiViewFetch(view,&row);
634         if (rc == ERROR_SUCCESS)
635         {
636             sz=0x100;
637             rc = MsiRecordGetStringW(row,1,value,&sz);
638             strncpyW(szValueBuf,value,min(sz+1,*pchValueBuf));
639             *pchValueBuf = sz+1;
640             MsiCloseHandle(row);
641         }
642         MsiViewClose(view);
643         MsiCloseHandle(view);
644     }
645
646     if (rc == ERROR_SUCCESS)
647         TRACE("returning %s for property %s\n", debugstr_w(szValueBuf),
648             debugstr_w(szName));
649     else
650     {
651         *pchValueBuf = 0;
652         TRACE("property not found\n");
653     }
654
655     return rc;
656 }