Страницы

Поиск по вопросам

Показаны сообщения с ярлыком clickonce. Показать все сообщения
Показаны сообщения с ярлыком clickonce. Показать все сообщения

воскресенье, 15 марта 2020 г.

Ассоциация программы с собственным типом файла

#c_sharp #net #winforms #clickonce


Имеется файл программы в виде:

[project]
type=MyApp 1.0 project
directory=D:\Папка проекта
Еще какие-то значения.


При открытии файла внутри программы (Файл->Открыть проект) всё работает замечательно.
Но появилась нужда открывать этот файл двойным щелчком.
С помощью ClickOne создал файл ассоциаций. 

Открытие проекта пытаюсь сделать так:

private void MainForm_Load(object sender, EventArgs e)
{
    string[] args = Environment.GetCommandLineArgs();
    if (args.Length >= 2)
    {
        MessageBox.Show("Открыт проект! УРА!");
        OpenProject(args[1]);
    }
}


В Program.cs прописано, что принимаются аргументы:

public static void Main(string[] args)


Как решить эту проблему?
    


Ответы

Ответ 1



При регистрации с помощью ClickOnce аргументы приходят не в командную строку, а в AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData: foreach (string arg in AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData) MessageBox.Show(arg);

понедельник, 6 января 2020 г.

Как правильно добавить ClickOnce приложение в автозагрузку?

#c_sharp #clickonce


Необходимо либо получить путь к ярлыку приложения, установленному при помощи ClickOnce,
который создаётся в каталоге Environment.GetFolderPath(Environment.SpecialFolder.Programs)\PublisherName
либо его создать программно. Всё что я находил либо не работает, либо не устраивает.

UPD: В чем суть. При установке с помощью ClickOnce запускается ваше приложение, вы
производите настройку, выходите из приложения, запускаете снова - всё ок, настройки
есть. Программно получаете Application.StartupPath, этот путь закидываете в автозапуск
в реестре или создаёте ярлык в Автозагрузке, но наступает фиаско, когда при включении
ПК настроек нет, всё по дефолту и необходимо конфигурировать заново. И запустив приложение
с ярлыка, в котором Application.StartupPath указан, мы можем получить настройки приложения
отличные от тех, которые указывали запустив приложение с ярлыка созданного установщиком
ClickOnce. Во как!
    


Ответы

Ответ 1



