Подтвердить что ты не робот

Приложение ClickOnce не запускается через Process.Start( "x.abc" ) с *.abc, связанным с приложением ClickOnce

Я успешно разработал и развернул приложение ClickOnce, которое регистрирует связанное расширение файла, например *.abc. Когда я нажимаю на файл с именем x.abc или, если я набираю x.abc из командной строки, запускается приложение ClickOnce, и я могу получить файл через выделенный API. Я также могу запустить приложение программно со следующим кодом:

System.Diagnostics.Process.Start ("x.abc");

Все отлично работает на моей Windows Vista 64 бит.

Однако, если я попытаюсь сделать то же самое на Windows 7 (также 64 бит), у меня возникла очень странная проблема. Вот что я наблюдаю:

  • Ручной запуск x.abc путем двойного щелчка по нему из проводника.
  • Ручной запуск x.abc из командной строки работает.
  • Process.Start("x.abc") не запускает приложение; однако возвращенный объект процесса показывает, что не было ошибки и что приложение ClickOnce каким-то образом вышло немедленно. Но даже Trace в самом начале приложения ClickOnce никогда не достигается.
  • Еще страннее, Process.Start("x.bat") с файлом x.bat, содержащим единственную строку x.abc, не запускает приложение ClickOnce! Тот же x.bat запущен из работ проводника (конечно).

Попытка проанализировать, что происходит с ProcMon, была не очень полезной, так как процесс ClickOnce запуска приложения очень сложен с моей точки зрения. Я наблюдаю rundll32, чтобы работать, но никаких доказательств каких-либо сбоев.

Программа, которая выполняет Process.Start, представляет собой полное консольное приложение доверия, которое действительно ничего не представляет.

Я не вижу, что изменилось в отношении того, как приложения ClickOnce обрабатываются в Windows 7 и почему Process.Start не будет делать то же самое, что и запуск файла из Проводника. Стоит отметить, что использование более продвинутых версий метода Start с ProcessStartInfo и установка UseShellExecute в true тоже не помогли.

Запуск cmd с помощью Process.Start, а затем попытка запуска x.abc показывает точно такую ​​же проблему. Если я сравниваю параметры среды с cmd, запущенными вручную, я вижу различия в том, как ProgramFiles определен (первый указывает на C:\Program Files (x86), а второй указывает на C:\Program Files). Приложения, запущенные с моего приложения .NET, запускаются на 32-битном уровне эмуляции (SysWoW64).

Я смог воспроизвести сбой запуска x.abc, запустив 32-битную версию командной строки (т.е. %windir%\SysWoW64\cmd.exe) и затем набрав x.abc в приглашении. Я также нашел уродливое обходное решение, которое должно запустить 64-битную командную строку из 32-битной среды, запустив %windir%\Sysnative\cmd.exe /C x.abc вместо x.abc.

Но я предпочел бы использовать чистый способ сделать это (или представитель Microsoft скажет мне, что это действительно проблема с Windows 7 и/или ClickOncce и что она будет исправлена ​​в ближайшее время).

4b9b3361

Ответ 1

Я придумал решение на базе .BAT, которое легко реализовать. Предположим, что вы хотите запустить приложение ClickOnce, связанное с файлами *.abc, тогда вы просто помещаете файл с тем же именем, но с расширением *.bat в той же папке, а затем вместо этого исполняете командный файл. Вот пакет script:

if exist "%windir%\sysnative\cmd.exe" goto :mode64bit

rem For a file named "C:\foo\xyz.bat", this will generate the corresponding
rem "C:\foo\xyz.abc" file, built as the concatenation of the drive (%~d0),
rem the folder (%~p0) and the file name (%~n0), plus ".abc":

"%~d0%~p0%~n0.abc"
goto :end

:mode64bit

rem When running in a 32-bit emulation environment on a real 64-bit system,
rem start the 64-bit version of CMD.EXE, and make if start the ".abc" file
rem for us:

C:\Windows\sysnative\cmd.exe /c "%~d0%~p0%~n0.xgen"

:end

Это может быть реализовано непосредственно в вызывающем файле *.abc, но иногда пакетный файл помогает в переходе...

Ответ 2

Похоже, вы создали свое приложение, используя "x32" в качестве целевой платформы, которая делает Process.Start порождать x32-разрядный процесс. И, как я полагаю, Windows 7 хранит ассоциации файлов для 32-разрядных и 64-разрядных приложений отдельно.

Если у вас нет COM или неуправляемых 32-битных зависимостей, вы можете попытаться создать приложение для целевой платформы "Any" вместо "x32".

Далее я исследовал и обнаружил, что установщик ClickOnce создает следующий открытый глагол для любого связанного расширения файла (GUID уникален для каждого приложения):

rundll32.exe dfshim.dll, ShOpenVerbExtension {dce01888-35e8-4df3-af35-cd971f520d8d} %1

Используя Process Monitor, я обнаружил, что 32-разрядная версия не может открыть ключ реестра HKCU\Software\Classes\Wow6432Node\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d}. (64-разрядная версия успешно открывает его на HKCU\Software\Classes\CLSID\{dce01888-35e8-4df3-af35-cd971f520d8d}.)

Так что для меня это действительно ошибка ClickOnce. Если бы я был вами, я использовал бы это грязное %WinDir%\system32\cmd.exe /C test.abc обходное решение. (Кажется, что работает - попробовал из диспетчера задач x32.)

Я добавил эту проблему в Microsoft Connect (обновление 2013-02-13: эта ссылка гниль).

Ответ 3

Это может привести только к расширению системы, например, .bat или даже .txt, но не всегда может запускать правильные программы через расширения.

Вместо этого попробуйте этот API или аналогичную альтернативу в .NET:

FindExecutable: shell32.dll Псевдоним: "FindExecutableA" / Тип возвращаемого типа "FindExecutableW": параметры int: lpFile Указатель на строку с нулевым завершением, указывающую имя файла. Это может быть документ или запускаемый файл. lpDirectory Указатель на строку с нулевым завершением, указывающую по умолчанию. lpResult Указатель на буфер для получения имени файла, когда функция возвращает. Это имя файла является строка с нулевым завершением, указывающая исполняемый файл запускается, когда "открытый" ассоциация запускается в файле указанный в параметре lpFile.

Это возвращает целое число, большее нуля, если успех и значение char будут содержать строка с нулевым завершением, которая указывает на исполняемый файл который запускает это расширение файла то вы можете использовать его так:

System.Diagnostics.Process.Start ("program.exe $:\path\x.abc");

Вместо program.exe вы будете использовать результат функции API, и вы будете использовать путь к файлу, разделенному пробелом, как в командной строке.

Что касается отказа приложения, это может указывать на то, что программе необходимы права администратора для правильной работы. cmd уже получил административные права, поэтому он может наследовать дочерние приложения, но не API окон. createprocess позволяет использовать атрибуты LPSECURITY, которые могут помочь в запуске этой программы с правильными привилегиями.

Ответ 4

Пробовали ли вы использовать ShellExecute(); API?

        [DllImport("Shell32.dll",CharSet=CharSet.Auto)]
    public static extern IntPtr ShellExecute(
        IntPtr hwnd, 
        string lpVerb,
        string lpFile, 
        string lpParameters, 
        string lpDirectory,
        int nShowCmd );

ShellExecute(this.Handle,"open","x.abc","","",3);

Вы также можете попробовать Shell(); функция, которая является частью фреймворка