msi: Add initial implementation of MsiPublishAssemblies.
This commit is contained in:
parent
e487b1e213
commit
bfe07d1d07
|
@ -33,6 +33,10 @@
|
|||
#include "msipriv.h"
|
||||
#include "winuser.h"
|
||||
#include "shlobj.h"
|
||||
#include "objbase.h"
|
||||
#include "mscoree.h"
|
||||
#include "fusion.h"
|
||||
#include "shlwapi.h"
|
||||
#include "wine/unicode.h"
|
||||
#include "winver.h"
|
||||
|
||||
|
@ -1246,6 +1250,9 @@ static MSIFEATURE *find_feature_by_name( MSIPACKAGE *package, LPCWSTR name )
|
|||
{
|
||||
MSIFEATURE *feature;
|
||||
|
||||
if ( !name )
|
||||
return NULL;
|
||||
|
||||
LIST_FOR_EACH_ENTRY( feature, &package->features, MSIFEATURE, entry )
|
||||
{
|
||||
if ( !lstrcmpW( feature->Feature, name ) )
|
||||
|
@ -5542,6 +5549,154 @@ static UINT ACTION_MoveFiles( MSIPACKAGE *package )
|
|||
return rc;
|
||||
}
|
||||
|
||||
static HRESULT (WINAPI *pCreateAssemblyCache)(IAssemblyCache **ppAsmCache,
|
||||
DWORD dwReserved);
|
||||
static HRESULT (WINAPI *pLoadLibraryShim)(LPCWSTR szDllName, LPCWSTR szVersion,
|
||||
LPVOID pvReserved, HMODULE *phModDll);
|
||||
|
||||
static BOOL init_functionpointers(void)
|
||||
{
|
||||
HRESULT hr;
|
||||
HMODULE hfusion;
|
||||
HMODULE hmscoree;
|
||||
|
||||
static const WCHAR szFusion[] = {'f','u','s','i','o','n','.','d','l','l',0};
|
||||
|
||||
hmscoree = LoadLibraryA("mscoree.dll");
|
||||
if (!hmscoree)
|
||||
{
|
||||
WARN("mscoree.dll not available\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pLoadLibraryShim = (void *)GetProcAddress(hmscoree, "LoadLibraryShim");
|
||||
if (!pLoadLibraryShim)
|
||||
{
|
||||
WARN("LoadLibraryShim not available\n");
|
||||
FreeLibrary(hmscoree);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
hr = pLoadLibraryShim(szFusion, NULL, NULL, &hfusion);
|
||||
if (FAILED(hr))
|
||||
{
|
||||
WARN("fusion.dll not available\n");
|
||||
FreeLibrary(hmscoree);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
pCreateAssemblyCache = (void *)GetProcAddress(hfusion, "CreateAssemblyCache");
|
||||
|
||||
FreeLibrary(hmscoree);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT install_assembly(LPWSTR path)
|
||||
{
|
||||
IAssemblyCache *cache;
|
||||
HRESULT hr;
|
||||
UINT r = ERROR_FUNCTION_FAILED;
|
||||
|
||||
if (!init_functionpointers() || !pCreateAssemblyCache)
|
||||
return ERROR_FUNCTION_FAILED;
|
||||
|
||||
hr = pCreateAssemblyCache(&cache, 0);
|
||||
if (FAILED(hr))
|
||||
goto done;
|
||||
|
||||
hr = IAssemblyCache_InstallAssembly(cache, 0, path, NULL);
|
||||
if (FAILED(hr))
|
||||
ERR("Failed to install assembly: %s %08x\n", debugstr_w(path), hr);
|
||||
|
||||
r = ERROR_SUCCESS;
|
||||
|
||||
done:
|
||||
IAssemblyCache_Release(cache);
|
||||
return r;
|
||||
}
|
||||
|
||||
static UINT ITERATE_PublishAssembly( MSIRECORD *rec, LPVOID param )
|
||||
{
|
||||
MSIPACKAGE *package = param;
|
||||
MSICOMPONENT *comp;
|
||||
MSIFEATURE *feature;
|
||||
MSIFILE *file;
|
||||
WCHAR path[MAX_PATH];
|
||||
LPCWSTR app;
|
||||
DWORD attr;
|
||||
UINT r;
|
||||
|
||||
comp = get_loaded_component(package, MSI_RecordGetString(rec, 1));
|
||||
if (!comp || !comp->Enabled ||
|
||||
!(comp->Action & (INSTALLSTATE_LOCAL | INSTALLSTATE_SOURCE)))
|
||||
{
|
||||
ERR("Component not set for install, not publishing assembly\n");
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
feature = find_feature_by_name(package, MSI_RecordGetString(rec, 2));
|
||||
if (feature)
|
||||
msi_feature_set_state(feature, INSTALLSTATE_LOCAL);
|
||||
|
||||
if (MSI_RecordGetString(rec, 3))
|
||||
FIXME("Manifest unhandled\n");
|
||||
|
||||
app = MSI_RecordGetString(rec, 4);
|
||||
if (app)
|
||||
{
|
||||
FIXME("Assembly should be privately installed\n");
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
attr = MSI_RecordGetInteger(rec, 5);
|
||||
if (attr == msidbAssemblyAttributesWin32)
|
||||
{
|
||||
FIXME("Win32 assemblies not handled\n");
|
||||
return ERROR_SUCCESS;
|
||||
}
|
||||
|
||||
/* FIXME: extract all files belonging to this component */
|
||||
file = msi_find_file(package, comp->KeyPath);
|
||||
|
||||
GetTempPathW(MAX_PATH, path);
|
||||
r = msi_extract_file(package, file, path);
|
||||
if (r != ERROR_SUCCESS)
|
||||
{
|
||||
ERR("Failed to extract temporary assembly\n");
|
||||
return r;
|
||||
}
|
||||
|
||||
PathAddBackslashW(path);
|
||||
lstrcatW(path, file->FileName);
|
||||
|
||||
r = install_assembly(path);
|
||||
if (r != ERROR_SUCCESS)
|
||||
ERR("Failed to install assembly\n");
|
||||
|
||||
/* FIXME: write Installer assembly reg values */
|
||||
|
||||
return r;
|
||||
}
|
||||
|
||||
static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
|
||||
{
|
||||
UINT rc;
|
||||
MSIQUERY *view;
|
||||
|
||||
static const WCHAR ExecSeqQuery[] =
|
||||
{'S','E','L','E','C','T',' ','*',' ','F','R','O','M',' ',
|
||||
'`','M','s','i','A','s','s','e','m','b','l','y','`',0};
|
||||
|
||||
rc = MSI_DatabaseOpenViewW(package->db, ExecSeqQuery, &view);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
rc = MSI_IterateRecords(view, NULL, ITERATE_PublishAssembly, package);
|
||||
msiobj_release(&view->hdr);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static UINT msi_unimplemented_action_stub( MSIPACKAGE *package,
|
||||
LPCSTR action, LPCWSTR table )
|
||||
{
|
||||
|
@ -5630,13 +5785,6 @@ static UINT ACTION_RemoveEnvironmentStrings( MSIPACKAGE *package )
|
|||
return msi_unimplemented_action_stub( package, "RemoveEnvironmentStrings", table );
|
||||
}
|
||||
|
||||
static UINT ACTION_MsiPublishAssemblies( MSIPACKAGE *package )
|
||||
{
|
||||
static const WCHAR table[] = {
|
||||
'M','s','i','A','s','s','e','m','b','l','y',0 };
|
||||
return msi_unimplemented_action_stub( package, "MsiPublishAssemblies", table );
|
||||
}
|
||||
|
||||
static UINT ACTION_MsiUnpublishAssemblies( MSIPACKAGE *package )
|
||||
{
|
||||
static const WCHAR table[] = {
|
||||
|
|
|
@ -57,20 +57,7 @@ extern const WCHAR szRemoveFiles[];
|
|||
|
||||
static const WCHAR cszTempFolder[]= {'T','e','m','p','F','o','l','d','e','r',0};
|
||||
|
||||
struct media_info {
|
||||
UINT disk_id;
|
||||
UINT type;
|
||||
UINT last_sequence;
|
||||
LPWSTR disk_prompt;
|
||||
LPWSTR cabinet;
|
||||
LPWSTR first_volume;
|
||||
LPWSTR volume_label;
|
||||
BOOL is_continuous;
|
||||
BOOL is_extracted;
|
||||
WCHAR source[MAX_PATH];
|
||||
};
|
||||
|
||||
static BOOL source_matches_volume(struct media_info *mi, LPWSTR source_root)
|
||||
static BOOL source_matches_volume(MSIMEDIAINFO *mi, LPWSTR source_root)
|
||||
{
|
||||
WCHAR volume_name[MAX_PATH + 1];
|
||||
|
||||
|
@ -84,7 +71,7 @@ static BOOL source_matches_volume(struct media_info *mi, LPWSTR source_root)
|
|||
return !lstrcmpW(mi->volume_label, volume_name);
|
||||
}
|
||||
|
||||
static UINT msi_change_media( MSIPACKAGE *package, struct media_info *mi )
|
||||
static UINT msi_change_media( MSIPACKAGE *package, MSIMEDIAINFO *mi )
|
||||
{
|
||||
LPSTR msg;
|
||||
LPWSTR error, error_dialog;
|
||||
|
@ -169,7 +156,7 @@ end:
|
|||
typedef struct
|
||||
{
|
||||
MSIPACKAGE* package;
|
||||
struct media_info *mi;
|
||||
MSIMEDIAINFO *mi;
|
||||
} CabData;
|
||||
|
||||
static void * cabinet_alloc(ULONG cb)
|
||||
|
@ -265,7 +252,7 @@ static void msi_file_update_ui( MSIPACKAGE *package, MSIFILE *f, const WCHAR *ac
|
|||
ui_progress( package, 2, f->FileSize, 0, 0);
|
||||
}
|
||||
|
||||
static UINT msi_media_get_disk_info( MSIPACKAGE *package, struct media_info *mi )
|
||||
static UINT msi_media_get_disk_info( MSIPACKAGE *package, MSIMEDIAINFO *mi )
|
||||
{
|
||||
MSIRECORD *row;
|
||||
LPWSTR ptr;
|
||||
|
@ -311,7 +298,7 @@ static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
|
|||
case fdintNEXT_CABINET:
|
||||
{
|
||||
CabData *data = (CabData *)pfdin->pv;
|
||||
struct media_info *mi = data->mi;
|
||||
MSIMEDIAINFO *mi = data->mi;
|
||||
LPWSTR cab = strdupAtoW(pfdin->psz1);
|
||||
UINT rc;
|
||||
|
||||
|
@ -423,18 +410,18 @@ static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
|
|||
}
|
||||
|
||||
/***********************************************************************
|
||||
* extract_cabinet_file
|
||||
* msi_cabextract
|
||||
*
|
||||
* Extract files from a cab file.
|
||||
*/
|
||||
static BOOL extract_cabinet_file(MSIPACKAGE* package, struct media_info *mi)
|
||||
BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi,
|
||||
PFNFDINOTIFY notify, LPVOID data)
|
||||
{
|
||||
LPSTR cabinet, cab_path = NULL;
|
||||
LPWSTR ptr;
|
||||
HFDI hfdi;
|
||||
ERF erf;
|
||||
BOOL ret = FALSE;
|
||||
CabData data;
|
||||
|
||||
TRACE("Extracting %s\n", debugstr_w(mi->source));
|
||||
|
||||
|
@ -457,10 +444,7 @@ static BOOL extract_cabinet_file(MSIPACKAGE* package, struct media_info *mi)
|
|||
|
||||
cab_path[ptr - mi->source] = '\0';
|
||||
|
||||
data.package = package;
|
||||
data.mi = mi;
|
||||
|
||||
ret = FDICopy(hfdi, cabinet, cab_path, 0, cabinet_notify, NULL, &data);
|
||||
ret = FDICopy(hfdi, cabinet, cab_path, 0, notify, NULL, data);
|
||||
if (!ret)
|
||||
ERR("FDICopy failed\n");
|
||||
|
||||
|
@ -495,7 +479,7 @@ static VOID set_file_source(MSIPACKAGE* package, MSIFILE* file, LPCWSTR path)
|
|||
file->SourcePath = build_directory_name(2, path, file->File);
|
||||
}
|
||||
|
||||
static void free_media_info( struct media_info *mi )
|
||||
void msi_free_media_info( MSIMEDIAINFO *mi )
|
||||
{
|
||||
msi_free( mi->disk_prompt );
|
||||
msi_free( mi->cabinet );
|
||||
|
@ -504,7 +488,7 @@ static void free_media_info( struct media_info *mi )
|
|||
msi_free( mi );
|
||||
}
|
||||
|
||||
static UINT load_media_info(MSIPACKAGE *package, MSIFILE *file, struct media_info *mi)
|
||||
UINT msi_load_media_info(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi)
|
||||
{
|
||||
MSIRECORD *row;
|
||||
LPWSTR source_dir;
|
||||
|
@ -593,7 +577,7 @@ static UINT load_media_info(MSIPACKAGE *package, MSIFILE *file, struct media_inf
|
|||
}
|
||||
|
||||
/* FIXME: search NETWORK and URL sources as well */
|
||||
static UINT find_published_source(MSIPACKAGE *package, struct media_info *mi)
|
||||
static UINT find_published_source(MSIPACKAGE *package, MSIMEDIAINFO *mi)
|
||||
{
|
||||
WCHAR source[MAX_PATH];
|
||||
WCHAR volume[MAX_PATH];
|
||||
|
@ -634,7 +618,7 @@ static UINT find_published_source(MSIPACKAGE *package, struct media_info *mi)
|
|||
return ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
static UINT ready_media(MSIPACKAGE *package, MSIFILE *file, struct media_info *mi)
|
||||
static UINT ready_media(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi)
|
||||
{
|
||||
UINT rc = ERROR_SUCCESS;
|
||||
|
||||
|
@ -642,7 +626,7 @@ static UINT ready_media(MSIPACKAGE *package, MSIFILE *file, struct media_info *m
|
|||
if (mi->is_continuous)
|
||||
return ERROR_SUCCESS;
|
||||
|
||||
rc = load_media_info(package, file, mi);
|
||||
rc = msi_load_media_info(package, file, mi);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
ERR("Unable to load media info\n");
|
||||
|
@ -808,7 +792,7 @@ static BOOL check_dest_hash_matches(MSIFILE *file)
|
|||
*/
|
||||
UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
||||
{
|
||||
struct media_info *mi;
|
||||
MSIMEDIAINFO *mi;
|
||||
UINT rc = ERROR_SUCCESS;
|
||||
MSIFILE *file;
|
||||
|
||||
|
@ -825,7 +809,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
|||
*/
|
||||
msi_create_component_directories( package );
|
||||
|
||||
mi = msi_alloc_zero( sizeof(struct media_info) );
|
||||
mi = msi_alloc_zero( sizeof(MSIMEDIAINFO) );
|
||||
|
||||
LIST_FOR_EACH_ENTRY( file, &package->files, MSIFILE, entry )
|
||||
{
|
||||
|
@ -841,6 +825,8 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
|||
if (file->Sequence > mi->last_sequence || mi->is_continuous ||
|
||||
(file->IsCompressed && !mi->is_extracted))
|
||||
{
|
||||
CabData data;
|
||||
|
||||
rc = ready_media(package, file, mi);
|
||||
if (rc != ERROR_SUCCESS)
|
||||
{
|
||||
|
@ -848,7 +834,11 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
|||
break;
|
||||
}
|
||||
|
||||
if (file->IsCompressed && !extract_cabinet_file(package, mi))
|
||||
data.mi = mi;
|
||||
data.package = package;
|
||||
|
||||
if (file->IsCompressed &&
|
||||
!msi_cabextract(package, mi, cabinet_notify, &data))
|
||||
{
|
||||
ERR("Failed to extract cabinet: %s\n", debugstr_w(mi->cabinet));
|
||||
rc = ERROR_FUNCTION_FAILED;
|
||||
|
@ -881,7 +871,7 @@ UINT ACTION_InstallFiles(MSIPACKAGE *package)
|
|||
}
|
||||
}
|
||||
|
||||
free_media_info( mi );
|
||||
msi_free_media_info( mi );
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
#include "wine/debug.h"
|
||||
#include "msipriv.h"
|
||||
#include "winuser.h"
|
||||
#include "winreg.h"
|
||||
#include "shlwapi.h"
|
||||
#include "wine/unicode.h"
|
||||
#include "msidefs.h"
|
||||
|
||||
|
@ -1054,3 +1056,122 @@ void msi_ui_error( DWORD msg_id, DWORD type )
|
|||
|
||||
MessageBoxW( NULL, text, title, type );
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MSIPACKAGE *package;
|
||||
MSIMEDIAINFO *mi;
|
||||
MSIFILE *file;
|
||||
LPWSTR destination;
|
||||
} CabData;
|
||||
|
||||
static INT_PTR cabinet_notify(FDINOTIFICATIONTYPE fdint, PFDINOTIFICATION pfdin)
|
||||
{
|
||||
TRACE("(%d)\n", fdint);
|
||||
|
||||
switch (fdint)
|
||||
{
|
||||
case fdintNEXT_CABINET:
|
||||
{
|
||||
ERR("continuous cabinets not handled\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
case fdintCOPY_FILE:
|
||||
{
|
||||
CabData *data = (CabData*) pfdin->pv;
|
||||
LPWSTR file, path;
|
||||
DWORD attrs, size;
|
||||
HANDLE handle;
|
||||
MSIFILE *f;
|
||||
|
||||
file = strdupAtoW(pfdin->psz1);
|
||||
f = get_loaded_file(data->package, file);
|
||||
msi_free(file);
|
||||
|
||||
if (!f)
|
||||
{
|
||||
WARN("unknown file in cabinet (%s)\n",debugstr_a(pfdin->psz1));
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (lstrcmpW(f->File, data->file->File))
|
||||
return 0;
|
||||
|
||||
size = lstrlenW(data->destination) + lstrlenW(file) + 2;
|
||||
path = msi_alloc(size * sizeof(WCHAR));
|
||||
lstrcpyW(path, data->destination);
|
||||
PathAddBackslashW(path);
|
||||
lstrcatW(path, data->file->FileName);
|
||||
|
||||
TRACE("extracting %s\n", debugstr_w(path));
|
||||
|
||||
attrs = f->Attributes & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_HIDDEN|FILE_ATTRIBUTE_SYSTEM);
|
||||
if (!attrs) attrs = FILE_ATTRIBUTE_NORMAL;
|
||||
|
||||
handle = CreateFileW(path, GENERIC_READ | GENERIC_WRITE, 0,
|
||||
NULL, CREATE_ALWAYS, attrs, NULL);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
if (GetFileAttributesW(path) == INVALID_FILE_ATTRIBUTES)
|
||||
ERR("failed to create %s (error %d)\n",
|
||||
debugstr_w(path), GetLastError());
|
||||
|
||||
msi_free(path);
|
||||
return 0;
|
||||
}
|
||||
|
||||
msi_free(path);
|
||||
return (INT_PTR)handle;
|
||||
}
|
||||
|
||||
case fdintCLOSE_FILE_INFO:
|
||||
{
|
||||
FILETIME ft;
|
||||
FILETIME ftLocal;
|
||||
HANDLE handle = (HANDLE)pfdin->hf;
|
||||
|
||||
if (!DosDateTimeToFileTime(pfdin->date, pfdin->time, &ft))
|
||||
return -1;
|
||||
if (!LocalFileTimeToFileTime(&ft, &ftLocal))
|
||||
return -1;
|
||||
if (!SetFileTime(handle, &ftLocal, 0, &ftLocal))
|
||||
return -1;
|
||||
CloseHandle(handle);
|
||||
return 1;
|
||||
}
|
||||
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
UINT msi_extract_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR destdir)
|
||||
{
|
||||
MSIMEDIAINFO *mi;
|
||||
CabData data;
|
||||
UINT r;
|
||||
|
||||
mi = msi_alloc_zero(sizeof(MSIMEDIAINFO));
|
||||
if (!mi)
|
||||
return ERROR_OUTOFMEMORY;
|
||||
|
||||
r = msi_load_media_info(package, file, mi);
|
||||
if (r != ERROR_SUCCESS)
|
||||
goto done;
|
||||
|
||||
data.package = package;
|
||||
data.mi = mi;
|
||||
data.file = file;
|
||||
data.destination = destdir;
|
||||
|
||||
if (!msi_cabextract(package, mi, cabinet_notify, &data))
|
||||
{
|
||||
ERR("Failed to extract cabinet file\n");
|
||||
r = ERROR_FUNCTION_FAILED;
|
||||
}
|
||||
|
||||
done:
|
||||
msi_free_media_info(mi);
|
||||
return r;
|
||||
}
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "windef.h"
|
||||
#include "winbase.h"
|
||||
#include "fdi.h"
|
||||
#include "msi.h"
|
||||
#include "msiquery.h"
|
||||
#include "objbase.h"
|
||||
|
@ -133,6 +134,20 @@ typedef struct tagMSIMEDIADISK
|
|||
LPWSTR disk_prompt;
|
||||
} MSIMEDIADISK;
|
||||
|
||||
typedef struct tagMSIMEDIAINFO
|
||||
{
|
||||
UINT disk_id;
|
||||
UINT type;
|
||||
UINT last_sequence;
|
||||
LPWSTR disk_prompt;
|
||||
LPWSTR cabinet;
|
||||
LPWSTR first_volume;
|
||||
LPWSTR volume_label;
|
||||
BOOL is_continuous;
|
||||
BOOL is_extracted;
|
||||
WCHAR source[MAX_PATH];
|
||||
} MSIMEDIAINFO;
|
||||
|
||||
typedef struct _column_info
|
||||
{
|
||||
LPCWSTR table;
|
||||
|
@ -889,6 +904,10 @@ extern UINT msi_create_component_directories( MSIPACKAGE *package );
|
|||
extern void msi_ui_error( DWORD msg_id, DWORD type );
|
||||
extern UINT msi_set_last_used_source(LPCWSTR product, LPCWSTR usersid,
|
||||
MSIINSTALLCONTEXT context, DWORD options, LPCWSTR value);
|
||||
extern UINT msi_load_media_info(MSIPACKAGE *package, MSIFILE *file, MSIMEDIAINFO *mi);
|
||||
extern void msi_free_media_info(MSIMEDIAINFO *mi);
|
||||
extern BOOL msi_cabextract(MSIPACKAGE* package, MSIMEDIAINFO *mi, PFNFDINOTIFY notify, LPVOID data);
|
||||
extern UINT msi_extract_file(MSIPACKAGE *package, MSIFILE *file, LPWSTR destdir);
|
||||
|
||||
/* control event stuff */
|
||||
extern VOID ControlEvent_FireSubscribedEvent(MSIPACKAGE *package, LPCWSTR event,
|
||||
|
|
|
@ -201,6 +201,12 @@ enum msidbMoveFileOptions
|
|||
msidbMoveFileOptionsMove = 0x00000001,
|
||||
};
|
||||
|
||||
enum msidbAssemblyAttributes
|
||||
{
|
||||
msidbAssemblyAttributesURT = 0x00000000,
|
||||
msidbAssemblyAttributesWin32 = 0x00000001,
|
||||
};
|
||||
|
||||
/*
|
||||
* Windows SDK braindamage alert
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue