Remove redundant check.
[wine] / dlls / msi / install.c
1 /*
2  * Implementation of the Microsoft Installer (msi.dll)
3  *
4  * Copyright 2005 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 /* Msi top level apis directly related to installs */
22
23 #include <stdarg.h>
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "winerror.h"
28 #include "wine/debug.h"
29 #include "msi.h"
30 #include "msidefs.h"
31 #include "msipriv.h"
32 #include "winuser.h"
33 #include "wine/unicode.h"
34 #include "action.h"
35
36 WINE_DEFAULT_DEBUG_CHANNEL(msi);
37
38 /***********************************************************************
39  * MsiDoActionA       (MSI.@)
40  */
41 UINT WINAPI MsiDoActionA( MSIHANDLE hInstall, LPCSTR szAction )
42 {
43     LPWSTR szwAction;
44     UINT rc;
45
46     TRACE(" exteral attempt at action %s\n",szAction);
47
48     if (!szAction)
49         return ERROR_FUNCTION_FAILED;
50     if (hInstall == 0)
51         return ERROR_FUNCTION_FAILED;
52
53     szwAction = strdupAtoW(szAction);
54
55     if (!szwAction)
56         return ERROR_FUNCTION_FAILED; 
57
58
59     rc = MsiDoActionW(hInstall, szwAction);
60     HeapFree(GetProcessHeap(),0,szwAction);
61     return rc;
62 }
63
64 /***********************************************************************
65  * MsiDoActionW       (MSI.@)
66  */
67 UINT WINAPI MsiDoActionW( MSIHANDLE hInstall, LPCWSTR szAction )
68 {
69     MSIPACKAGE *package;
70     UINT ret = ERROR_INVALID_HANDLE;
71
72     TRACE(" external attempt at action %s \n",debugstr_w(szAction));
73
74     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
75     if( package )
76     {
77         ret = ACTION_PerformUIAction(package,szAction);
78         msiobj_release( &package->hdr );
79     }
80     return ret;
81 }
82
83 /***********************************************************************
84  * MsiGetTargetPathA        (MSI.@)
85  */
86 UINT WINAPI MsiGetTargetPathA( MSIHANDLE hInstall, LPCSTR szFolder, 
87                                LPSTR szPathBuf, DWORD* pcchPathBuf) 
88 {
89     LPWSTR szwFolder;
90     LPWSTR szwPathBuf;
91     UINT rc;
92
93     TRACE("getting folder %s %p %li\n",szFolder,szPathBuf, *pcchPathBuf);
94
95     if (!szFolder)
96         return ERROR_FUNCTION_FAILED;
97     if (hInstall == 0)
98         return ERROR_FUNCTION_FAILED;
99
100     szwFolder = strdupAtoW(szFolder);
101
102     if (!szwFolder)
103         return ERROR_FUNCTION_FAILED; 
104
105     szwPathBuf = HeapAlloc( GetProcessHeap(), 0 , *pcchPathBuf * sizeof(WCHAR));
106
107     rc = MsiGetTargetPathW(hInstall, szwFolder, szwPathBuf,pcchPathBuf);
108
109     WideCharToMultiByte( CP_ACP, 0, szwPathBuf, *pcchPathBuf, szPathBuf,
110                          *pcchPathBuf, NULL, NULL );
111
112     HeapFree(GetProcessHeap(),0,szwFolder);
113     HeapFree(GetProcessHeap(),0,szwPathBuf);
114
115     return rc;
116 }
117
118 /***********************************************************************
119 * MsiGetTargetPathW        (MSI.@)
120 */
121 UINT WINAPI MsiGetTargetPathW( MSIHANDLE hInstall, LPCWSTR szFolder, LPWSTR
122                                 szPathBuf, DWORD* pcchPathBuf) 
123 {
124     LPWSTR path;
125     UINT rc = ERROR_FUNCTION_FAILED;
126     MSIPACKAGE *package;
127
128     TRACE("(%s %p %li)\n",debugstr_w(szFolder),szPathBuf,*pcchPathBuf);
129
130     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
131     if (!package)
132         return ERROR_INVALID_HANDLE;
133     path = resolve_folder(package, szFolder, FALSE, FALSE, NULL);
134     msiobj_release( &package->hdr );
135
136     if (path && (strlenW(path) > *pcchPathBuf))
137     {
138         *pcchPathBuf = strlenW(path)+1;
139         rc = ERROR_MORE_DATA;
140     }
141     else if (path)
142     {
143         *pcchPathBuf = strlenW(path)+1;
144         strcpyW(szPathBuf,path);
145         TRACE("Returning Path %s\n",debugstr_w(path));
146         rc = ERROR_SUCCESS;
147     }
148     HeapFree(GetProcessHeap(),0,path);
149     
150     return rc;
151 }
152
153 /***********************************************************************
154  * MsiGetSourcePath   (internal)
155  */
156 static UINT MSI_GetSourcePath( MSIHANDLE hInstall, LPCWSTR szFolder,
157                                awstring *szPathBuf, DWORD* pcchPathBuf )
158 {
159     LPWSTR path;
160     UINT r, len;
161     MSIPACKAGE *package;
162
163     TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf );
164
165     if (!szFolder)
166         return ERROR_INVALID_PARAMETER;
167
168     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
169     if (!package)
170         return ERROR_INVALID_HANDLE;
171
172     if (szPathBuf->str.w && !pcchPathBuf )
173     {
174         msiobj_release( &package->hdr );
175         return ERROR_INVALID_PARAMETER;
176     }
177
178     path = resolve_folder(package, szFolder, TRUE, FALSE, NULL);
179     msiobj_release( &package->hdr );
180
181     TRACE("path = %s\n",debugstr_w(path));
182     if (!path)
183         return ERROR_DIRECTORY;
184
185     r = ERROR_SUCCESS;
186     if (pcchPathBuf)
187     {
188         if (szPathBuf->unicode)
189         {
190             len = lstrlenW( path );
191             if (szPathBuf->str.w) 
192                 lstrcpynW( szPathBuf->str.w, path, *pcchPathBuf );
193         }
194         else
195         {
196             len = WideCharToMultiByte( CP_ACP, 0, path, -1,
197                                szPathBuf->str.a, *pcchPathBuf, NULL, NULL );
198             len--;
199         }
200
201         if (len >= *pcchPathBuf)
202             r = ERROR_MORE_DATA;
203         *pcchPathBuf = len;
204     }
205     HeapFree( GetProcessHeap(), 0, path );
206     return r;
207 }
208
209 /***********************************************************************
210  * MsiGetSourcePathA     (MSI.@)
211  */
212 UINT WINAPI MsiGetSourcePathA( MSIHANDLE hInstall, LPCSTR szFolder, 
213                                LPSTR szPathBuf, DWORD* pcchPathBuf )
214 {
215     LPWSTR folder;
216     awstring str;
217     UINT r;
218
219     TRACE("%s %p %p\n", szFolder, debugstr_a(szPathBuf), pcchPathBuf);
220
221     str.unicode = FALSE;
222     str.str.a = szPathBuf;
223
224     folder = strdupAtoW( szFolder );
225     r = MSI_GetSourcePath( hInstall, folder, &str, pcchPathBuf );
226     HeapFree( GetProcessHeap(), 0, folder );
227
228     return r;
229 }
230
231 /***********************************************************************
232  * MsiGetSourcePathW     (MSI.@)
233  */
234 UINT WINAPI MsiGetSourcePathW( MSIHANDLE hInstall, LPCWSTR szFolder,
235                                LPWSTR szPathBuf, DWORD* pcchPathBuf )
236 {
237     awstring str;
238
239     TRACE("%s %p %p\n", debugstr_w(szFolder), szPathBuf, pcchPathBuf );
240
241     str.unicode = TRUE;
242     str.str.w = szPathBuf;
243
244     return MSI_GetSourcePath( hInstall, szFolder, &str, pcchPathBuf );
245 }
246
247 /***********************************************************************
248  * MsiSetTargetPathA  (MSI.@)
249  */
250 UINT WINAPI MsiSetTargetPathA(MSIHANDLE hInstall, LPCSTR szFolder, 
251                              LPCSTR szFolderPath)
252 {
253     LPWSTR szwFolder;
254     LPWSTR szwFolderPath;
255     UINT rc;
256
257     if (!szFolder)
258         return ERROR_FUNCTION_FAILED;
259     if (hInstall == 0)
260         return ERROR_FUNCTION_FAILED;
261
262     szwFolder = strdupAtoW(szFolder);
263     if (!szwFolder)
264         return ERROR_FUNCTION_FAILED; 
265
266     szwFolderPath = strdupAtoW(szFolderPath);
267     if (!szwFolderPath)
268     {
269         HeapFree(GetProcessHeap(),0,szwFolder);
270         return ERROR_FUNCTION_FAILED; 
271     }
272
273     rc = MsiSetTargetPathW(hInstall, szwFolder, szwFolderPath);
274
275     HeapFree(GetProcessHeap(),0,szwFolder);
276     HeapFree(GetProcessHeap(),0,szwFolderPath);
277
278     return rc;
279 }
280
281 /*
282  * Ok my original interpretation of this was wrong. And it looks like msdn has
283  * changed a bit also. The given folder path does not have to actually already
284  * exist, it just cannot be read only and must be a legal folder path.
285  */
286 UINT MSI_SetTargetPathW(MSIPACKAGE *package, LPCWSTR szFolder, 
287                              LPCWSTR szFolderPath)
288 {
289     DWORD attrib;
290     LPWSTR path = NULL;
291     LPWSTR path2 = NULL;
292     MSIFOLDER *folder;
293
294     TRACE("(%p %s %s)\n",package, debugstr_w(szFolder),debugstr_w(szFolderPath));
295
296     if (package==NULL)
297         return ERROR_INVALID_HANDLE;
298
299     if (szFolderPath[0]==0)
300         return ERROR_FUNCTION_FAILED;
301
302     attrib = GetFileAttributesW(szFolderPath);
303     if ( attrib != INVALID_FILE_ATTRIBUTES &&
304           (!(attrib & FILE_ATTRIBUTE_DIRECTORY) ||
305            attrib & FILE_ATTRIBUTE_OFFLINE ||
306            attrib & FILE_ATTRIBUTE_READONLY))
307         return ERROR_FUNCTION_FAILED;
308
309     path = resolve_folder(package,szFolder,FALSE,FALSE,&folder);
310
311     if (!path)
312         return ERROR_INVALID_PARAMETER;
313
314     if (attrib == INVALID_FILE_ATTRIBUTES)
315     {
316         if (!CreateDirectoryW(szFolderPath,NULL))
317             return ERROR_FUNCTION_FAILED;
318         RemoveDirectoryW(szFolderPath);
319     }
320
321     HeapFree(GetProcessHeap(),0,folder->Property);
322     folder->Property = build_directory_name(2, szFolderPath, NULL);
323
324     if (lstrcmpiW(path, folder->Property) == 0)
325     {
326         /*
327          *  Resolved Target has not really changed, so just 
328          *  set this folder and do not recalculate everything.
329          */
330         HeapFree(GetProcessHeap(),0,folder->ResolvedTarget);
331         folder->ResolvedTarget = NULL;
332         path2 = resolve_folder(package,szFolder,FALSE,TRUE,NULL);
333         HeapFree(GetProcessHeap(),0,path2);
334     }
335     else
336     {
337         MSIFOLDER *f;
338
339         LIST_FOR_EACH_ENTRY( f, &package->folders, MSIFOLDER, entry )
340         {
341             HeapFree( GetProcessHeap(),0,f->ResolvedTarget);
342             f->ResolvedTarget=NULL;
343         }
344
345         LIST_FOR_EACH_ENTRY( f, &package->folders, MSIFOLDER, entry )
346         {
347             path2 = resolve_folder(package, f->Directory, FALSE, TRUE, NULL);
348             HeapFree(GetProcessHeap(),0,path2);
349         }
350     }
351     HeapFree(GetProcessHeap(),0,path);
352
353     return ERROR_SUCCESS;
354 }
355
356 /***********************************************************************
357  * MsiSetTargetPathW  (MSI.@)
358  */
359 UINT WINAPI MsiSetTargetPathW(MSIHANDLE hInstall, LPCWSTR szFolder, 
360                              LPCWSTR szFolderPath)
361 {
362     MSIPACKAGE *package;
363     UINT ret;
364
365     TRACE("(%s %s)\n",debugstr_w(szFolder),debugstr_w(szFolderPath));
366
367     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
368     ret = MSI_SetTargetPathW( package, szFolder, szFolderPath );
369     msiobj_release( &package->hdr );
370     return ret;
371 }
372
373 /***********************************************************************
374  *           MsiGetMode    (MSI.@)
375  *
376  * Returns an internal installer state (if it is running in a mode iRunMode)
377  *
378  * PARAMS
379  *   hInstall    [I]  Handle to the installation
380  *   hRunMode    [I]  Checking run mode
381  *        MSIRUNMODE_ADMIN             Administrative mode
382  *        MSIRUNMODE_ADVERTISE         Advertisement mode
383  *        MSIRUNMODE_MAINTENANCE       Maintenance mode
384  *        MSIRUNMODE_ROLLBACKENABLED   Rollback is enabled
385  *        MSIRUNMODE_LOGENABLED        Log file is writing
386  *        MSIRUNMODE_OPERATIONS        Operations in progress??
387  *        MSIRUNMODE_REBOOTATEND       We need to reboot after installation completed
388  *        MSIRUNMODE_REBOOTNOW         We need to reboot to continue the installation
389  *        MSIRUNMODE_CABINET           Files from cabinet are installed
390  *        MSIRUNMODE_SOURCESHORTNAMES  Long names in source files is suppressed
391  *        MSIRUNMODE_TARGETSHORTNAMES  Long names in destination files is suppressed
392  *        MSIRUNMODE_RESERVED11        Reserved
393  *        MSIRUNMODE_WINDOWS9X         Running under Windows95/98
394  *        MSIRUNMODE_ZAWENABLED        Demand installation is supported
395  *        MSIRUNMODE_RESERVED14        Reserved
396  *        MSIRUNMODE_RESERVED15        Reserved
397  *        MSIRUNMODE_SCHEDULED         called from install script
398  *        MSIRUNMODE_ROLLBACK          called from rollback script
399  *        MSIRUNMODE_COMMIT            called from commit script
400  *
401  * RETURNS
402  *    In the state: TRUE
403  *    Not in the state: FALSE
404  *
405  */
406
407 BOOL WINAPI MsiGetMode(MSIHANDLE hInstall, MSIRUNMODE iRunMode)
408 {
409     FIXME("STUB (iRunMode=%i)\n",iRunMode);
410     return TRUE;
411 }
412
413 /***********************************************************************
414  * MsiSetFeatureStateA (MSI.@)
415  *
416  * According to the docs, when this is called it immediately recalculates
417  * all the component states as well
418  */
419 UINT WINAPI MsiSetFeatureStateA(MSIHANDLE hInstall, LPCSTR szFeature,
420                                 INSTALLSTATE iState)
421 {
422     LPWSTR szwFeature = NULL;
423     UINT rc;
424
425     szwFeature = strdupAtoW(szFeature);
426
427     if (!szwFeature)
428         return ERROR_FUNCTION_FAILED;
429    
430     rc = MsiSetFeatureStateW(hInstall,szwFeature, iState); 
431
432     HeapFree(GetProcessHeap(),0,szwFeature);
433
434     return rc;
435 }
436
437
438
439 UINT WINAPI MSI_SetFeatureStateW(MSIPACKAGE* package, LPCWSTR szFeature,
440                                 INSTALLSTATE iState)
441 {
442     UINT rc = ERROR_SUCCESS;
443     MSIFEATURE *feature, *child;
444
445     TRACE(" %s to %i\n",debugstr_w(szFeature), iState);
446
447     feature = get_loaded_feature(package,szFeature);
448     if (!feature)
449         return ERROR_UNKNOWN_FEATURE;
450
451     if (iState == INSTALLSTATE_ADVERTISED && 
452         feature->Attributes & msidbFeatureAttributesDisallowAdvertise)
453         return ERROR_FUNCTION_FAILED;
454
455     feature->ActionRequest = iState;
456     feature->Action = iState;
457
458     ACTION_UpdateComponentStates(package,szFeature);
459
460     /* update all the features that are children of this feature */
461     LIST_FOR_EACH_ENTRY( child, &package->features, MSIFEATURE, entry )
462     {
463         if (lstrcmpW(szFeature, child->Feature_Parent) == 0)
464             MSI_SetFeatureStateW(package, child->Feature, iState);
465     }
466     
467     return rc;
468 }
469
470 /***********************************************************************
471  * MsiSetFeatureStateW (MSI.@)
472  */
473 UINT WINAPI MsiSetFeatureStateW(MSIHANDLE hInstall, LPCWSTR szFeature,
474                                 INSTALLSTATE iState)
475 {
476     MSIPACKAGE* package;
477     UINT rc = ERROR_SUCCESS;
478
479     TRACE(" %s to %i\n",debugstr_w(szFeature), iState);
480
481     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
482     if (!package)
483         return ERROR_INVALID_HANDLE;
484
485     rc = MSI_SetFeatureStateW(package,szFeature,iState);
486
487     msiobj_release( &package->hdr );
488     return rc;
489 }
490
491 /***********************************************************************
492 * MsiGetFeatureStateA   (MSI.@)
493 */
494 UINT WINAPI MsiGetFeatureStateA(MSIHANDLE hInstall, LPSTR szFeature,
495                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
496 {
497     LPWSTR szwFeature = NULL;
498     UINT rc;
499     
500     szwFeature = strdupAtoW(szFeature);
501
502     rc = MsiGetFeatureStateW(hInstall,szwFeature,piInstalled, piAction);
503
504     HeapFree( GetProcessHeap(), 0 , szwFeature);
505
506     return rc;
507 }
508
509 UINT MSI_GetFeatureStateW(MSIPACKAGE *package, LPWSTR szFeature,
510                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
511 {
512     MSIFEATURE *feature;
513
514     feature = get_loaded_feature(package,szFeature);
515     if (!feature)
516         return ERROR_UNKNOWN_FEATURE;
517
518     if (piInstalled)
519         *piInstalled = feature->Installed;
520
521     if (piAction)
522         *piAction = feature->Action;
523
524     TRACE("returning %i %i\n", feature->Installed, feature->Action);
525
526     return ERROR_SUCCESS;
527 }
528
529 /***********************************************************************
530 * MsiGetFeatureStateW   (MSI.@)
531 */
532 UINT WINAPI MsiGetFeatureStateW(MSIHANDLE hInstall, LPWSTR szFeature,
533                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
534 {
535     MSIPACKAGE* package;
536     UINT ret;
537
538     TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szFeature), piInstalled,
539 piAction);
540
541     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
542     if (!package)
543         return ERROR_INVALID_HANDLE;
544     ret = MSI_GetFeatureStateW(package, szFeature, piInstalled, piAction);
545     msiobj_release( &package->hdr );
546     return ret;
547 }
548
549 /***********************************************************************
550  * MsiSetComponentStateA (MSI.@)
551  */
552 UINT WINAPI MsiSetComponentStateA(MSIHANDLE hInstall, LPCSTR szComponent,
553                                   INSTALLSTATE iState)
554 {
555     UINT rc;
556     LPWSTR szwComponent = strdupAtoW(szComponent);
557
558     rc = MsiSetComponentStateW(hInstall, szwComponent, iState);
559
560     HeapFree(GetProcessHeap(), 0, szwComponent);
561
562     return rc;
563 }
564
565 /***********************************************************************
566  * MsiGetComponentStateA (MSI.@)
567  */
568 UINT WINAPI MsiGetComponentStateA(MSIHANDLE hInstall, LPSTR szComponent,
569                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
570 {
571     LPWSTR szwComponent= NULL;
572     UINT rc;
573     
574     szwComponent= strdupAtoW(szComponent);
575
576     rc = MsiGetComponentStateW(hInstall,szwComponent,piInstalled, piAction);
577
578     HeapFree( GetProcessHeap(), 0 , szwComponent);
579
580     return rc;
581 }
582
583 static UINT MSI_SetComponentStateW(MSIPACKAGE *package, LPCWSTR szComponent,
584                                    INSTALLSTATE iState)
585 {
586     MSICOMPONENT *comp;
587
588     TRACE("%p %s %d\n", package, debugstr_w(szComponent), iState);
589
590     comp = get_loaded_component(package, szComponent);
591     if (!comp)
592         return ERROR_UNKNOWN_COMPONENT;
593
594     comp->Installed = iState;
595
596     return ERROR_SUCCESS;
597 }
598
599 UINT MSI_GetComponentStateW(MSIPACKAGE *package, LPWSTR szComponent,
600                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
601 {
602     MSICOMPONENT *comp;
603
604     TRACE("%p %s %p %p\n", package, debugstr_w(szComponent),
605            piInstalled, piAction);
606
607     comp = get_loaded_component(package,szComponent);
608     if (!comp)
609         return ERROR_UNKNOWN_COMPONENT;
610
611     if (piInstalled)
612         *piInstalled = comp->Installed;
613
614     if (piAction)
615         *piAction = comp->Action;
616
617     TRACE("states (%i, %i)\n", comp->Installed, comp->Action );
618
619     return ERROR_SUCCESS;
620 }
621
622 /***********************************************************************
623  * MsiSetComponentStateW (MSI.@)
624  */
625 UINT WINAPI MsiSetComponentStateW(MSIHANDLE hInstall, LPCWSTR szComponent,
626                                   INSTALLSTATE iState)
627 {
628     MSIPACKAGE* package;
629     UINT ret;
630
631     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
632     if (!package)
633         return ERROR_INVALID_HANDLE;
634     ret = MSI_SetComponentStateW(package, szComponent, iState);
635     msiobj_release(&package->hdr);
636     return ret;
637 }
638
639 /***********************************************************************
640  * MsiGetComponentStateW (MSI.@)
641  */
642 UINT WINAPI MsiGetComponentStateW(MSIHANDLE hInstall, LPWSTR szComponent,
643                   INSTALLSTATE *piInstalled, INSTALLSTATE *piAction)
644 {
645     MSIPACKAGE* package;
646     UINT ret;
647
648     TRACE("%ld %s %p %p\n", hInstall, debugstr_w(szComponent),
649            piInstalled, piAction);
650
651     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
652     if (!package)
653         return ERROR_INVALID_HANDLE;
654     ret = MSI_GetComponentStateW( package, szComponent, piInstalled, piAction);
655     msiobj_release( &package->hdr );
656     return ret;
657 }
658
659 /***********************************************************************
660  * MsiGetLanguage (MSI.@)
661  */
662 LANGID WINAPI MsiGetLanguage(MSIHANDLE hInstall)
663 {
664     MSIPACKAGE* package;
665     LANGID langid;
666     LPWSTR buffer;
667     static const WCHAR szProductLanguage[] =
668         {'P','r','o','d','u','c','t','L','a','n','g','u','a','g','e',0};
669     
670     package = msihandle2msiinfo(hInstall, MSIHANDLETYPE_PACKAGE);
671     if (!package)
672         return ERROR_INVALID_HANDLE;
673
674     buffer = load_dynamic_property(package,szProductLanguage,NULL);
675     langid = atoiW(buffer);
676
677     HeapFree(GetProcessHeap(),0,buffer);
678     msiobj_release (&package->hdr);
679     return langid;
680 }