Самая неудачная идея для приложения с развертыванием clickonce, да и большинства приложений в принципе, это хранить конфигурационные файлы рядом с исполняемым файлом. Это вполне допустимо, если необходимые настройки можно сделать на этапе сборки релиза и менять их только в случае крайней необходимости, но если пользователь может менять их сам когда ему вздумается, лучше вынести в отдельное фиксированное расположение, не зависимое от расположения исполняемых файлов. Плюс к этому, механизм обновления clickonce построен таким образом, что ваши конфигурационные файлы могут быть перезаписаны во время обновления, если в обновленной версии они более новые, для текстовых файлов по дате последнего изменения. Причина такого поведения довольно проста, в конфигурации обновленного приложения могут быть дополнительные параметры, без которых оно не сможет нормально работать, следовательно самый простой путь - перезаписать старую конфигурацию новой. Самое грустное, что стандартные средства вроде app.config или Settings, которые можно настроить в свойствах проекта или добавить явно, предполагают хранение файла с настройками по-умолчанию рядом с исполняемым файлом. Разумеется, можно дописать недостающее в сгенерированый класс и он будет работать так как вы захотите, но по-умолчанию реализация минимальная из возможных, описание на MSDN довольно скудное, хотя при большом желании можно разобраться что и куда прописать в параметры объекта Settins, чтобы он начал себя вести хоть чуточку осмысленно (к тому же, MS сами в итоге отказались от этой идеи и по-умолчанию предлагают более легковесный и понятный app.config). Итого, нужно сделать так, чтобы ваше приложение один раз настроенное, не теряло настройки при обновлениях. переустановках и т.д. Штатные средства этого не позволяют, значит придется писать собственный менеджер (ну или допиливать заготовку Settings от MS). Зато вы можете сами выбрать удобный для вас формат хранения настроек, хоть шифрованный бинарник, если вас или заказчика мучает паранойя. В качестве возможных мест хранения конфигурации можно рассмотреть два: Реестр Windows (куст CurrentUser, чтобы не требовать права администратора) и папка %APPDATA%. В обоих случаях логика работы примерно одинакова, с поправкой на различие методов чтения/записи значений настроек: Проверяем наличие папки %APPDATA%\\ или нужного раздела реестра. Проверяете наличие нужных файлов конфигурации в папке, ключей в разделе реестра. (Опционально) Проверяем версию конфигурации. Если нужные файлы/ключи реестра обнаружены и версия подходит, загружаем настройки и работаем как обычно. Если не подходит версия, дочитываем необходимое из настроек по-умолчанию, при необходимости просим пользователя уточнить параметры. Если файлы/ключи реестра не обнаружены, создаем папку и файлы, запускаем конфигуратор для пользователя или просто стартуем с настройками по-умолчанию. Как видите, в данной схеме отсутствует упоминание "первого" и "последующих" запусков. В этом просто нет необходимости. Подобный подход не дает потерять настройки даже в результате случайного удаления и/или принудительной переустановки приложения по какой-то иной причине (при должном упорстве можно получить устойчивость и к переустановке ОС, но это уже отдельная тема). Еще один плюс данного подхода заключается в том, что данная папка, как и конечная папка размещения приложения clickonce, находятся в профиле пользователя, а это означает, что приложение и настройки будут доступны пользователю на любой машине, если у вас есть домен, машина в домене и в домене настроены перемещаемые профили пользователей. Как именно читать и сохранять настройки зависит от формата, который вы выберете. В простейшем случае можно просто скопировать *.config файлы с настройками по-умолчанию из папки с исполняемыми файлами в новое расположение и уже там их менять. Для получения дополнительных сведений о текущей установке приложения clickonce воспользуйтесь классом ApplicationDeployment. Вообще стоит повнимательнее почитать про его возможности, если решили использовать clickonce. Cсылку на приложение Environment.GetFolderPath(Environment.SpecialFolder.Programs)\PublisherName\AppName\AppName.appref-ms добавить в автозапуск любым удобным способом. При обозначенном выше способе хранения настроек, описанной вами проблемы не будет, так как они больше не привязаны к точке запуска приложения. Подобная схема хранения конфигураций прекрасно работает и с классической моделью установки, и с самописными обновляторами/инсталляторами, и, разумеется, с clickonce-приложениями. Причем конкретно у меня clickonce появился самым последним и никаких изменений для перехода не потребовалось.

Ответ 2



создается сам при первом запуске но в настройках можно указать путь размещения приложения

Ответ 3



Вы неправильно регистрируете приложение в автозагрузке. ClickOnce-приложения не должны запускаться через .exe-файл Для того, чтобы запустить приложение, воспользу // Узел реестра, в котором хранятся автозапускаемые приложения using (RegistryKey rkApp = Registry.CurrentUser.OpenSubKey(@"\Microsoft\Windows\CurrentVersion\Run", true)) { // Путь к ярлыку вашего приложения string startPath; startPath = GetApplicationPath("YourPublisher", "YourSuite", "YourProduct"); startPath = GetApplicationPath("YourSuite", "YourProduct"); startPath = GetApplicationPath("YourProduct"); rkApp.SetValue("YourProduct", startPath); } private static String GetApplicationPath(String yourPublisher, String yourSuite, String yourProduct) { return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Programs), yourPublisher, yourSuite, yourProduct) + ".appref-ms"; } private static String GetApplicationPath(String yourSuite, String yourProduct) { foreach (String publisherPath in Directory.EnumerateDirectories(Environment.GetFolderPath(Environment.SpecialFolder.Programs))) { String applicationPath = Path.Combine(publisherPath, yourSuite, yourProduct) + ".appref-ms"; if (File.Exists(applicationPath)) return applicationPath; } throw new FileNotFoundException(); } private static String GetApplicationPath(String yourProduct) { return Directory.EnumerateFiles(Environment.GetFolderPath(Environment.SpecialFolder.Programs), yourProduct + ".appref-ms").First(); } Это немного модифицированное решение с англоязычной версии сайта. Поясню - ClickOnce-приложения ввиду возможности развёртывания в сети, используют в качестве хранилища персистентных данных не текущий каталог программы, а System.Deployment.Application.ApplicationDeployment.CurrentDeployment.DataDirectory. При запуске через ярлык он инициализируется. При запуске через .exe - нет.

