2013-05-30 2 views
4

У меня есть настроенное приложение настройки, которое устанавливает несколько файлов MSI. сегодня я попытался реализовать внешний пользовательский интерфейс, чтобы реализовать свой собственный индикатор выполнения, используя статью this. все выглядит нормально (progressbar получает данные и обновления), но примерно через 60% при запуске обновления компонентов я получаю исключение: «объект не установлен ...» и копание далее получил: _COMPlusExceptionCode «-532462766»MsiInstallProduct() запускает msiexe.exe, но в 32-битном режиме?

проверил монитор процесса и вдруг понял, что msiexec работает в 32-битном режиме.

смешно, когда вы вызываете msiexe напрямую, он начинается с 64 бит, но с использованием метода MsiInstallProduct() он начинается с 32 бит.

Я считаю, что исключение возникает, когда msiexec пытается настроить разделы реестра, а файлы MSI - на 64 бит.

любая помощь оценена.

Приветствия, Afshin

Update 1: включен журнал, используя MsiEnableLog и эта ошибка появилась:

«MSI (с) (94: F8) [07:50:29 : 395]: Внутреннее исключение во время операции установки: 0xc0000005 при 0x000007FE9827F768. "

Обновление 2: рытья дополнительно на основе предложению @marceln, используемый Process Monitor и заметил, есть два MSIexec процессов в памяти. один в 64-битном режиме, который находится в сеансе 0, а другой начинается с первого, когда я вызываю MsiInstallProduct. второй начинается с 'c: \ windows \ syswow64 \ msiexec.exe', который является 32-разрядной версией. Я попытался установить путь поиска, используя SetDllDirectory, но все равно получая тот же результат.

Update 3: основной процесс, безусловно, работает в 64-битном режиме: как prccess монитор доказывает это и результат команды PowerShell:

[reflection.assemblyname]::GetAssemblyName("setup.exe") | fl 

Name     : Setup 
Version    : 5.0.0.0 
CultureInfo   : 
CultureName   : 
CodeBase    : file:///...../Setup.exe 
EscapedCodeBase  : file:///Setup.exe 
ProcessorArchitecture : **MSIL** 
ContentType   : Default 
Flags     : None 
HashAlgorithm   : SHA1 
VersionCompatibility : SameMachine 
KeyPair    : 
FullName    : Setup, Version=5.0.0.0, Culture=neutral, PublicKeyToken=null 

Update 4: Я использую этот метод импорта msi.dll:

[DllImport("msi.dll", CharSet = CharSet.Auto, SetLastError = true)] 
internal static extern int MsiInstallProduct(string packagePath, string commandLine); 

Update 5: Пробовал проводник процесса, приложение - 64 бит, файл MSI.DLL под приложением запускается из system32. но процесс msiexec.exe по-прежнему работает от syswow64, который составляет 32 бит. Файл msi построен как 64-битный msi.

Update 6: Я только что узнал, что эта линия является источником проблемы:

oldHandler = MSIIntrop.MsiSetExternalUI(
    new MSIIntrop.InstallUIHandler(OnExternalUI), 
    32735, 
    IntPtr.Zero); 

Update 7 [FINAL UPDATE]: Для кого это может касаться: После нескольких часов и часов тратить время, я, наконец, удалось преодолеть эту проблему. похоже, что в MSI API есть какая-то внутренняя утечка памяти, которая приводит к сбою внешнего обработчика пользовательского интерфейса в совершенно случайном поведении. Чтобы исправить эту проблему, я реализовал интерфейс IDisposable и попытался использовать класс в блоке «using», чтобы убедиться, что класс полностью удален. MsiSetExternalUI() и MsiInstallProduct() теперь безопасно вызываются внутри этого блока. не забудьте вызвать MsiSetExternalUI(), чтобы вернуть интерфейс в его исходное состояние:

