2 * Implementation of the AppSearch action of the Microsoft Installer (msi.dll)
4 * Copyright 2005 Juan Lang
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.
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.
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
29 #include "wine/unicode.h"
30 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(msi
);
36 typedef struct tagMSISIGNATURE
38 LPWSTR Name
; /* NOT owned by this structure */
39 LPWSTR Property
; /* NOT owned by this structure */
52 static void ACTION_VerStrToInteger(LPCWSTR verStr
, PDWORD ms
, PDWORD ls
)
55 int x1
= 0, x2
= 0, x3
= 0, x4
= 0;
58 ptr
= strchrW(verStr
, '.');
62 ptr
= strchrW(ptr
+ 1, '.');
67 ptr
= strchrW(ptr
+ 1, '.');
71 /* FIXME: byte-order dependent? */
76 /* Fills in sig with the the values from the Signature table, where name is the
77 * signature to find. Upon return, sig->File will be NULL if the record is not
78 * found, and not NULL if it is found.
79 * Warning: clears all fields in sig!
80 * Returns ERROR_SUCCESS upon success (where not finding the record counts as
81 * success), something else on error.
83 static UINT
ACTION_AppSearchGetSignature(MSIPACKAGE
*package
, MSISIGNATURE
*sig
,
88 static const WCHAR ExecSeqQuery
[] = {
89 's','e','l','e','c','t',' ','*',' ',
91 'S','i','g','n','a','t','u','r','e',' ',
92 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e',' ','=',' ',
95 TRACE("(package %p, sig %p)\n", package
, sig
);
96 memset(sig
, 0, sizeof(*sig
));
97 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, name
);
98 if (rc
== ERROR_SUCCESS
)
102 WCHAR
*minVersion
, *maxVersion
;
104 rc
= MSI_ViewExecute(view
, 0);
105 if (rc
!= ERROR_SUCCESS
)
107 TRACE("MSI_ViewExecute returned %d\n", rc
);
110 rc
= MSI_ViewFetch(view
,&row
);
111 if (rc
!= ERROR_SUCCESS
)
113 TRACE("MSI_ViewFetch returned %d\n", rc
);
119 sig
->File
= load_dynamic_stringW(row
,2);
120 minVersion
= load_dynamic_stringW(row
,3);
123 ACTION_VerStrToInteger(minVersion
, &sig
->MinVersionMS
,
125 HeapFree(GetProcessHeap(), 0, minVersion
);
127 maxVersion
= load_dynamic_stringW(row
,4);
130 ACTION_VerStrToInteger(maxVersion
, &sig
->MaxVersionMS
,
132 HeapFree(GetProcessHeap(), 0, maxVersion
);
134 sig
->MinSize
= MSI_RecordGetInteger(row
,5);
135 if (sig
->MinSize
== MSI_NULL_INTEGER
)
137 sig
->MaxSize
= MSI_RecordGetInteger(row
,6);
138 if (sig
->MaxSize
== MSI_NULL_INTEGER
)
140 sig
->Languages
= load_dynamic_stringW(row
,9);
141 time
= MSI_RecordGetInteger(row
,7);
142 if (time
!= MSI_NULL_INTEGER
)
143 DosDateTimeToFileTime(HIWORD(time
), LOWORD(time
), &sig
->MinTime
);
144 time
= MSI_RecordGetInteger(row
,8);
145 if (time
!= MSI_NULL_INTEGER
)
146 DosDateTimeToFileTime(HIWORD(time
), LOWORD(time
), &sig
->MaxTime
);
147 TRACE("Found file name %s for Signature_ %s;\n",
148 debugstr_w(sig
->File
), debugstr_w(name
));
149 TRACE("MinVersion is %d.%d.%d.%d\n", HIWORD(sig
->MinVersionMS
),
150 LOWORD(sig
->MinVersionMS
), HIWORD(sig
->MinVersionLS
),
151 LOWORD(sig
->MinVersionLS
));
152 TRACE("MaxVersion is %d.%d.%d.%d\n", HIWORD(sig
->MaxVersionMS
),
153 LOWORD(sig
->MaxVersionMS
), HIWORD(sig
->MaxVersionLS
),
154 LOWORD(sig
->MaxVersionLS
));
155 TRACE("MinSize is %ld, MaxSize is %ld;\n", sig
->MinSize
, sig
->MaxSize
);
156 TRACE("Languages is %s\n", debugstr_w(sig
->Languages
));
159 msiobj_release(&row
->hdr
);
161 msiobj_release(&view
->hdr
);
165 TRACE("MSI_OpenQuery returned %d\n", rc
);
169 TRACE("returning %d\n", rc
);
173 static UINT
ACTION_AppSearchComponents(MSIPACKAGE
*package
, BOOL
*appFound
,
178 static const WCHAR ExecSeqQuery
[] = {
179 's','e','l','e','c','t',' ','*',' ',
181 'C','o','m','p','L','o','c','a','t','o','r',' ',
182 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ',
183 '\'','%','s','\'',0};
185 TRACE("(package %p, appFound %p, sig %p)\n", package
, appFound
, sig
);
187 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, sig
->Name
);
188 if (rc
== ERROR_SUCCESS
)
194 rc
= MSI_ViewExecute(view
, 0);
195 if (rc
!= ERROR_SUCCESS
)
197 TRACE("MSI_ViewExecute returned %d\n", rc
);
200 rc
= MSI_ViewFetch(view
,&row
);
201 if (rc
!= ERROR_SUCCESS
)
203 TRACE("MSI_ViewFetch returned %d\n", rc
);
210 sz
=sizeof(guid
)/sizeof(guid
[0]);
211 rc
= MSI_RecordGetStringW(row
,2,guid
,&sz
);
212 if (rc
!= ERROR_SUCCESS
)
214 ERR("Error is %x\n",rc
);
217 FIXME("AppSearch unimplemented for CompLocator table (GUID %s)\n",
221 msiobj_release(&row
->hdr
);
223 msiobj_release(&view
->hdr
);
227 TRACE("MSI_OpenQuery returned %d\n", rc
);
231 TRACE("returning %d\n", rc
);
235 static UINT
ACTION_AppSearchReg(MSIPACKAGE
*package
, BOOL
*appFound
,
240 static const WCHAR ExecSeqQuery
[] = {
241 's','e','l','e','c','t',' ','*',' ',
243 'R','e','g','L','o','c','a','t','o','r',' ',
244 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ',
245 '\'','%','s','\'',0};
247 TRACE("(package %p, appFound %p, sig %p)\n", package
, appFound
, sig
);
249 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, sig
->Name
);
250 if (rc
== ERROR_SUCCESS
)
255 rc
= MSI_ViewExecute(view
, 0);
256 if (rc
!= ERROR_SUCCESS
)
258 TRACE("MSI_ViewExecute returned %d\n", rc
);
261 rc
= MSI_ViewFetch(view
,&row
);
262 if (rc
!= ERROR_SUCCESS
)
264 TRACE("MSI_ViewFetch returned %d\n", rc
);
270 keyPath
= load_dynamic_stringW(row
,3);
271 FIXME("AppSearch unimplemented for RegLocator (key path %s)\n",
272 debugstr_w(keyPath
));
273 HeapFree(GetProcessHeap(), 0, keyPath
);
276 msiobj_release(&row
->hdr
);
278 msiobj_release(&view
->hdr
);
282 TRACE("MSI_OpenQuery returned %d\n", rc
);
286 TRACE("returning %d\n", rc
);
290 static UINT
ACTION_AppSearchIni(MSIPACKAGE
*package
, BOOL
*appFound
,
295 static const WCHAR ExecSeqQuery
[] = {
296 's','e','l','e','c','t',' ','*',' ',
298 'I','n','i','L','o','c','a','t','o','r',' ',
299 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ',
300 '\'','%','s','\'',0};
302 TRACE("(package %p, appFound %p, sig %p)\n", package
, appFound
, sig
);
304 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, sig
->Name
);
305 if (rc
== ERROR_SUCCESS
)
310 rc
= MSI_ViewExecute(view
, 0);
311 if (rc
!= ERROR_SUCCESS
)
313 TRACE("MSI_ViewExecute returned %d\n", rc
);
316 rc
= MSI_ViewFetch(view
,&row
);
317 if (rc
!= ERROR_SUCCESS
)
319 TRACE("MSI_ViewFetch returned %d\n", rc
);
325 fileName
= load_dynamic_stringW(row
,2);
326 FIXME("AppSearch unimplemented for IniLocator (ini file name %s)\n",
327 debugstr_w(fileName
));
328 HeapFree(GetProcessHeap(), 0, fileName
);
331 msiobj_release(&row
->hdr
);
333 msiobj_release(&view
->hdr
);
337 TRACE("MSI_OpenQuery returned %d\n", rc
);
342 TRACE("returning %d\n", rc
);
346 /* Expands the value in src into a path without property names and only
347 * containing long path names into dst. Replaces at most len characters of dst,
348 * and always NULL-terminates dst if dst is not NULL and len >= 1.
350 * Assumes src and dst are non-overlapping.
351 * FIXME: return code probably needed:
352 * - what does AppSearch return if the table values are invalid?
353 * - what if dst is too small?
355 static void ACTION_ExpandAnyPath(MSIPACKAGE
*package
, WCHAR
*src
, WCHAR
*dst
,
361 if (!src
|| !dst
|| !len
)
364 /* Ignore the short portion of the path, don't think we can use it anyway */
365 if ((ptr
= strchrW(src
, '|')))
369 while (*ptr
&& copied
< len
- 1)
371 WCHAR
*prop
= strchrW(ptr
, '[');
375 WCHAR
*propEnd
= strchrW(prop
+ 1, ']');
379 WARN("Unterminated property name in AnyPath: %s\n",
388 propLen
= len
- copied
- 1;
389 MSI_GetPropertyW(package
, prop
+ 1, dst
+ copied
, &propLen
);
396 size_t toCopy
= min(strlenW(ptr
) + 1, len
- copied
- 1);
398 memcpy(dst
+ copied
, ptr
, toCopy
* sizeof(WCHAR
));
403 *(dst
+ copied
) = '\0';
406 /* Sets *matches to whether the file (whose path is filePath) matches the
407 * versions set in sig.
408 * Return ERROR_SUCCESS in case of success (whether or not the file matches),
409 * something else if a install-halting error occurs.
411 static UINT
ACTION_FileVersionMatches(MSISIGNATURE
*sig
, LPCWSTR filePath
,
414 UINT rc
= ERROR_SUCCESS
;
419 FIXME(": need to check version for languages %s\n",
420 debugstr_w(sig
->Languages
));
424 DWORD zero
, size
= GetFileVersionInfoSizeW(filePath
, &zero
);
428 LPVOID buf
= HeapAlloc(GetProcessHeap(), 0, size
);
432 static const WCHAR rootW
[] = { '\\',0 };
434 LPVOID subBlock
= NULL
;
436 if (GetFileVersionInfoW(filePath
, 0, size
, buf
))
437 VerQueryValueW(buf
, rootW
, &subBlock
, &versionLen
);
440 VS_FIXEDFILEINFO
*info
=
441 (VS_FIXEDFILEINFO
*)subBlock
;
443 TRACE("Comparing file version %d.%d.%d.%d:\n",
444 HIWORD(info
->dwFileVersionMS
),
445 LOWORD(info
->dwFileVersionMS
),
446 HIWORD(info
->dwFileVersionLS
),
447 LOWORD(info
->dwFileVersionLS
));
448 if (info
->dwFileVersionMS
< sig
->MinVersionMS
449 || (info
->dwFileVersionMS
== sig
->MinVersionMS
&&
450 info
->dwFileVersionLS
< sig
->MinVersionLS
))
452 TRACE("Less than minimum version %d.%d.%d.%d\n",
453 HIWORD(sig
->MinVersionMS
),
454 LOWORD(sig
->MinVersionMS
),
455 HIWORD(sig
->MinVersionLS
),
456 LOWORD(sig
->MinVersionLS
));
458 else if (info
->dwFileVersionMS
< sig
->MinVersionMS
459 || (info
->dwFileVersionMS
== sig
->MinVersionMS
&&
460 info
->dwFileVersionLS
< sig
->MinVersionLS
))
462 TRACE("Greater than minimum version %d.%d.%d.%d\n",
463 HIWORD(sig
->MaxVersionMS
),
464 LOWORD(sig
->MaxVersionMS
),
465 HIWORD(sig
->MaxVersionLS
),
466 LOWORD(sig
->MaxVersionLS
));
471 HeapFree(GetProcessHeap(), 0, buf
);
474 rc
= ERROR_OUTOFMEMORY
;
480 /* Sets *matches to whether the file in findData matches that in sig.
481 * fullFilePath is assumed to be the full path of the file specified in
482 * findData, which may be necessary to compare the version.
483 * Return ERROR_SUCCESS in case of success (whether or not the file matches),
484 * something else if a install-halting error occurs.
486 static UINT
ACTION_FileMatchesSig(MSISIGNATURE
*sig
,
487 LPWIN32_FIND_DATAW findData
, LPCWSTR fullFilePath
, BOOL
*matches
)
489 UINT rc
= ERROR_SUCCESS
;
492 /* assumes the caller has already ensured the filenames match, so check
495 if (sig
->MinTime
.dwLowDateTime
|| sig
->MinTime
.dwHighDateTime
)
497 if (findData
->ftCreationTime
.dwHighDateTime
<
498 sig
->MinTime
.dwHighDateTime
||
499 (findData
->ftCreationTime
.dwHighDateTime
== sig
->MinTime
.dwHighDateTime
500 && findData
->ftCreationTime
.dwLowDateTime
<
501 sig
->MinTime
.dwLowDateTime
))
504 if (*matches
&& (sig
->MaxTime
.dwLowDateTime
|| sig
->MaxTime
.dwHighDateTime
))
506 if (findData
->ftCreationTime
.dwHighDateTime
>
507 sig
->MaxTime
.dwHighDateTime
||
508 (findData
->ftCreationTime
.dwHighDateTime
== sig
->MaxTime
.dwHighDateTime
509 && findData
->ftCreationTime
.dwLowDateTime
>
510 sig
->MaxTime
.dwLowDateTime
))
513 if (*matches
&& sig
->MinSize
&& findData
->nFileSizeLow
< sig
->MinSize
)
515 if (*matches
&& sig
->MaxSize
&& findData
->nFileSizeLow
> sig
->MaxSize
)
517 if (*matches
&& (sig
->MinVersionMS
|| sig
->MinVersionLS
||
518 sig
->MaxVersionMS
|| sig
->MaxVersionLS
))
519 rc
= ACTION_FileVersionMatches(sig
, fullFilePath
, matches
);
523 /* Recursively searches the directory dir for files that match the signature
524 * sig, up to (depth + 1) levels deep. That is, if depth is 0, it searches dir
525 * (and only dir). If depth is 1, searches dir and its immediate
527 * Assumes sig->File is not NULL.
528 * Returns ERROR_SUCCESS on success (which may include non-critical errors),
529 * something else on failures which should halt the install.
531 static UINT
ACTION_RecurseSearchDirectory(MSIPACKAGE
*package
, BOOL
*appFound
,
532 MSISIGNATURE
*sig
, LPCWSTR dir
, int depth
)
534 static const WCHAR starDotStarW
[] = { '*','.','*',0 };
535 UINT rc
= ERROR_SUCCESS
;
536 size_t dirLen
= lstrlenW(dir
), fileLen
= lstrlenW(sig
->File
);
539 TRACE("Searching directory %s for file %s, depth %d\n", debugstr_w(dir
),
540 debugstr_w(sig
->File
), depth
);
543 return ERROR_INVALID_PARAMETER
;
546 /* We need the buffer in both paths below, so go ahead and allocate it
547 * here. Add two because we might need to add a backslash if the dir name
548 * isn't backslash-terminated.
550 buf
= HeapAlloc(GetProcessHeap(), 0,
551 (dirLen
+ max(fileLen
, lstrlenW(starDotStarW
)) + 2) * sizeof(WCHAR
));
554 /* a depth of 0 implies we should search dir, so go ahead and search */
556 WIN32_FIND_DATAW findData
;
558 memcpy(buf
, dir
, dirLen
* sizeof(WCHAR
));
559 if (buf
[dirLen
- 1] != '\\')
560 buf
[dirLen
++ - 1] = '\\';
561 memcpy(buf
+ dirLen
, sig
->File
, (fileLen
+ 1) * sizeof(WCHAR
));
562 hFind
= FindFirstFileW(buf
, &findData
);
563 if (hFind
!= INVALID_HANDLE_VALUE
)
567 /* assuming Signature can't contain wildcards for the file name,
568 * so don't bother with FindNextFileW here.
570 if (!(rc
= ACTION_FileMatchesSig(sig
, &findData
, buf
, &matches
))
573 TRACE("found file, setting %s to %s\n",
574 debugstr_w(sig
->Property
), debugstr_w(buf
));
575 rc
= MSI_SetPropertyW(package
, sig
->Property
, buf
);
580 if (rc
== ERROR_SUCCESS
&& !*appFound
&& depth
> 0)
583 WIN32_FIND_DATAW findData
;
585 memcpy(buf
, dir
, dirLen
* sizeof(WCHAR
));
586 if (buf
[dirLen
- 1] != '\\')
587 buf
[dirLen
++ - 1] = '\\';
588 lstrcpyW(buf
+ dirLen
, starDotStarW
);
589 hFind
= FindFirstFileW(buf
, &findData
);
590 if (hFind
!= INVALID_HANDLE_VALUE
)
592 if (findData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
593 rc
= ACTION_RecurseSearchDirectory(package
, appFound
,
594 sig
, findData
.cFileName
, depth
- 1);
595 while (rc
== ERROR_SUCCESS
&& !*appFound
&&
596 FindNextFileW(hFind
, &findData
) != 0)
598 if (findData
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
599 rc
= ACTION_RecurseSearchDirectory(package
, appFound
,
600 sig
, findData
.cFileName
, depth
- 1);
605 HeapFree(GetProcessHeap(), 0, buf
);
608 rc
= ERROR_OUTOFMEMORY
;
613 static UINT
ACTION_CheckDirectory(MSIPACKAGE
*package
, MSISIGNATURE
*sig
,
616 UINT rc
= ERROR_SUCCESS
;
618 if (GetFileAttributesW(dir
) & FILE_ATTRIBUTE_DIRECTORY
)
620 TRACE("directory exists, setting %s to %s\n",
621 debugstr_w(sig
->Property
), debugstr_w(dir
));
622 rc
= MSI_SetPropertyW(package
, sig
->Property
, dir
);
627 static BOOL
ACTION_IsFullPath(LPCWSTR path
)
629 WCHAR first
= toupperW(path
[0]);
632 if (first
>= 'A' && first
<= 'A' && path
[1] == ':')
634 else if (path
[0] == '\\' && path
[1] == '\\')
641 static UINT
ACTION_SearchDirectory(MSIPACKAGE
*package
, MSISIGNATURE
*sig
,
642 LPCWSTR expanded
, int depth
)
647 if (ACTION_IsFullPath(expanded
))
650 rc
= ACTION_RecurseSearchDirectory(package
, &found
, sig
,
654 /* Recursively searching a directory makes no sense when the
655 * directory to search is the thing you're trying to find.
657 rc
= ACTION_CheckDirectory(package
, sig
, expanded
);
662 WCHAR pathWithDrive
[MAX_PATH
] = { 'C',':','\\',0 };
663 DWORD drives
= GetLogicalDrives();
668 for (i
= 0; rc
== ERROR_SUCCESS
&& !found
&& i
< 26; i
++)
669 if (drives
& (1 << drives
))
671 pathWithDrive
[0] = 'A' + i
;
672 if (GetDriveTypeW(pathWithDrive
) == DRIVE_FIXED
)
674 strncpyW(pathWithDrive
+ 3, expanded
,
675 sizeof(pathWithDrive
) / sizeof(pathWithDrive
[0]) - 3);
677 rc
= ACTION_RecurseSearchDirectory(package
, &found
, sig
,
678 pathWithDrive
, depth
);
680 rc
= ACTION_CheckDirectory(package
, sig
, pathWithDrive
);
687 static UINT
ACTION_AppSearchDr(MSIPACKAGE
*package
, MSISIGNATURE
*sig
)
691 static const WCHAR ExecSeqQuery
[] = {
692 's','e','l','e','c','t',' ','*',' ',
694 'D','r','L','o','c','a','t','o','r',' ',
695 'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e','_',' ','=',' ',
696 '\'','%','s','\'',0};
698 TRACE("(package %p, sig %p)\n", package
, sig
);
699 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
, sig
->Name
);
700 if (rc
== ERROR_SUCCESS
)
703 WCHAR buffer
[MAX_PATH
], expanded
[MAX_PATH
];
707 rc
= MSI_ViewExecute(view
, 0);
708 if (rc
!= ERROR_SUCCESS
)
710 TRACE("MSI_ViewExecute returned %d\n", rc
);
713 rc
= MSI_ViewFetch(view
,&row
);
714 if (rc
!= ERROR_SUCCESS
)
716 TRACE("MSI_ViewFetch returned %d\n", rc
);
721 /* check whether parent is set */
723 sz
=sizeof(buffer
)/sizeof(buffer
[0]);
724 rc
= MSI_RecordGetStringW(row
,2,buffer
,&sz
);
725 if (rc
!= ERROR_SUCCESS
)
727 ERR("Error is %x\n",rc
);
732 FIXME(": searching parent (%s) unimplemented\n",
736 /* no parent, now look for path */
738 sz
=sizeof(buffer
)/sizeof(buffer
[0]);
739 rc
= MSI_RecordGetStringW(row
,3,buffer
,&sz
);
740 if (rc
!= ERROR_SUCCESS
)
742 ERR("Error is %x\n",rc
);
745 if (MSI_RecordIsNull(row
,4))
748 depth
= MSI_RecordGetInteger(row
,4);
749 ACTION_ExpandAnyPath(package
, buffer
, expanded
,
750 sizeof(expanded
) / sizeof(expanded
[0]));
751 rc
= ACTION_SearchDirectory(package
, sig
, expanded
, depth
);
754 msiobj_release(&row
->hdr
);
756 msiobj_release(&view
->hdr
);
760 TRACE("MSI_OpenQuery returned %d\n", rc
);
765 TRACE("returning %d\n", rc
);
769 /* http://msdn.microsoft.com/library/en-us/msi/setup/appsearch_table.asp
770 * is the best reference for the AppSearch table and how it's used.
772 UINT
ACTION_AppSearch(MSIPACKAGE
*package
)
776 static const WCHAR ExecSeqQuery
[] = {
777 's','e','l','e','c','t',' ','*',' ',
779 'A','p','p','S','e','a','r','c','h',0};
781 rc
= MSI_OpenQuery(package
->db
, &view
, ExecSeqQuery
);
782 if (rc
== ERROR_SUCCESS
)
785 WCHAR propBuf
[0x100], sigBuf
[0x100];
788 BOOL appFound
= FALSE
;
790 rc
= MSI_ViewExecute(view
, 0);
791 if (rc
!= ERROR_SUCCESS
)
796 rc
= MSI_ViewFetch(view
,&row
);
797 if (rc
!= ERROR_SUCCESS
)
803 /* get property and signature */
805 sz
=sizeof(propBuf
)/sizeof(propBuf
[0]);
806 rc
= MSI_RecordGetStringW(row
,1,propBuf
,&sz
);
807 if (rc
!= ERROR_SUCCESS
)
809 ERR("Error is %x\n",rc
);
810 msiobj_release(&row
->hdr
);
814 sz
=sizeof(sigBuf
)/sizeof(sigBuf
[0]);
815 rc
= MSI_RecordGetStringW(row
,2,sigBuf
,&sz
);
816 if (rc
!= ERROR_SUCCESS
)
818 ERR("Error is %x\n",rc
);
819 msiobj_release(&row
->hdr
);
822 TRACE("Searching for Property %s, Signature_ %s\n",
823 debugstr_w(propBuf
), debugstr_w(sigBuf
));
824 /* This clears all the fields, so set Name and Property afterward */
825 rc
= ACTION_AppSearchGetSignature(package
, &sig
, sigBuf
);
827 sig
.Property
= propBuf
;
828 if (rc
== ERROR_SUCCESS
)
830 rc
= ACTION_AppSearchComponents(package
, &appFound
, &sig
);
831 if (rc
== ERROR_SUCCESS
&& !appFound
)
833 rc
= ACTION_AppSearchReg(package
, &appFound
, &sig
);
834 if (rc
== ERROR_SUCCESS
&& !appFound
)
836 rc
= ACTION_AppSearchIni(package
, &appFound
, &sig
);
837 if (rc
== ERROR_SUCCESS
&& !appFound
)
838 rc
= ACTION_AppSearchDr(package
, &sig
);
842 HeapFree(GetProcessHeap(), 0, sig
.File
);
843 HeapFree(GetProcessHeap(), 0, sig
.Languages
);
844 msiobj_release(&row
->hdr
);
849 msiobj_release(&view
->hdr
);