2006-02-17 00:04:10 +00:00
|
|
|
/*
|
|
|
|
* Implementation of the AppSearch action of the Microsoft Installer (msi.dll)
|
|
|
|
*
|
|
|
|
* Copyright 2005 Juan Lang
|
|
|
|
*
|
|
|
|
* 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
|
2006-08-01 23:12:11 +00:00
|
|
|
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
|
2006-02-17 00:04:10 +00:00
|
|
|
*/
|
2013-01-24 23:00:42 +00:00
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
#include "msipriv.h"
|
|
|
|
|
|
|
|
WINE_DEFAULT_DEBUG_CHANNEL(msi);
|
|
|
|
|
|
|
|
typedef struct tagMSISIGNATURE
|
|
|
|
{
|
2006-08-01 23:12:11 +00:00
|
|
|
LPCWSTR Name; /* NOT owned by this structure */
|
2006-02-17 00:04:10 +00:00
|
|
|
LPWSTR File;
|
|
|
|
DWORD MinVersionMS;
|
|
|
|
DWORD MinVersionLS;
|
|
|
|
DWORD MaxVersionMS;
|
|
|
|
DWORD MaxVersionLS;
|
|
|
|
DWORD MinSize;
|
|
|
|
DWORD MaxSize;
|
|
|
|
FILETIME MinTime;
|
|
|
|
FILETIME MaxTime;
|
|
|
|
LPWSTR Languages;
|
|
|
|
}MSISIGNATURE;
|
|
|
|
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
void msi_parse_version_string(LPCWSTR verStr, PDWORD ms, PDWORD ls)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
const WCHAR *ptr;
|
|
|
|
int x1 = 0, x2 = 0, x3 = 0, x4 = 0;
|
|
|
|
|
|
|
|
x1 = atoiW(verStr);
|
|
|
|
ptr = strchrW(verStr, '.');
|
|
|
|
if (ptr)
|
|
|
|
{
|
|
|
|
x2 = atoiW(ptr + 1);
|
|
|
|
ptr = strchrW(ptr + 1, '.');
|
|
|
|
}
|
|
|
|
if (ptr)
|
|
|
|
{
|
|
|
|
x3 = atoiW(ptr + 1);
|
|
|
|
ptr = strchrW(ptr + 1, '.');
|
|
|
|
}
|
|
|
|
if (ptr)
|
|
|
|
x4 = atoiW(ptr + 1);
|
|
|
|
/* FIXME: byte-order dependent? */
|
|
|
|
*ms = x1 << 16 | x2;
|
2011-03-20 08:47:41 +00:00
|
|
|
if (ls) *ls = x3 << 16 | x4;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
/* Fills in sig with the values from the Signature table, where name is the
|
2006-02-17 00:04:10 +00:00
|
|
|
* signature to find. Upon return, sig->File will be NULL if the record is not
|
|
|
|
* found, and not NULL if it is found.
|
|
|
|
* Warning: clears all fields in sig!
|
|
|
|
* Returns ERROR_SUCCESS upon success (where not finding the record counts as
|
|
|
|
* success), something else on error.
|
|
|
|
*/
|
2008-01-16 10:11:22 +00:00
|
|
|
static UINT ACTION_AppSearchGetSignature(MSIPACKAGE *package, MSISIGNATURE *sig, LPCWSTR name)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
static const WCHAR query[] = {
|
|
|
|
's','e','l','e','c','t',' ','*',' ',
|
|
|
|
'f','r','o','m',' ',
|
|
|
|
'S','i','g','n','a','t','u','r','e',' ',
|
|
|
|
'w','h','e','r','e',' ','S','i','g','n','a','t','u','r','e',' ','=',' ',
|
|
|
|
'\'','%','s','\'',0};
|
2010-10-22 13:18:11 +00:00
|
|
|
LPWSTR minVersion, maxVersion, p;
|
2008-01-16 10:11:22 +00:00
|
|
|
MSIRECORD *row;
|
|
|
|
DWORD time;
|
|
|
|
|
|
|
|
TRACE("package %p, sig %p\n", package, sig);
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
memset(sig, 0, sizeof(*sig));
|
2006-08-01 23:12:11 +00:00
|
|
|
sig->Name = name;
|
2008-01-16 10:11:22 +00:00
|
|
|
row = MSI_QueryGetRecord( package->db, query, name );
|
|
|
|
if (!row)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
TRACE("failed to query signature for %s\n", debugstr_w(name));
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
/* get properties */
|
|
|
|
sig->File = msi_dup_record_field(row,2);
|
2010-10-22 13:18:11 +00:00
|
|
|
if ((p = strchrW(sig->File, '|')))
|
|
|
|
{
|
|
|
|
p++;
|
|
|
|
memmove(sig->File, p, (strlenW(p) + 1) * sizeof(WCHAR));
|
|
|
|
}
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
minVersion = msi_dup_record_field(row,3);
|
|
|
|
if (minVersion)
|
|
|
|
{
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
msi_parse_version_string( minVersion, &sig->MinVersionMS, &sig->MinVersionLS );
|
2008-01-16 10:11:22 +00:00
|
|
|
msi_free( minVersion );
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2008-01-16 10:11:22 +00:00
|
|
|
maxVersion = msi_dup_record_field(row,4);
|
|
|
|
if (maxVersion)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
Sync aclui, advapi32, atl, authz, kernel32, msi, oledlg, powrprof, qmgr, riched20, samlib to Wine 1.2rc5
Update some psdk Headers to get some more synched winetests build
svn path=/trunk/; revision=47930
2010-07-03 12:42:55 +00:00
|
|
|
msi_parse_version_string( maxVersion, &sig->MaxVersionMS, &sig->MaxVersionLS );
|
2008-01-16 10:11:22 +00:00
|
|
|
msi_free( maxVersion );
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2008-01-16 10:11:22 +00:00
|
|
|
sig->MinSize = MSI_RecordGetInteger(row,5);
|
|
|
|
if (sig->MinSize == MSI_NULL_INTEGER)
|
|
|
|
sig->MinSize = 0;
|
|
|
|
sig->MaxSize = MSI_RecordGetInteger(row,6);
|
|
|
|
if (sig->MaxSize == MSI_NULL_INTEGER)
|
|
|
|
sig->MaxSize = 0;
|
|
|
|
sig->Languages = msi_dup_record_field(row,9);
|
|
|
|
time = MSI_RecordGetInteger(row,7);
|
|
|
|
if (time != MSI_NULL_INTEGER)
|
|
|
|
DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MinTime);
|
|
|
|
time = MSI_RecordGetInteger(row,8);
|
|
|
|
if (time != MSI_NULL_INTEGER)
|
|
|
|
DosDateTimeToFileTime(HIWORD(time), LOWORD(time), &sig->MaxTime);
|
|
|
|
|
|
|
|
TRACE("Found file name %s for Signature_ %s;\n",
|
|
|
|
debugstr_w(sig->File), debugstr_w(name));
|
|
|
|
TRACE("MinVersion is %d.%d.%d.%d\n", HIWORD(sig->MinVersionMS),
|
|
|
|
LOWORD(sig->MinVersionMS), HIWORD(sig->MinVersionLS),
|
|
|
|
LOWORD(sig->MinVersionLS));
|
|
|
|
TRACE("MaxVersion is %d.%d.%d.%d\n", HIWORD(sig->MaxVersionMS),
|
|
|
|
LOWORD(sig->MaxVersionMS), HIWORD(sig->MaxVersionLS),
|
|
|
|
LOWORD(sig->MaxVersionLS));
|
|
|
|
TRACE("MinSize is %d, MaxSize is %d;\n", sig->MinSize, sig->MaxSize);
|
|
|
|
TRACE("Languages is %s\n", debugstr_w(sig->Languages));
|
|
|
|
|
|
|
|
msiobj_release( &row->hdr );
|
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
/* Frees any memory allocated in sig */
|
|
|
|
static void ACTION_FreeSignature(MSISIGNATURE *sig)
|
|
|
|
{
|
|
|
|
msi_free(sig->File);
|
|
|
|
msi_free(sig->Languages);
|
|
|
|
}
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
static LPWSTR app_search_file(LPWSTR path, MSISIGNATURE *sig)
|
|
|
|
{
|
|
|
|
VS_FIXEDFILEINFO *info;
|
|
|
|
DWORD attr, handle, size;
|
|
|
|
LPWSTR val = NULL;
|
|
|
|
LPBYTE buffer;
|
|
|
|
|
|
|
|
if (!sig->File)
|
|
|
|
{
|
|
|
|
PathRemoveFileSpecW(path);
|
|
|
|
PathAddBackslashW(path);
|
|
|
|
|
|
|
|
attr = GetFileAttributesW(path);
|
|
|
|
if (attr != INVALID_FILE_ATTRIBUTES &&
|
|
|
|
(attr & FILE_ATTRIBUTE_DIRECTORY))
|
|
|
|
return strdupW(path);
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = GetFileAttributesW(path);
|
2009-05-20 12:59:23 +00:00
|
|
|
if (attr == INVALID_FILE_ATTRIBUTES ||
|
|
|
|
(attr & FILE_ATTRIBUTE_DIRECTORY))
|
2008-12-27 15:10:14 +00:00
|
|
|
return NULL;
|
|
|
|
|
|
|
|
size = GetFileVersionInfoSizeW(path, &handle);
|
|
|
|
if (!size)
|
|
|
|
return strdupW(path);
|
|
|
|
|
|
|
|
buffer = msi_alloc(size);
|
|
|
|
if (!buffer)
|
|
|
|
return NULL;
|
|
|
|
|
|
|
|
if (!GetFileVersionInfoW(path, 0, size, buffer))
|
|
|
|
goto done;
|
|
|
|
|
2009-10-25 11:06:09 +00:00
|
|
|
if (!VerQueryValueW(buffer, szBackSlash, (LPVOID)&info, &size) || !info)
|
2008-12-27 15:10:14 +00:00
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (sig->MinVersionLS || sig->MinVersionMS)
|
|
|
|
{
|
|
|
|
if (info->dwFileVersionMS < sig->MinVersionMS)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (info->dwFileVersionMS == sig->MinVersionMS &&
|
|
|
|
info->dwFileVersionLS < sig->MinVersionLS)
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sig->MaxVersionLS || sig->MaxVersionMS)
|
|
|
|
{
|
|
|
|
if (info->dwFileVersionMS > sig->MaxVersionMS)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
if (info->dwFileVersionMS == sig->MaxVersionMS &&
|
|
|
|
info->dwFileVersionLS > sig->MaxVersionLS)
|
|
|
|
goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
val = strdupW(path);
|
|
|
|
|
|
|
|
done:
|
|
|
|
msi_free(buffer);
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
static UINT ACTION_AppSearchComponents(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
static const WCHAR query[] = {
|
|
|
|
'S','E','L','E','C','T',' ','*',' ',
|
|
|
|
'F','R','O','M',' ',
|
|
|
|
'`','C','o','m','p','L','o','c','a','t','o','r','`',' ',
|
|
|
|
'W','H','E','R','E',' ','`','S','i','g','n','a','t','u','r','e','_','`',' ','=',' ',
|
|
|
|
'\'','%','s','\'',0};
|
|
|
|
static const WCHAR sigquery[] = {
|
|
|
|
'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
|
|
|
'`','S','i','g','n','a','t','u','r','e','`',' ',
|
|
|
|
'W','H','E','R','E',' ','`','S','i','g','n','a','t','u','r','e','`',' ','=',' ',
|
|
|
|
'\'','%','s','\'',0};
|
|
|
|
|
|
|
|
MSIRECORD *row, *rec;
|
|
|
|
LPCWSTR signature, guid;
|
|
|
|
BOOL sigpresent = TRUE;
|
|
|
|
BOOL isdir;
|
|
|
|
UINT type;
|
|
|
|
WCHAR path[MAX_PATH];
|
|
|
|
DWORD size = MAX_PATH;
|
|
|
|
LPWSTR ptr;
|
|
|
|
DWORD attr;
|
|
|
|
|
|
|
|
TRACE("%s\n", debugstr_w(sig->Name));
|
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
*appValue = NULL;
|
2008-01-16 10:11:22 +00:00
|
|
|
|
|
|
|
row = MSI_QueryGetRecord(package->db, query, sig->Name);
|
|
|
|
if (!row)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
TRACE("failed to query CompLocator for %s\n", debugstr_w(sig->Name));
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
signature = MSI_RecordGetString(row, 1);
|
|
|
|
guid = MSI_RecordGetString(row, 2);
|
|
|
|
type = MSI_RecordGetInteger(row, 3);
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
rec = MSI_QueryGetRecord(package->db, sigquery, signature);
|
|
|
|
if (!rec)
|
|
|
|
sigpresent = FALSE;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
*path = '\0';
|
|
|
|
MsiLocateComponentW(guid, path, &size);
|
|
|
|
if (!*path)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
attr = GetFileAttributesW(path);
|
|
|
|
if (attr == INVALID_FILE_ATTRIBUTES)
|
|
|
|
goto done;
|
|
|
|
|
|
|
|
isdir = (attr & FILE_ATTRIBUTE_DIRECTORY);
|
|
|
|
|
|
|
|
if (type != msidbLocatorTypeDirectory && sigpresent && !isdir)
|
|
|
|
{
|
2008-12-27 15:10:14 +00:00
|
|
|
*appValue = app_search_file(path, sig);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2008-01-16 10:11:22 +00:00
|
|
|
else if (!sigpresent && (type != msidbLocatorTypeDirectory || isdir))
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
if (type == msidbLocatorTypeFileName)
|
|
|
|
{
|
|
|
|
ptr = strrchrW(path, '\\');
|
|
|
|
*(ptr + 1) = '\0';
|
|
|
|
}
|
|
|
|
else
|
|
|
|
PathAddBackslashW(path);
|
|
|
|
|
|
|
|
*appValue = strdupW(path);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2008-12-27 15:10:14 +00:00
|
|
|
else if (sigpresent)
|
|
|
|
{
|
|
|
|
PathAddBackslashW(path);
|
|
|
|
lstrcatW(path, MSI_RecordGetString(rec, 2));
|
|
|
|
|
|
|
|
attr = GetFileAttributesW(path);
|
2009-05-20 12:59:23 +00:00
|
|
|
if (attr != INVALID_FILE_ATTRIBUTES &&
|
|
|
|
!(attr & FILE_ATTRIBUTE_DIRECTORY))
|
2008-12-27 15:10:14 +00:00
|
|
|
*appValue = strdupW(path);
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
done:
|
|
|
|
if (rec) msiobj_release(&rec->hdr);
|
|
|
|
msiobj_release(&row->hdr);
|
|
|
|
return ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
static void ACTION_ConvertRegValue(DWORD regType, const BYTE *value, DWORD sz,
|
|
|
|
LPWSTR *appValue)
|
|
|
|
{
|
|
|
|
static const WCHAR dwordFmt[] = { '#','%','d','\0' };
|
2008-12-27 15:10:14 +00:00
|
|
|
static const WCHAR binPre[] = { '#','x','\0' };
|
|
|
|
static const WCHAR binFmt[] = { '%','0','2','X','\0' };
|
|
|
|
LPWSTR ptr;
|
2006-08-01 23:12:11 +00:00
|
|
|
DWORD i;
|
|
|
|
|
|
|
|
switch (regType)
|
|
|
|
{
|
|
|
|
case REG_SZ:
|
2006-10-22 20:23:59 +00:00
|
|
|
if (*(LPCWSTR)value == '#')
|
2006-08-01 23:12:11 +00:00
|
|
|
{
|
|
|
|
/* escape leading pound with another */
|
|
|
|
*appValue = msi_alloc(sz + sizeof(WCHAR));
|
|
|
|
(*appValue)[0] = '#';
|
|
|
|
strcpyW(*appValue + 1, (LPCWSTR)value);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*appValue = msi_alloc(sz);
|
|
|
|
strcpyW(*appValue, (LPCWSTR)value);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case REG_DWORD:
|
|
|
|
/* 7 chars for digits, 1 for NULL, 1 for #, and 1 for sign
|
|
|
|
* char if needed
|
|
|
|
*/
|
|
|
|
*appValue = msi_alloc(10 * sizeof(WCHAR));
|
|
|
|
sprintfW(*appValue, dwordFmt, *(const DWORD *)value);
|
|
|
|
break;
|
|
|
|
case REG_EXPAND_SZ:
|
2008-12-27 15:10:14 +00:00
|
|
|
sz = ExpandEnvironmentStringsW((LPCWSTR)value, NULL, 0);
|
|
|
|
*appValue = msi_alloc(sz * sizeof(WCHAR));
|
|
|
|
ExpandEnvironmentStringsW((LPCWSTR)value, *appValue, sz);
|
2006-08-01 23:12:11 +00:00
|
|
|
break;
|
|
|
|
case REG_BINARY:
|
2008-12-27 15:10:14 +00:00
|
|
|
/* #x<nibbles>\0 */
|
|
|
|
*appValue = msi_alloc((sz * 2 + 3) * sizeof(WCHAR));
|
|
|
|
lstrcpyW(*appValue, binPre);
|
|
|
|
ptr = *appValue + lstrlenW(binPre);
|
|
|
|
for (i = 0; i < sz; i++, ptr += 2)
|
|
|
|
sprintfW(ptr, binFmt, value[i]);
|
2006-08-01 23:12:11 +00:00
|
|
|
break;
|
|
|
|
default:
|
2006-10-22 20:23:59 +00:00
|
|
|
WARN("unimplemented for values of type %d\n", regType);
|
2006-08-01 23:12:11 +00:00
|
|
|
*appValue = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT ACTION_SearchDirectory(MSIPACKAGE *package, MSISIGNATURE *sig,
|
|
|
|
LPCWSTR path, int depth, LPWSTR *appValue);
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
static UINT ACTION_AppSearchReg(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
static const WCHAR query[] = {
|
2012-01-21 17:19:12 +00:00
|
|
|
'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
|
|
|
'R','e','g','L','o','c','a','t','o','r',' ','W','H','E','R','E',' ',
|
2008-01-16 10:11:22 +00:00
|
|
|
'S','i','g','n','a','t','u','r','e','_',' ','=',' ', '\'','%','s','\'',0};
|
2012-01-21 17:19:12 +00:00
|
|
|
const WCHAR *keyPath, *valueName;
|
|
|
|
WCHAR *deformatted = NULL, *ptr = NULL, *end;
|
2008-01-16 10:11:22 +00:00
|
|
|
int root, type;
|
|
|
|
HKEY rootKey, key = NULL;
|
|
|
|
DWORD sz = 0, regType;
|
|
|
|
LPBYTE value = NULL;
|
|
|
|
MSIRECORD *row;
|
2006-02-17 00:04:10 +00:00
|
|
|
UINT rc;
|
2008-01-16 10:11:22 +00:00
|
|
|
|
|
|
|
TRACE("%s\n", debugstr_w(sig->Name));
|
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
*appValue = NULL;
|
2008-01-16 10:11:22 +00:00
|
|
|
|
|
|
|
row = MSI_QueryGetRecord( package->db, query, sig->Name );
|
|
|
|
if (!row)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
TRACE("failed to query RegLocator for %s\n", debugstr_w(sig->Name));
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
root = MSI_RecordGetInteger(row, 2);
|
|
|
|
keyPath = MSI_RecordGetString(row, 3);
|
|
|
|
valueName = MSI_RecordGetString(row, 4);
|
|
|
|
type = MSI_RecordGetInteger(row, 5);
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
deformat_string(package, keyPath, &deformatted);
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
switch (root)
|
|
|
|
{
|
|
|
|
case msidbRegistryRootClassesRoot:
|
|
|
|
rootKey = HKEY_CLASSES_ROOT;
|
|
|
|
break;
|
|
|
|
case msidbRegistryRootCurrentUser:
|
|
|
|
rootKey = HKEY_CURRENT_USER;
|
|
|
|
break;
|
|
|
|
case msidbRegistryRootLocalMachine:
|
|
|
|
rootKey = HKEY_LOCAL_MACHINE;
|
|
|
|
break;
|
|
|
|
case msidbRegistryRootUsers:
|
|
|
|
rootKey = HKEY_USERS;
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
WARN("Unknown root key %d\n", root);
|
|
|
|
goto end;
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
rc = RegOpenKeyW(rootKey, deformatted, &key);
|
|
|
|
if (rc)
|
|
|
|
{
|
|
|
|
TRACE("RegOpenKeyW returned %d\n", rc);
|
|
|
|
goto end;
|
|
|
|
}
|
2006-08-01 23:12:11 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_free(deformatted);
|
|
|
|
deformat_string(package, valueName, &deformatted);
|
|
|
|
|
|
|
|
rc = RegQueryValueExW(key, deformatted, NULL, NULL, NULL, &sz);
|
2008-01-16 10:11:22 +00:00
|
|
|
if (rc)
|
|
|
|
{
|
|
|
|
TRACE("RegQueryValueExW returned %d\n", rc);
|
|
|
|
goto end;
|
|
|
|
}
|
|
|
|
/* FIXME: sanity-check sz before allocating (is there an upper-limit
|
|
|
|
* on the value of a property?)
|
|
|
|
*/
|
|
|
|
value = msi_alloc( sz );
|
2012-01-21 17:19:12 +00:00
|
|
|
rc = RegQueryValueExW(key, deformatted, NULL, ®Type, value, &sz);
|
2008-01-16 10:11:22 +00:00
|
|
|
if (rc)
|
|
|
|
{
|
|
|
|
TRACE("RegQueryValueExW returned %d\n", rc);
|
|
|
|
goto end;
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
/* bail out if the registry key is empty */
|
|
|
|
if (sz == 0)
|
|
|
|
goto end;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2009-11-16 01:13:42 +00:00
|
|
|
if ((regType == REG_SZ || regType == REG_EXPAND_SZ) &&
|
|
|
|
(ptr = strchrW((LPWSTR)value, '"')) && (end = strchrW(++ptr, '"')))
|
2008-12-27 15:10:14 +00:00
|
|
|
*end = '\0';
|
|
|
|
else
|
|
|
|
ptr = (LPWSTR)value;
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
switch (type & 0x0f)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
case msidbLocatorTypeDirectory:
|
2012-01-21 17:19:12 +00:00
|
|
|
ACTION_SearchDirectory(package, sig, ptr, 0, appValue);
|
2008-01-16 10:11:22 +00:00
|
|
|
break;
|
|
|
|
case msidbLocatorTypeFileName:
|
2008-12-27 15:10:14 +00:00
|
|
|
*appValue = app_search_file(ptr, sig);
|
2008-01-16 10:11:22 +00:00
|
|
|
break;
|
|
|
|
case msidbLocatorTypeRawValue:
|
|
|
|
ACTION_ConvertRegValue(regType, value, sz, appValue);
|
|
|
|
break;
|
|
|
|
default:
|
2010-02-06 21:28:28 +00:00
|
|
|
FIXME("unimplemented for type %d (key path %s, value %s)\n",
|
2008-01-16 10:11:22 +00:00
|
|
|
type, debugstr_w(keyPath), debugstr_w(valueName));
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2008-01-16 10:11:22 +00:00
|
|
|
end:
|
|
|
|
msi_free( value );
|
|
|
|
RegCloseKey( key );
|
|
|
|
msi_free( deformatted );
|
|
|
|
|
|
|
|
msiobj_release(&row->hdr);
|
|
|
|
return ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
static LPWSTR get_ini_field(LPWSTR buf, int field)
|
|
|
|
{
|
|
|
|
LPWSTR beg, end;
|
|
|
|
int i = 1;
|
|
|
|
|
|
|
|
if (field == 0)
|
|
|
|
return strdupW(buf);
|
|
|
|
|
|
|
|
beg = buf;
|
|
|
|
while ((end = strchrW(beg, ',')) && i < field)
|
|
|
|
{
|
|
|
|
beg = end + 1;
|
|
|
|
while (*beg && *beg == ' ')
|
|
|
|
beg++;
|
|
|
|
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
|
|
|
|
end = strchrW(beg, ',');
|
|
|
|
if (!end)
|
|
|
|
end = beg + lstrlenW(beg);
|
|
|
|
|
|
|
|
*end = '\0';
|
|
|
|
return strdupW(beg);
|
|
|
|
}
|
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
static UINT ACTION_AppSearchIni(MSIPACKAGE *package, LPWSTR *appValue,
|
2006-02-17 00:04:10 +00:00
|
|
|
MSISIGNATURE *sig)
|
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
static const WCHAR query[] = {
|
|
|
|
's','e','l','e','c','t',' ','*',' ',
|
|
|
|
'f','r','o','m',' ',
|
|
|
|
'I','n','i','L','o','c','a','t','o','r',' ',
|
|
|
|
'w','h','e','r','e',' ',
|
|
|
|
'S','i','g','n','a','t','u','r','e','_',' ','=',' ','\'','%','s','\'',0};
|
|
|
|
MSIRECORD *row;
|
|
|
|
LPWSTR fileName, section, key;
|
|
|
|
int field, type;
|
|
|
|
WCHAR buf[MAX_PATH];
|
|
|
|
|
|
|
|
TRACE("%s\n", debugstr_w(sig->Name));
|
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
*appValue = NULL;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
row = MSI_QueryGetRecord( package->db, query, sig->Name );
|
|
|
|
if (!row)
|
|
|
|
{
|
|
|
|
TRACE("failed to query IniLocator for %s\n", debugstr_w(sig->Name));
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
fileName = msi_dup_record_field(row, 2);
|
|
|
|
section = msi_dup_record_field(row, 3);
|
|
|
|
key = msi_dup_record_field(row, 4);
|
|
|
|
field = MSI_RecordGetInteger(row, 5);
|
|
|
|
type = MSI_RecordGetInteger(row, 6);
|
|
|
|
if (field == MSI_NULL_INTEGER)
|
|
|
|
field = 0;
|
|
|
|
if (type == MSI_NULL_INTEGER)
|
|
|
|
type = 0;
|
|
|
|
|
|
|
|
GetPrivateProfileStringW(section, key, NULL, buf, MAX_PATH, fileName);
|
|
|
|
if (buf[0])
|
|
|
|
{
|
|
|
|
switch (type & 0x0f)
|
2006-08-01 23:12:11 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
case msidbLocatorTypeDirectory:
|
2008-12-27 15:10:14 +00:00
|
|
|
ACTION_SearchDirectory(package, sig, buf, 0, appValue);
|
2008-01-16 10:11:22 +00:00
|
|
|
break;
|
|
|
|
case msidbLocatorTypeFileName:
|
2008-12-27 15:10:14 +00:00
|
|
|
*appValue = app_search_file(buf, sig);
|
2008-01-16 10:11:22 +00:00
|
|
|
break;
|
|
|
|
case msidbLocatorTypeRawValue:
|
2008-12-27 15:10:14 +00:00
|
|
|
*appValue = get_ini_field(buf, field);
|
2008-01-16 10:11:22 +00:00
|
|
|
break;
|
2006-08-01 23:12:11 +00:00
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
msi_free(fileName);
|
|
|
|
msi_free(section);
|
|
|
|
msi_free(key);
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
msiobj_release(&row->hdr);
|
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Expands the value in src into a path without property names and only
|
|
|
|
* containing long path names into dst. Replaces at most len characters of dst,
|
|
|
|
* and always NULL-terminates dst if dst is not NULL and len >= 1.
|
|
|
|
* May modify src.
|
|
|
|
* Assumes src and dst are non-overlapping.
|
|
|
|
* FIXME: return code probably needed:
|
|
|
|
* - what does AppSearch return if the table values are invalid?
|
|
|
|
* - what if dst is too small?
|
|
|
|
*/
|
|
|
|
static void ACTION_ExpandAnyPath(MSIPACKAGE *package, WCHAR *src, WCHAR *dst,
|
|
|
|
size_t len)
|
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
WCHAR *ptr, *deformatted;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
if (!src || !dst || !len)
|
2006-10-22 20:23:59 +00:00
|
|
|
{
|
|
|
|
if (dst) *dst = '\0';
|
2006-02-17 00:04:10 +00:00
|
|
|
return;
|
2006-10-22 20:23:59 +00:00
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
dst[0] = '\0';
|
|
|
|
|
|
|
|
/* Ignore the short portion of the path */
|
2006-02-17 00:04:10 +00:00
|
|
|
if ((ptr = strchrW(src, '|')))
|
|
|
|
ptr++;
|
|
|
|
else
|
|
|
|
ptr = src;
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
deformat_string(package, ptr, &deformatted);
|
2008-12-27 15:10:14 +00:00
|
|
|
if (!deformatted || strlenW(deformatted) > len - 1)
|
2008-01-16 10:11:22 +00:00
|
|
|
{
|
|
|
|
msi_free(deformatted);
|
|
|
|
return;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2008-01-16 10:11:22 +00:00
|
|
|
|
|
|
|
lstrcpyW(dst, deformatted);
|
|
|
|
dst[lstrlenW(deformatted)] = '\0';
|
|
|
|
msi_free(deformatted);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2012-05-14 21:41:31 +00:00
|
|
|
static LANGID *parse_languages( const WCHAR *languages, DWORD *num_ids )
|
|
|
|
{
|
|
|
|
UINT i, count = 1;
|
|
|
|
WCHAR *str = strdupW( languages ), *p, *q;
|
|
|
|
LANGID *ret;
|
|
|
|
|
|
|
|
if (!str) return NULL;
|
|
|
|
for (p = q = str; (q = strchrW( q, ',' )); q++) count++;
|
|
|
|
|
|
|
|
if (!(ret = msi_alloc( count * sizeof(LANGID) )))
|
|
|
|
{
|
|
|
|
msi_free( str );
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
i = 0;
|
|
|
|
while (*p)
|
|
|
|
{
|
|
|
|
q = strchrW( p, ',' );
|
|
|
|
if (q) *q = 0;
|
|
|
|
ret[i] = atoiW( p );
|
|
|
|
if (!q) break;
|
|
|
|
p = q + 1;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
msi_free( str );
|
|
|
|
*num_ids = count;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL match_languages( const void *version, const WCHAR *languages )
|
|
|
|
{
|
|
|
|
struct lang
|
|
|
|
{
|
|
|
|
USHORT id;
|
|
|
|
USHORT codepage;
|
|
|
|
} *lang;
|
|
|
|
DWORD len, num_ids, i, j;
|
|
|
|
BOOL found = FALSE;
|
|
|
|
LANGID *ids;
|
|
|
|
|
|
|
|
if (!languages || !languages[0]) return TRUE;
|
|
|
|
if (!VerQueryValueW( version, szLangResource, (void **)&lang, &len )) return FALSE;
|
|
|
|
if (!(ids = parse_languages( languages, &num_ids ))) return FALSE;
|
|
|
|
|
|
|
|
for (i = 0; i < num_ids; i++)
|
|
|
|
{
|
|
|
|
found = FALSE;
|
|
|
|
for (j = 0; j < len / sizeof(struct lang); j++)
|
|
|
|
{
|
|
|
|
if (!ids[i] || ids[i] == lang[j].id) found = TRUE;
|
|
|
|
}
|
|
|
|
if (!found) goto done;
|
|
|
|
}
|
|
|
|
|
|
|
|
done:
|
|
|
|
msi_free( ids );
|
|
|
|
return found;
|
|
|
|
}
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
/* Sets *matches to whether the file (whose path is filePath) matches the
|
|
|
|
* versions set in sig.
|
|
|
|
* Return ERROR_SUCCESS in case of success (whether or not the file matches),
|
|
|
|
* something else if an install-halting error occurs.
|
|
|
|
*/
|
2008-01-16 10:11:22 +00:00
|
|
|
static UINT ACTION_FileVersionMatches(const MSISIGNATURE *sig, LPCWSTR filePath,
|
2006-02-17 00:04:10 +00:00
|
|
|
BOOL *matches)
|
|
|
|
{
|
2012-05-14 21:41:31 +00:00
|
|
|
UINT len;
|
|
|
|
void *version;
|
|
|
|
VS_FIXEDFILEINFO *info = NULL;
|
|
|
|
DWORD zero, size = GetFileVersionInfoSizeW( filePath, &zero );
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
*matches = FALSE;
|
|
|
|
|
2012-05-14 21:41:31 +00:00
|
|
|
if (!size) return ERROR_SUCCESS;
|
|
|
|
if (!(version = msi_alloc( size ))) return ERROR_OUTOFMEMORY;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2012-05-14 21:41:31 +00:00
|
|
|
if (GetFileVersionInfoW( filePath, 0, size, version ))
|
|
|
|
VerQueryValueW( version, szBackSlash, (void **)&info, &len );
|
|
|
|
|
|
|
|
if (info)
|
|
|
|
{
|
|
|
|
TRACE("comparing file version %d.%d.%d.%d:\n",
|
|
|
|
HIWORD(info->dwFileVersionMS),
|
|
|
|
LOWORD(info->dwFileVersionMS),
|
|
|
|
HIWORD(info->dwFileVersionLS),
|
|
|
|
LOWORD(info->dwFileVersionLS));
|
|
|
|
if (info->dwFileVersionMS < sig->MinVersionMS
|
|
|
|
|| (info->dwFileVersionMS == sig->MinVersionMS &&
|
|
|
|
info->dwFileVersionLS < sig->MinVersionLS))
|
|
|
|
{
|
|
|
|
TRACE("less than minimum version %d.%d.%d.%d\n",
|
|
|
|
HIWORD(sig->MinVersionMS),
|
|
|
|
LOWORD(sig->MinVersionMS),
|
|
|
|
HIWORD(sig->MinVersionLS),
|
|
|
|
LOWORD(sig->MinVersionLS));
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2012-05-14 21:41:31 +00:00
|
|
|
else if ((sig->MaxVersionMS || sig->MaxVersionLS) &&
|
|
|
|
(info->dwFileVersionMS > sig->MaxVersionMS ||
|
|
|
|
(info->dwFileVersionMS == sig->MaxVersionMS &&
|
|
|
|
info->dwFileVersionLS > sig->MaxVersionLS)))
|
|
|
|
{
|
|
|
|
TRACE("greater than maximum version %d.%d.%d.%d\n",
|
|
|
|
HIWORD(sig->MaxVersionMS),
|
|
|
|
LOWORD(sig->MaxVersionMS),
|
|
|
|
HIWORD(sig->MaxVersionLS),
|
|
|
|
LOWORD(sig->MaxVersionLS));
|
|
|
|
}
|
|
|
|
else if (!match_languages( version, sig->Languages ))
|
|
|
|
{
|
|
|
|
TRACE("languages %s not supported\n", debugstr_w( sig->Languages ));
|
|
|
|
}
|
|
|
|
else *matches = TRUE;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2012-05-14 21:41:31 +00:00
|
|
|
msi_free( version );
|
|
|
|
return ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Sets *matches to whether the file in findData matches that in sig.
|
|
|
|
* fullFilePath is assumed to be the full path of the file specified in
|
|
|
|
* findData, which may be necessary to compare the version.
|
|
|
|
* Return ERROR_SUCCESS in case of success (whether or not the file matches),
|
|
|
|
* something else if an install-halting error occurs.
|
|
|
|
*/
|
2008-01-16 10:11:22 +00:00
|
|
|
static UINT ACTION_FileMatchesSig(const MSISIGNATURE *sig,
|
|
|
|
const WIN32_FIND_DATAW *findData, LPCWSTR fullFilePath, BOOL *matches)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
UINT rc = ERROR_SUCCESS;
|
|
|
|
|
|
|
|
*matches = TRUE;
|
|
|
|
/* assumes the caller has already ensured the filenames match, so check
|
|
|
|
* the other fields..
|
|
|
|
*/
|
|
|
|
if (sig->MinTime.dwLowDateTime || sig->MinTime.dwHighDateTime)
|
|
|
|
{
|
|
|
|
if (findData->ftCreationTime.dwHighDateTime <
|
|
|
|
sig->MinTime.dwHighDateTime ||
|
|
|
|
(findData->ftCreationTime.dwHighDateTime == sig->MinTime.dwHighDateTime
|
|
|
|
&& findData->ftCreationTime.dwLowDateTime <
|
|
|
|
sig->MinTime.dwLowDateTime))
|
|
|
|
*matches = FALSE;
|
|
|
|
}
|
|
|
|
if (*matches && (sig->MaxTime.dwLowDateTime || sig->MaxTime.dwHighDateTime))
|
|
|
|
{
|
|
|
|
if (findData->ftCreationTime.dwHighDateTime >
|
|
|
|
sig->MaxTime.dwHighDateTime ||
|
|
|
|
(findData->ftCreationTime.dwHighDateTime == sig->MaxTime.dwHighDateTime
|
|
|
|
&& findData->ftCreationTime.dwLowDateTime >
|
|
|
|
sig->MaxTime.dwLowDateTime))
|
|
|
|
*matches = FALSE;
|
|
|
|
}
|
|
|
|
if (*matches && sig->MinSize && findData->nFileSizeLow < sig->MinSize)
|
|
|
|
*matches = FALSE;
|
|
|
|
if (*matches && sig->MaxSize && findData->nFileSizeLow > sig->MaxSize)
|
|
|
|
*matches = FALSE;
|
|
|
|
if (*matches && (sig->MinVersionMS || sig->MinVersionLS ||
|
|
|
|
sig->MaxVersionMS || sig->MaxVersionLS))
|
|
|
|
rc = ACTION_FileVersionMatches(sig, fullFilePath, matches);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Recursively searches the directory dir for files that match the signature
|
|
|
|
* sig, up to (depth + 1) levels deep. That is, if depth is 0, it searches dir
|
|
|
|
* (and only dir). If depth is 1, searches dir and its immediate
|
|
|
|
* subdirectories.
|
|
|
|
* Assumes sig->File is not NULL.
|
|
|
|
* Returns ERROR_SUCCESS on success (which may include non-critical errors),
|
|
|
|
* something else on failures which should halt the install.
|
|
|
|
*/
|
2006-08-01 23:12:11 +00:00
|
|
|
static UINT ACTION_RecurseSearchDirectory(MSIPACKAGE *package, LPWSTR *appValue,
|
2006-02-17 00:04:10 +00:00
|
|
|
MSISIGNATURE *sig, LPCWSTR dir, int depth)
|
|
|
|
{
|
2008-12-27 15:10:14 +00:00
|
|
|
HANDLE hFind;
|
|
|
|
WIN32_FIND_DATAW findData;
|
2006-02-17 00:04:10 +00:00
|
|
|
UINT rc = ERROR_SUCCESS;
|
|
|
|
size_t dirLen = lstrlenW(dir), fileLen = lstrlenW(sig->File);
|
2008-12-27 15:10:14 +00:00
|
|
|
WCHAR subpath[MAX_PATH];
|
2006-02-17 00:04:10 +00:00
|
|
|
WCHAR *buf;
|
2009-12-22 09:28:03 +00:00
|
|
|
DWORD len;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
static const WCHAR starDotStarW[] = { '*','.','*',0 };
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
TRACE("Searching directory %s for file %s, depth %d\n", debugstr_w(dir),
|
2008-12-27 15:10:14 +00:00
|
|
|
debugstr_w(sig->File), depth);
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
if (depth < 0)
|
2008-12-27 15:10:14 +00:00
|
|
|
return ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
*appValue = NULL;
|
2006-02-17 00:04:10 +00:00
|
|
|
/* We need the buffer in both paths below, so go ahead and allocate it
|
|
|
|
* here. Add two because we might need to add a backslash if the dir name
|
|
|
|
* isn't backslash-terminated.
|
|
|
|
*/
|
2009-12-22 09:28:03 +00:00
|
|
|
len = dirLen + max(fileLen, strlenW(starDotStarW)) + 2;
|
|
|
|
buf = msi_alloc(len * sizeof(WCHAR));
|
2008-12-27 15:10:14 +00:00
|
|
|
if (!buf)
|
|
|
|
return ERROR_OUTOFMEMORY;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
lstrcpyW(buf, dir);
|
|
|
|
PathAddBackslashW(buf);
|
|
|
|
lstrcatW(buf, sig->File);
|
|
|
|
|
|
|
|
hFind = FindFirstFileW(buf, &findData);
|
|
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
|
|
|
{
|
|
|
|
if (!(findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
BOOL matches;
|
|
|
|
|
2008-12-27 15:10:14 +00:00
|
|
|
rc = ACTION_FileMatchesSig(sig, &findData, buf, &matches);
|
|
|
|
if (rc == ERROR_SUCCESS && matches)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2006-08-01 23:12:11 +00:00
|
|
|
TRACE("found file, returning %s\n", debugstr_w(buf));
|
|
|
|
*appValue = buf;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
2008-12-27 15:10:14 +00:00
|
|
|
FindClose(hFind);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (rc == ERROR_SUCCESS && !*appValue)
|
|
|
|
{
|
|
|
|
lstrcpyW(buf, dir);
|
|
|
|
PathAddBackslashW(buf);
|
|
|
|
lstrcatW(buf, starDotStarW);
|
|
|
|
|
|
|
|
hFind = FindFirstFileW(buf, &findData);
|
|
|
|
if (hFind != INVALID_HANDLE_VALUE)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-12-27 15:10:14 +00:00
|
|
|
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY &&
|
2011-03-20 08:47:41 +00:00
|
|
|
strcmpW( findData.cFileName, szDot ) &&
|
|
|
|
strcmpW( findData.cFileName, szDotDot ))
|
2008-12-27 15:10:14 +00:00
|
|
|
{
|
|
|
|
lstrcpyW(subpath, dir);
|
|
|
|
PathAppendW(subpath, findData.cFileName);
|
|
|
|
rc = ACTION_RecurseSearchDirectory(package, appValue, sig,
|
|
|
|
subpath, depth - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
while (rc == ERROR_SUCCESS && !*appValue &&
|
|
|
|
FindNextFileW(hFind, &findData) != 0)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2011-03-20 08:47:41 +00:00
|
|
|
if (!strcmpW( findData.cFileName, szDot ) ||
|
|
|
|
!strcmpW( findData.cFileName, szDotDot ))
|
2008-12-27 15:10:14 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
lstrcpyW(subpath, dir);
|
|
|
|
PathAppendW(subpath, findData.cFileName);
|
2006-02-17 00:04:10 +00:00
|
|
|
if (findData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
2008-12-27 15:10:14 +00:00
|
|
|
rc = ACTION_RecurseSearchDirectory(package, appValue,
|
|
|
|
sig, subpath, depth - 1);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
FindClose(hFind);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2009-12-22 09:28:03 +00:00
|
|
|
if (*appValue != buf)
|
2008-12-27 15:10:14 +00:00
|
|
|
msi_free(buf);
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
static UINT ACTION_CheckDirectory(MSIPACKAGE *package, LPCWSTR dir,
|
|
|
|
LPWSTR *appValue)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
DWORD attr = GetFileAttributesW(dir);
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
if (attr != INVALID_FILE_ATTRIBUTES && (attr & FILE_ATTRIBUTE_DIRECTORY))
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2006-08-01 23:12:11 +00:00
|
|
|
TRACE("directory exists, returning %s\n", debugstr_w(dir));
|
|
|
|
*appValue = strdupW(dir);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2008-01-16 10:11:22 +00:00
|
|
|
|
|
|
|
return ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static BOOL ACTION_IsFullPath(LPCWSTR path)
|
|
|
|
{
|
|
|
|
WCHAR first = toupperW(path[0]);
|
|
|
|
BOOL ret;
|
|
|
|
|
|
|
|
if (first >= 'A' && first <= 'Z' && path[1] == ':')
|
|
|
|
ret = TRUE;
|
|
|
|
else if (path[0] == '\\' && path[1] == '\\')
|
|
|
|
ret = TRUE;
|
|
|
|
else
|
|
|
|
ret = FALSE;
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
static UINT ACTION_SearchDirectory(MSIPACKAGE *package, MSISIGNATURE *sig,
|
2006-08-01 23:12:11 +00:00
|
|
|
LPCWSTR path, int depth, LPWSTR *appValue)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
UINT rc;
|
2008-12-27 15:10:14 +00:00
|
|
|
DWORD attr;
|
|
|
|
LPWSTR val = NULL;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
TRACE("%p, %p, %s, %d, %p\n", package, sig, debugstr_w(path), depth,
|
|
|
|
appValue);
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
if (ACTION_IsFullPath(path))
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
|
|
|
if (sig->File)
|
2008-12-27 15:10:14 +00:00
|
|
|
rc = ACTION_RecurseSearchDirectory(package, &val, sig, path, depth);
|
2006-02-17 00:04:10 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
/* Recursively searching a directory makes no sense when the
|
|
|
|
* directory to search is the thing you're trying to find.
|
|
|
|
*/
|
2008-12-27 15:10:14 +00:00
|
|
|
rc = ACTION_CheckDirectory(package, path, &val);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
WCHAR pathWithDrive[MAX_PATH] = { 'C',':','\\',0 };
|
|
|
|
DWORD drives = GetLogicalDrives();
|
|
|
|
int i;
|
|
|
|
|
|
|
|
rc = ERROR_SUCCESS;
|
2008-12-27 15:10:14 +00:00
|
|
|
for (i = 0; rc == ERROR_SUCCESS && !val && i < 26; i++)
|
|
|
|
{
|
|
|
|
if (!(drives & (1 << i)))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
pathWithDrive[0] = 'A' + i;
|
|
|
|
if (GetDriveTypeW(pathWithDrive) != DRIVE_FIXED)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
lstrcpynW(pathWithDrive + 3, path,
|
|
|
|
sizeof(pathWithDrive) / sizeof(pathWithDrive[0]) - 3);
|
|
|
|
|
|
|
|
if (sig->File)
|
|
|
|
rc = ACTION_RecurseSearchDirectory(package, &val, sig,
|
|
|
|
pathWithDrive, depth);
|
|
|
|
else
|
|
|
|
rc = ACTION_CheckDirectory(package, pathWithDrive, &val);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
attr = GetFileAttributesW(val);
|
2009-05-20 12:59:23 +00:00
|
|
|
if (attr != INVALID_FILE_ATTRIBUTES &&
|
|
|
|
(attr & FILE_ATTRIBUTE_DIRECTORY) &&
|
2008-12-27 15:10:14 +00:00
|
|
|
val && val[lstrlenW(val) - 1] != '\\')
|
|
|
|
{
|
|
|
|
val = msi_realloc(val, (lstrlenW(val) + 2) * sizeof(WCHAR));
|
|
|
|
if (!val)
|
|
|
|
rc = ERROR_OUTOFMEMORY;
|
|
|
|
else
|
|
|
|
PathAddBackslashW(val);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
*appValue = val;
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
TRACE("returning %d\n", rc);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
static UINT ACTION_AppSearchSigName(MSIPACKAGE *package, LPCWSTR sigName,
|
|
|
|
MSISIGNATURE *sig, LPWSTR *appValue);
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
static UINT ACTION_AppSearchDr(MSIPACKAGE *package, LPWSTR *appValue, MSISIGNATURE *sig)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
static const WCHAR query[] = {
|
|
|
|
's','e','l','e','c','t',' ','*',' ',
|
|
|
|
'f','r','o','m',' ',
|
|
|
|
'D','r','L','o','c','a','t','o','r',' ',
|
|
|
|
'w','h','e','r','e',' ',
|
|
|
|
'S','i','g','n','a','t','u','r','e','_',' ','=',' ', '\'','%','s','\'',0};
|
2010-05-29 08:55:43 +00:00
|
|
|
LPWSTR parent = NULL;
|
|
|
|
LPCWSTR parentName;
|
2008-12-27 15:10:14 +00:00
|
|
|
WCHAR path[MAX_PATH];
|
2008-01-16 10:11:22 +00:00
|
|
|
WCHAR expanded[MAX_PATH];
|
|
|
|
MSIRECORD *row;
|
|
|
|
int depth;
|
2009-05-20 12:59:23 +00:00
|
|
|
DWORD sz, attr;
|
2006-02-17 00:04:10 +00:00
|
|
|
UINT rc;
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
TRACE("%s\n", debugstr_w(sig->Name));
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
*appValue = NULL;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
row = MSI_QueryGetRecord( package->db, query, sig->Name );
|
|
|
|
if (!row)
|
|
|
|
{
|
|
|
|
TRACE("failed to query DrLocator for %s\n", debugstr_w(sig->Name));
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* check whether parent is set */
|
2010-05-29 08:55:43 +00:00
|
|
|
parentName = MSI_RecordGetString(row, 2);
|
2008-01-16 10:11:22 +00:00
|
|
|
if (parentName)
|
|
|
|
{
|
|
|
|
MSISIGNATURE parentSig;
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
ACTION_AppSearchSigName(package, parentName, &parentSig, &parent);
|
2008-01-16 10:11:22 +00:00
|
|
|
ACTION_FreeSignature(&parentSig);
|
2010-05-29 08:55:43 +00:00
|
|
|
if (!parent)
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
{
|
|
|
|
msiobj_release(&row->hdr);
|
2010-05-29 08:55:43 +00:00
|
|
|
return ERROR_SUCCESS;
|
Finish the Wine sync. These components are not just rc file changes
atl, comctl32, comdlg32, dwmapi, fusion, gdiplus, jscript, mpr, mshtml, msi, msimtf, msxml3, ole32, oleaut32, riched20, shdocvw, shlwapi, urlmon, usp10, version and windowscodecs
Seems to build and boot. /me hides
svn path=/trunk/; revision=48273
2010-07-26 02:26:04 +00:00
|
|
|
}
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2008-12-27 15:10:14 +00:00
|
|
|
|
|
|
|
sz = MAX_PATH;
|
|
|
|
MSI_RecordGetStringW(row, 3, path, &sz);
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
if (MSI_RecordIsNull(row,4))
|
|
|
|
depth = 0;
|
2006-02-17 00:04:10 +00:00
|
|
|
else
|
2008-01-16 10:11:22 +00:00
|
|
|
depth = MSI_RecordGetInteger(row,4);
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2009-05-20 12:59:23 +00:00
|
|
|
if (sz)
|
|
|
|
ACTION_ExpandAnyPath(package, path, expanded, MAX_PATH);
|
|
|
|
else
|
|
|
|
strcpyW(expanded, path);
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
if (parent)
|
2006-02-17 00:04:10 +00:00
|
|
|
{
|
2009-05-20 12:59:23 +00:00
|
|
|
attr = GetFileAttributesW(parent);
|
|
|
|
if (attr != INVALID_FILE_ATTRIBUTES &&
|
|
|
|
!(attr & FILE_ATTRIBUTE_DIRECTORY))
|
2008-01-16 10:11:22 +00:00
|
|
|
{
|
2008-12-27 15:10:14 +00:00
|
|
|
PathRemoveFileSpecW(parent);
|
|
|
|
PathAddBackslashW(parent);
|
2008-01-16 10:11:22 +00:00
|
|
|
}
|
2008-12-27 15:10:14 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
strcpyW(path, parent);
|
|
|
|
strcatW(path, expanded);
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
2009-05-20 12:59:23 +00:00
|
|
|
else if (sz)
|
2008-12-27 15:10:14 +00:00
|
|
|
strcpyW(path, expanded);
|
|
|
|
|
|
|
|
PathAddBackslashW(path);
|
2008-01-16 10:11:22 +00:00
|
|
|
|
|
|
|
rc = ACTION_SearchDirectory(package, sig, path, depth, appValue);
|
|
|
|
|
|
|
|
msi_free(parent);
|
|
|
|
msiobj_release(&row->hdr);
|
2006-02-17 00:04:10 +00:00
|
|
|
|
|
|
|
TRACE("returning %d\n", rc);
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2006-08-01 23:12:11 +00:00
|
|
|
static UINT ACTION_AppSearchSigName(MSIPACKAGE *package, LPCWSTR sigName,
|
|
|
|
MSISIGNATURE *sig, LPWSTR *appValue)
|
|
|
|
{
|
|
|
|
UINT rc;
|
|
|
|
|
|
|
|
*appValue = NULL;
|
|
|
|
rc = ACTION_AppSearchGetSignature(package, sig, sigName);
|
|
|
|
if (rc == ERROR_SUCCESS)
|
|
|
|
{
|
|
|
|
rc = ACTION_AppSearchComponents(package, appValue, sig);
|
|
|
|
if (rc == ERROR_SUCCESS && !*appValue)
|
|
|
|
{
|
|
|
|
rc = ACTION_AppSearchReg(package, appValue, sig);
|
|
|
|
if (rc == ERROR_SUCCESS && !*appValue)
|
|
|
|
{
|
|
|
|
rc = ACTION_AppSearchIni(package, appValue, sig);
|
|
|
|
if (rc == ERROR_SUCCESS && !*appValue)
|
|
|
|
rc = ACTION_AppSearchDr(package, appValue, sig);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
static UINT iterate_appsearch(MSIRECORD *row, LPVOID param)
|
|
|
|
{
|
|
|
|
MSIPACKAGE *package = param;
|
2010-03-06 09:05:09 +00:00
|
|
|
LPCWSTR propName, sigName;
|
|
|
|
LPWSTR value = NULL;
|
2008-01-16 10:11:22 +00:00
|
|
|
MSISIGNATURE sig;
|
2010-03-06 09:05:09 +00:00
|
|
|
MSIRECORD *uirow;
|
2008-01-16 10:11:22 +00:00
|
|
|
UINT r;
|
|
|
|
|
|
|
|
/* get property and signature */
|
2010-03-06 09:05:09 +00:00
|
|
|
propName = MSI_RecordGetString(row, 1);
|
|
|
|
sigName = MSI_RecordGetString(row, 2);
|
2008-01-16 10:11:22 +00:00
|
|
|
|
|
|
|
TRACE("%s %s\n", debugstr_w(propName), debugstr_w(sigName));
|
|
|
|
|
|
|
|
r = ACTION_AppSearchSigName(package, sigName, &sig, &value);
|
|
|
|
if (value)
|
|
|
|
{
|
2012-12-09 19:43:59 +00:00
|
|
|
r = msi_set_property( package->db, propName, value, -1 );
|
2012-01-21 17:19:12 +00:00
|
|
|
if (r == ERROR_SUCCESS && !strcmpW( propName, szSourceDir ))
|
2010-05-29 08:55:43 +00:00
|
|
|
msi_reset_folders( package, TRUE );
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
msi_free(value);
|
|
|
|
}
|
|
|
|
ACTION_FreeSignature(&sig);
|
2010-03-06 09:05:09 +00:00
|
|
|
|
|
|
|
uirow = MSI_CreateRecord( 2 );
|
|
|
|
MSI_RecordSetStringW( uirow, 1, propName );
|
|
|
|
MSI_RecordSetStringW( uirow, 2, sigName );
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_ui_actiondata( package, szAppSearch, uirow );
|
2010-03-06 09:05:09 +00:00
|
|
|
msiobj_release( &uirow->hdr );
|
2008-01-16 10:11:22 +00:00
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
2006-02-17 00:04:10 +00:00
|
|
|
UINT ACTION_AppSearch(MSIPACKAGE *package)
|
|
|
|
{
|
2008-01-16 10:11:22 +00:00
|
|
|
static const WCHAR query[] = {
|
2012-01-21 17:19:12 +00:00
|
|
|
'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
2008-01-16 10:11:22 +00:00
|
|
|
'A','p','p','S','e','a','r','c','h',0};
|
2012-01-21 17:19:12 +00:00
|
|
|
MSIQUERY *view;
|
2008-01-16 10:11:22 +00:00
|
|
|
UINT r;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
if (msi_action_is_unique(package, szAppSearch))
|
2010-05-29 08:55:43 +00:00
|
|
|
{
|
|
|
|
TRACE("Skipping AppSearch action: already done in UI sequence\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
else
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_register_unique_action(package, szAppSearch);
|
2010-05-29 08:55:43 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
r = MSI_OpenQuery( package->db, &view, query );
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
r = MSI_IterateRecords( view, NULL, iterate_appsearch, package );
|
|
|
|
msiobj_release( &view->hdr );
|
|
|
|
return r;
|
|
|
|
}
|
2006-08-01 23:12:11 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
static UINT ITERATE_CCPSearch(MSIRECORD *row, LPVOID param)
|
|
|
|
{
|
|
|
|
MSIPACKAGE *package = param;
|
|
|
|
LPCWSTR signature;
|
|
|
|
LPWSTR value = NULL;
|
|
|
|
MSISIGNATURE sig;
|
|
|
|
UINT r = ERROR_SUCCESS;
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
static const WCHAR success[] = {'C','C','P','_','S','u','c','c','e','s','s',0};
|
2006-08-01 23:12:11 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
signature = MSI_RecordGetString(row, 1);
|
2006-08-01 23:12:11 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
TRACE("%s\n", debugstr_w(signature));
|
2006-02-17 00:04:10 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
ACTION_AppSearchSigName(package, signature, &sig, &value);
|
|
|
|
if (value)
|
|
|
|
{
|
|
|
|
TRACE("Found signature %s\n", debugstr_w(signature));
|
2012-12-09 19:43:59 +00:00
|
|
|
msi_set_property( package->db, success, szOne, -1 );
|
2008-01-16 10:11:22 +00:00
|
|
|
msi_free(value);
|
|
|
|
r = ERROR_NO_MORE_ITEMS;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
ACTION_FreeSignature(&sig);
|
|
|
|
|
|
|
|
return r;
|
|
|
|
}
|
|
|
|
|
|
|
|
UINT ACTION_CCPSearch(MSIPACKAGE *package)
|
|
|
|
{
|
|
|
|
static const WCHAR query[] = {
|
2012-01-21 17:19:12 +00:00
|
|
|
'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
2008-01-16 10:11:22 +00:00
|
|
|
'C','C','P','S','e','a','r','c','h',0};
|
2012-01-21 17:19:12 +00:00
|
|
|
MSIQUERY *view;
|
2008-01-16 10:11:22 +00:00
|
|
|
UINT r;
|
|
|
|
|
2012-01-21 17:19:12 +00:00
|
|
|
if (msi_action_is_unique(package, szCCPSearch))
|
2010-05-29 08:55:43 +00:00
|
|
|
{
|
|
|
|
TRACE("Skipping AppSearch action: already done in UI sequence\n");
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
}
|
|
|
|
else
|
2012-01-21 17:19:12 +00:00
|
|
|
msi_register_unique_action(package, szCCPSearch);
|
2010-05-29 08:55:43 +00:00
|
|
|
|
2008-01-16 10:11:22 +00:00
|
|
|
r = MSI_OpenQuery(package->db, &view, query);
|
|
|
|
if (r != ERROR_SUCCESS)
|
|
|
|
return ERROR_SUCCESS;
|
|
|
|
|
|
|
|
r = MSI_IterateRecords(view, NULL, ITERATE_CCPSearch, package);
|
|
|
|
msiobj_release(&view->hdr);
|
|
|
|
return r;
|
2006-02-17 00:04:10 +00:00
|
|
|
}
|