IntPtr prevWindow = IntPtr.Zero; 
MSIIntrop.INSTALLUILEVEL prevUILevel = MSIIntrop.MsiSetInternalUI(MSIIntrop.INSTALLUILEVEL.INSTALLUILEVEL_NONE, ref prevWindow); 

using (MSIContext context = new MSIContext(progressChanged, messageRaised)) 
{ 
    MSIIntrop.INSTALLUI_HANDLER prevHandlre = MSIIntrop.MsiSetExternalUI(context.Handler, 
     MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_FATALEXIT | 
     MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_ERROR | 
     MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_WARNING | 
     MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_ACTIONDATA | 
     MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_PROGRESS, 
     IntPtr.Zero); 
    try 
    { 
     int ret = MSIIntrop.MsiInstallProduct(runningPath, commandLine); 
    } 
    catch (Exception ex) 
    { 
     messageRaised("Error: " + ex.Message); 
    } 
    finally 
    { 
     MSIIntrop.MsiSetExternalUI(prevHandlre, 0, IntPtr.Zero); 
    } 
} 

PS 1: Я have't поставить это в ответ, так как я не% 100 уверен, что это был источник ошибки. Это всего лишь обходной путь. PS 2: Спасибо Марселю и Иаков :)

+0

Тогда ваш хост-процесс должен быть 32 бит (тот, который называет 'MsiInstallProduct'). Вы проверили это? –

+0

Да @marceln, у меня есть! хост-процесс - 64 бит, как я уже упоминал, если я запускаю msiexec.exe с использованием класса Process и передаю файл MSI в качестве аргумента, он запускается в режиме 64 бит. но с использованием MSI API и MsiInstallProduct начинается с 32 бит – Afshin

+0

Затем используйте Dependency Walker, чтобы проверить, какая версия msi.dll (и его расположение) используется msiexec.exe. Затем убедитесь, что вы используете один и тот же из управляемого процесса. Кроме того, убедитесь, что ваш управляемый процесс построен с целью «Любой ЦП». –

ответ

0

Я думаю, что после двух лет коды работает без каких-либо проблем, было бы правильно, чтобы завершить рабочий процесс, предложенный в Update-выше является ответом на вопрос:

похоже, что в MSI API есть некоторая утечка управления внутренней памятью, что приводит к сбою внешнего обработчика пользовательского интерфейса в абсолютно случайном поведении. Чтобы исправить эту проблему, я реализовал интерфейс IDisposable и попытался использовать класс в блоке «using», чтобы убедиться, что класс полностью удален. MsiSetExternalUI() и MsiInstallProduct() теперь безопасно вызываются внутри этого блока. не забудьте вызвать MsiSetExternalUI(), чтобы вернуть пользовательский интерфейс в исходное состояние

0

Причина - сборщик мусора. Пожалуйста, попробуйте использовать метод GC.KeepAlive(), чтобы сборщик мусора не собирал внешний обработчик пользовательского интерфейса.

Например:

// create ui handler 
MSIIntrop.UIHandlerDelegate externalUIHandler = new MSIIntrop.UIHandlerDelegate(context.Handler); 

// execute MsiSetExternalUI with this handler 
MSIIntrop.INSTALLUI_HANDLER prevHandlre = MSIIntrop.MsiSetExternalUI(externalUIHandler, 
    MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_FATALEXIT | 
    MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_ERROR | 
    MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_WARNING | 
    MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_ACTIONDATA | 
    MSIIntrop.INSTALLLOGMODE.INSTALLLOGMODE_PROGRESS, 
    IntPtr.Zero); 

// install product 
int ret = MSIIntrop.MsiInstallProduct(runningPath, commandLine); 

// restore the previous ui handler 
MSIIntrop.MsiSetExternalUI(prevHandlre, 0, IntPtr.Zero); 

// prevent GC from collecting ExternalUIHandler during the product installation 
GC.KeepAlive(externalUIHandler); 

 Смежные вопросы

  • Нет связанных вопросов^_^