четверг, 2 января 2020 г.

Как получить версию публикации ClickOnce приложения?

#c_sharp #net #clickonce


Требуется отображать в программе, какая версия опубликованного в ClickOnce приложения
сейчас запущена. Просьба не путать с версией сборки приложения.
    


Ответы

Ответ 1



Чтобы получить версию публикации ClickOnce из самого приложения, необходимо сделать три действия: Добавить ссылку в проект на библиотеку System.Deployment.dll Подключить пространство имён: using System.Deployment.Application; Получить версию установленной публикации: if (ApplicationDeployment.IsNetworkDeployed) { Version version = ApplicationDeployment.CurrentDeployment.CurrentVersion; }

пятница, 27 декабря 2019 г.

Как отловить нажатие клавиш в WPF-приложении, когда оно свёрнуто?

#c_sharp #wpf #windows #clickonce #keyboardhook


Имеется WPF-приложение (аудио-проигрыватель). Необходимо добавить возможность отлавливать
нажатие определённых клавиш, даже когда приложение свёрнуто. Например, при нажатии
Num4 - останавливать вопроизведение, при нажатии других клавиш (в зависимости от настроек
- продолжать воспроизведение музыки и т.п.). 
Пробовал реализовать через функцию GetAsyncKeyState(int vkey) из user32.dll, но мне
это не совсем подходит, вот пример кода:

public static class KeyState
{
    public static void Wait(Key key, Action action)
    {
        var asyncOperation = AsyncOperationManager.CreateOperation(null);
        SendOrPostCallback callBack = state => action((KeyStates) state);
        ThreadPool.QueueUserWorkItem(state =>
        {
            var vk = KeyInterop.VirtualKeyFromKey(key);                
            var prev = ((GetAsyncKeyState(vk) & 0x8000) == 0x8000);
            if (prev)
            {
                asyncOperation.Post(callBack, KeyStates.Down);
            }
            while (true)
            {
                var res = ((GetAsyncKeyState(vk) & 0x8000) == 0x8000);
                if (res != prev && res)
                {
                    asyncOperation.Post(callBack, KeyStates.Down);
                }
                prev = res;
            }
        });
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    private static extern short GetAsyncKeyState(int vkey);
}


Проблема здесь в том, что мне не только нужно отловить нажатие клавиши, но и предотвратить
"продвижение" нажатия клавиши далее. Т.е. нажатие клавиши должно перехватываться моим
приложением и не попадать в другие (например, в текстовый редактор).
Буду благодарен за помощь. Если кто-нибудь ещё и подскажет как это корректно реализовать
для приложения, которое развертывается через ClickOnce, было бы вообще шикарно. Спасибо! 
    


Ответы

Ответ 1



Начиная с Windows Vista можно использовать функцию RegisterHotKey. Она позволяет зарегистрировать глобальную горячую клавишу и указать окно, которое будет принимать сообщения. Чтобы обработать эти сообщения в WPF приложении, нужно зарегистрировать обработчик через HwndSource.AddHook. Пример как обработать событие горячей клавиши и вывести момент ее нажатия в TextBlock: using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Runtime.InteropServices; using System.Windows.Interop; namespace WpfHotkey { public partial class MainWindow : Window { [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); //необходимые константы public const int MOD_ALT = 0x1; public const int MOD_CONTROL = 0x2; public const int MOD_SHIFT = 0x4; public const int MOD_WIN = 0x8; public const int WM_HOTKEY = 0x312; //несколько примеров виртуальных кодов public const uint VK_F1 = 0x70; public const uint VK_F2 = 0x71; public const uint VK_UP = 0x26; public const uint VK_DOWN = 0x28; //обработчик сообщений для окна private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == WM_HOTKEY) { textBlock1.Text += DateTime.Now.ToString() + " WM_HOTKEY message, ID: 0x" + wParam.ToString("X"); textBlock1.Text += Environment.NewLine; handled = true; } return IntPtr.Zero; } public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { WindowInteropHelper h = new WindowInteropHelper(this); HwndSource source = HwndSource.FromHwnd(h.Handle); source.AddHook(new HwndSourceHook(WndProc));//регистрируем обработчик сообщений bool res = RegisterHotKey(h.Handle, 1, 0, VK_F1);//регистрируем горячую клавишу if (res == false) MessageBox.Show("RegisterHotKey failed"); } } }

среда, 5 июня 2019 г.

Ассоциация программы с собственным типом файла

Имеется файл программы в виде:
[project] type=MyApp 1.0 project directory=D:\Папка проекта Еще какие-то значения.
При открытии файла внутри программы (Файл->Открыть проект) всё работает замечательно. Но появилась нужда открывать этот файл двойным щелчком. С помощью ClickOne создал файл ассоциаций.
Открытие проекта пытаюсь сделать так:
private void MainForm_Load(object sender, EventArgs e) { string[] args = Environment.GetCommandLineArgs(); if (args.Length >= 2) { MessageBox.Show("Открыт проект! УРА!"); OpenProject(args[1]); } }
В Program.cs прописано, что принимаются аргументы:
public static void Main(string[] args)
Как решить эту проблему?


Ответ

При регистрации с помощью ClickOnce аргументы приходят не в командную строку, а в AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData
foreach (string arg in AppDomain.CurrentDomain.SetupInformation.ActivationArguments.ActivationData) MessageBox.Show(arg);

суббота, 20 апреля 2019 г.

Как получить версию публикации ClickOnce приложения?

Требуется отображать в программе, какая версия опубликованного в ClickOnce приложения сейчас запущена. Просьба не путать с версией сборки приложения.


Ответ

Чтобы получить версию публикации ClickOnce из самого приложения, необходимо сделать три действия:
Добавить ссылку в проект на библиотеку System.Deployment.dll Подключить пространство имён:
using System.Deployment.Application; Получить версию установленной публикации:
if (ApplicationDeployment.IsNetworkDeployed) { Version version = ApplicationDeployment.CurrentDeployment.CurrentVersion; }

понедельник, 11 февраля 2019 г.

Как правильно добавить ClickOnce приложение в автозагрузку?

Необходимо либо получить путь к ярлыку приложения, установленному при помощи ClickOnce, который создаётся в каталоге Environment.GetFolderPath(Environment.SpecialFolder.Programs)\PublisherName либо его создать программно. Всё что я находил либо не работает, либо не устраивает.
UPD: В чем суть. При установке с помощью ClickOnce запускается ваше приложение, вы производите настройку, выходите из приложения, запускаете снова - всё ок, настройки есть. Программно получаете Application.StartupPath, этот путь закидываете в автозапуск в реестре или создаёте ярлык в Автозагрузке, но наступает фиаско, когда при включении ПК настроек нет, всё по дефолту и необходимо конфигурировать заново. И запустив приложение с ярлыка, в котором Application.StartupPath указан, мы можем получить настройки приложения отличные от тех, которые указывали запустив приложение с ярлыка созданного установщиком ClickOnce. Во как!


Ответ

Самая неудачная идея для приложения с развертыванием clickonce, да и большинства приложений в принципе, это хранить конфигурационные файлы рядом с исполняемым файлом. Это вполне допустимо, если необходимые настройки можно сделать на этапе сборки релиза и менять их только в случае крайней необходимости, но если пользователь может менять их сам когда ему вздумается, лучше вынести в отдельное фиксированное расположение, не зависимое от расположения исполняемых файлов.
Плюс к этому, механизм обновления clickonce построен таким образом, что ваши конфигурационные файлы могут быть перезаписаны во время обновления, если в обновленной версии они более новые, для текстовых файлов по дате последнего изменения. Причина такого поведения довольно проста, в конфигурации обновленного приложения могут быть дополнительные параметры, без которых оно не сможет нормально работать, следовательно самый простой путь - перезаписать старую конфигурацию новой.
Самое грустное, что стандартные средства вроде app.config или Settings, которые можно настроить в свойствах проекта или добавить явно, предполагают хранение файла с настройками по-умолчанию рядом с исполняемым файлом. Разумеется, можно дописать недостающее в сгенерированый класс и он будет работать так как вы захотите, но по-умолчанию реализация минимальная из возможных, описание на MSDN довольно скудное, хотя при большом желании можно разобраться что и куда прописать в параметры объекта Settins, чтобы он начал себя вести хоть чуточку осмысленно (к тому же, MS сами в итоге отказались от этой идеи и по-умолчанию предлагают более легковесный и понятный app.config).
Итого, нужно сделать так, чтобы ваше приложение один раз настроенное, не теряло настройки при обновлениях. переустановках и т.д. Штатные средства этого не позволяют, значит придется писать собственный менеджер (ну или допиливать заготовку Settings от MS). Зато вы можете сами выбрать удобный для вас формат хранения настроек, хоть шифрованный бинарник, если вас или заказчика мучает паранойя.
В качестве возможных мест хранения конфигурации можно рассмотреть два: Реестр Windows (куст CurrentUser, чтобы не требовать права администратора) и папка %APPDATA%. В обоих случаях логика работы примерно одинакова, с поправкой на различие методов чтения/записи значений настроек:
Проверяем наличие папки %APPDATA%\\ или нужного раздела реестра. Проверяете наличие нужных файлов конфигурации в папке, ключей в разделе реестра. (Опционально) Проверяем версию конфигурации. Если нужные файлы/ключи реестра обнаружены и версия подходит, загружаем настройки и работаем как обычно. Если не подходит версия, дочитываем необходимое из настроек по-умолчанию, при необходимости просим пользователя уточнить параметры. Если файлы/ключи реестра не обнаружены, создаем папку и файлы, запускаем конфигуратор для пользователя или просто стартуем с настройками по-умолчанию.
Как видите, в данной схеме отсутствует упоминание "первого" и "последующих" запусков. В этом просто нет необходимости. Подобный подход не дает потерять настройки даже в результате случайного удаления и/или принудительной переустановки приложения по какой-то иной причине (при должном упорстве можно получить устойчивость и к переустановке ОС, но это уже отдельная тема).
Еще один плюс данного подхода заключается в том, что данная папка, как и конечная папка размещения приложения clickonce, находятся в профиле пользователя, а это означает, что приложение и настройки будут доступны пользователю на любой машине, если у вас есть домен, машина в домене и в домене настроены перемещаемые профили пользователей.
Как именно читать и сохранять настройки зависит от формата, который вы выберете. В простейшем случае можно просто скопировать *.config файлы с настройками по-умолчанию из папки с исполняемыми файлами в новое расположение и уже там их менять.
Для получения дополнительных сведений о текущей установке приложения clickonce воспользуйтесь классом ApplicationDeployment. Вообще стоит повнимательнее почитать про его возможности, если решили использовать clickonce.
Cсылку на приложение Environment.GetFolderPath(Environment.SpecialFolder.Programs)\PublisherName\AppName\AppName.appref-ms добавить в автозапуск любым удобным способом. При обозначенном выше способе хранения настроек, описанной вами проблемы не будет, так как они больше не привязаны к точке запуска приложения.

Подобная схема хранения конфигураций прекрасно работает и с классической моделью установки, и с самописными обновляторами/инсталляторами, и, разумеется, с clickonce-приложениями. Причем конкретно у меня clickonce появился самым последним и никаких изменений для перехода не потребовалось.