osdep/w32_register: add register/unregister options for Windows

On Linux, we have `mpv.desktop`, and on macOS, we have `osxbundle`, both
of which handle file associations and protocol registration in the
system environment.

On Windows, this information is stored in the registry, so this commit
adds support for it.

It registers the application, supported file types, and supported
protocols, and adds an uninstall entry so users can remove all
registrations via the control panel. Note that this does not remove the
binary itself.

The implementation is fully portable. There are no external installers,
as mpv handles everything automatically. This should improve usability
when moving binaries and so on.

- `mpv --register` registers mpv (see verbose output for a list of actions).
- `mpv --unregister` reverts all changes made during installation.
- `mpv --register-rpath <string>` allows specifying a string to be
  prepended to `PATH` before running mpv. This is useful when using
external dependencies that shouldn't be added globally to `PATH`.
This commit is contained in:
Kacper Michajłow
2025-02-18 06:41:33 +01:00
parent b9b3106d2f
commit 4f08802ff1
8 changed files with 593 additions and 0 deletions

View File

@@ -0,0 +1 @@
Add `--register` and `--unregister`

View File

@@ -7969,6 +7969,11 @@ Miscellaneous
``--media-controls=<yes|no>``
(Windows only)
Enable integration of media control interface SystemMediaTransportControls.
Windows may display "Unknown app" or show a missing mpv icon in the media
control panel. To fully support it, you need to register mpv using the
``--register`` command.
Default: yes (except for libmpv)
``--force-media-title=<string>``
@@ -8213,3 +8218,72 @@ Miscellaneous
On Wayland, this option only has effect on the ``wayland`` backend, and
not for the ``vo`` backend. See ``current-clipboard-backend`` property for
more details.
``--register``
(Windows only)
Registers mpv as a media player on Windows. This includes adding registry
entries to associate mpv with media files and protocols, as well as enabling
autoplay handlers for Blu-ray, DVD, and CD-Audio.
Note that the registration is done in-place, so the current mpv.exe path will
be used. If you move mpv after registering it, you can re-run this command to
update the registry entries. You can also ``--unregister`` at any time and
using any mpv binary that supports this command, it doesn't have to be
specifically the one that was used to register it.
When using this option, mpv will exit after completing the process.
To see a detailed list of operations, run mpv with the ``-v`` option.
The list of the file extensions to register, can be controlled with the
``--video-exts``, ``--audio-exts``, ``--image-exts``, ``--playlist-exts``
and ``--archive-exts`` options.
By default, mpv will be registered for the current user. To register it for
all users, run mpv as an administrator with this option. However, this is
not recommended, as registering it per user is generally preferable.
You can unregister mpv from the Windows Settings or by running mpv with the
``--unregister`` option.
``--register-rpath=<string>``
(Windows only)
When registering with ``--register``, this option allows you to specify the
path(s) to prepend so that mpv can find the necessary DLLs. The specified
string will be prepended to the runtime PATH whenever mpv is executed.
This is useful for setting up paths to external libraries required by mpv
without adding them to the global PATH environment variable.
The format of the string follows the same structure as the PATH environment
variable, a semicolon-separated list of paths.
.. note::
This sets the ``App Paths`` for mpv in the Windows registry, which
Windows Shell uses to locate the executable and its dependencies. As a
result, mpv can be launched seamlessly in most cases, but not in every
scenario. Notably, running mpv from the command line does not use
`ShellExecute` under the hood, it uses `CreateProcess`, which does not
handle the ``App Paths`` registry key.
To work around this, you can create a small wrapper PowerShell script that
runs ``Start-Process <mpv path>`` and all will work as expected.
``--unregister``
(Windows only)
Unregisters mpv as a media player on Windows, undoing all changes made by
the ``--register`` option. This will not remove mpv binary itself.
You can use any mpv binary that supports this command, to unregister, doesn't
have to be specifically the one that was used to register it.
Windows Settings Application entry is tied to the mpv.exe path. If you
remove the binary, it will not work. However, you can still unregister it
using this command, register it in a new location, or restore mpv to its
original location.
If mpv was previously registered for all users, run this command as an
administrator to remove it for all users.

View File

@@ -518,6 +518,7 @@ if features['win32-desktop']
'player/clipboard/clipboard-win.c',
'osdep/language-win.c',
'osdep/terminal-win.c',
'osdep/w32_register.c',
'video/out/w32_common.c',
'video/out/win32/displayconfig.c',
'video/out/win32/droptarget.c',

View File

@@ -107,6 +107,8 @@ extern const struct m_sub_options egl_conf;
extern const struct m_sub_options mp_sub_filter_opts;
extern const struct m_sub_options w32_register_conf;
static const struct m_sub_options screenshot_conf = {
.opts = image_writer_opts,
.size = sizeof(struct image_writer_opts),
@@ -522,6 +524,7 @@ static const m_option_t mp_opts[] = {
{"msg-module", OPT_BOOL(msg_module), .flags = UPDATE_TERM},
{"msg-time", OPT_BOOL(msg_time), .flags = UPDATE_TERM},
#if HAVE_WIN32_DESKTOP
{"", OPT_SUBSTRUCT(w32_register_opts, w32_register_conf)},
{"priority", OPT_CHOICE(w32_priority,
{"no", 0},
{"realtime", REALTIME_PRIORITY_CLASS},

View File

@@ -350,6 +350,7 @@ typedef struct MPOpts {
char **playlist_exts;
bool osd_bar_visible;
struct w32_register_opts *w32_register_opts;
int w32_priority;
bool media_controls;

478
osdep/w32_register.c Normal file
View File

@@ -0,0 +1,478 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#include "w32_register.h"
#include <windows.h>
#include <knownfolders.h>
#include <pathcch.h>
#include <shlobj.h>
#include <shlwapi.h>
#include <common/msg.h>
#include <options/options.h>
#include <osdep/io.h>
#include <player/core.h>
#include <stream/stream.h>
#include <version.h>
#include "windows_utils.h"
#define MPV_NAME L"mpv"
#define MPV_FRIENDLY_NAME L"mpv media player"
#define KEY_AUTOPLAY L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoplayHandlers"
#define KEY_MPV_APP L"Software\\Classes\\Applications\\" MPV_NAME L".exe"
#define KEY_MPV_APP_PATHS L"Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\"
#define KEY_MPV_APP_PATH(suffix) KEY_MPV_APP_PATHS MPV_NAME suffix
#define KEY_MPV_CAPABILITIES_APP L"Software\\Clients\\Media\\" MPV_NAME
#define KEY_MPV_CAPABILITIES KEY_MPV_CAPABILITIES_APP L"\\Capabilities"
#define KEY_MPV_UNINSTALL L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" MPV_NAME
#define MPV_PROG_ID_PREFIX L"io.mpv."
#define MPV_PROG_ID(h) MPV_PROG_ID_PREFIX h
#define KEY_MPV_PROG_ID(h) L"Software\\Classes\\" MPV_PROG_ID(h)
struct w32_register_opts {
bool register_opt;
char *rpath;
bool unregister;
};
#define OPT_BASE_STRUCT struct w32_register_opts
const struct m_sub_options w32_register_conf = {
.opts = (const struct m_option[]) {
{"register", OPT_BOOL(register_opt)},
{"register-rpath", OPT_STRING(rpath)},
{"unregister", OPT_BOOL(unregister)},
{0}
},
.size = sizeof(struct w32_register_opts)
};
static bool is_admin(void)
{
PSID sid;
SID_IDENTIFIER_AUTHORITY identifier_authority = SECURITY_NT_AUTHORITY;
if (!AllocateAndInitializeSid(&identifier_authority, 2, SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &sid))
{
return false;
}
BOOL member = false;
CheckTokenMembership(NULL, sid, &member);
if (sid)
FreeSid(sid);
return member;
}
static void create_shortcut(struct mp_log *log, const wchar_t *target,
const wchar_t *shortcut)
{
HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
if (FAILED(hr)) {
mp_msg(log, MSGL_ERR, "Failed to initialize COM: %s\n",
mp_HRESULT_to_str(hr));
return;
}
IShellLinkW *shell_link = NULL;
IPersistFile *persist_file = NULL;
hr = CoCreateInstance(&CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
&IID_IShellLinkW, (void **)&shell_link);
if (FAILED(hr)) {
mp_msg(log, MSGL_ERR, "Failed to create IShellLinkW instance: %s\n",
mp_HRESULT_to_str(hr));
goto done;
}
hr = IShellLinkW_SetPath(shell_link, target);
if (FAILED(hr)) {
mp_msg(log, MSGL_ERR, "Failed to set target path: %s\n",
mp_HRESULT_to_str(hr));
goto done;
}
hr = IShellLinkW_QueryInterface(shell_link, &IID_IPersistFile,
(LPVOID *)&persist_file);
if (FAILED(hr)) {
mp_msg(log, MSGL_ERR, "Failed to get IPersistFile interface: %s\n",
mp_HRESULT_to_str(hr));
goto done;
}
hr = IPersistFile_Save(persist_file, shortcut, TRUE);
if (FAILED(hr)) {
mp_msg(log, MSGL_ERR, "Failed to save shortcut: %s\n",
mp_HRESULT_to_str(hr));
goto done;
}
mp_msg(log, MSGL_V, "Shortcut created: '%ls' -> '%ls'\n", shortcut, target);
done:
SAFE_RELEASE(persist_file);
SAFE_RELEASE(shell_link);
CoUninitialize();
}
static void reg_add(struct mp_log *log, HKEY key, LPCWSTR sub_key, LPCWSTR name,
DWORD type, const BYTE *data, DWORD size)
{
HKEY sub_key_handle;
if (RegCreateKeyExW(key, sub_key, 0, NULL, REG_OPTION_NON_VOLATILE,
KEY_WRITE, NULL, &sub_key_handle,
NULL) != ERROR_SUCCESS)
{
mp_msg(log, MSGL_ERR, "Failed to create registry key\n");
return;
}
if (RegSetValueExW(sub_key_handle, name, 0, type, data, size) != ERROR_SUCCESS)
mp_msg(log, MSGL_ERR, "Failed to set registry value\n");
RegCloseKey(sub_key_handle);
}
static void reg_add_str(struct mp_log *log, HKEY key, LPCWSTR sub_key,
LPCWSTR name, LPCWSTR value)
{
const wchar_t *root = key == HKEY_LOCAL_MACHINE ? L"HKEY_LOCAL_MACHINE"
: L"HKEY_CURRENT_USER";
mp_msg(log, MSGL_V, "Adding registry key: %ls\\%ls\\%ls -> '%ls'\n", root,
sub_key, name ? name : L"(Default)", value);
reg_add(log, key, sub_key, name, REG_SZ, (const BYTE *)value,
(wcslen(value) + 1) * sizeof(WCHAR));
}
static void reg_add_dwr(struct mp_log *log, HKEY key, LPCWSTR sub_key,
LPCWSTR name, DWORD value)
{
const wchar_t *root = key == HKEY_LOCAL_MACHINE ? L"HKEY_LOCAL_MACHINE"
: L"HKEY_CURRENT_USER";
mp_msg(log, MSGL_V, "Adding registry key: %ls\\%ls\\%ls -> %lu\n", root,
sub_key, name ? name : L"(Default)", value);
reg_add(log, key, sub_key, name, REG_DWORD, (const BYTE *)&value,
sizeof(DWORD));
}
static void reg_del(struct mp_log *log, HKEY key, LPCWSTR sub_key,
LPCWSTR value)
{
const wchar_t *root = key == HKEY_LOCAL_MACHINE ? L"HKEY_LOCAL_MACHINE"
: L"HKEY_CURRENT_USER";
const char *name = value ? "value" : "key";
mp_msg(log, MSGL_V, "Removing registry %s: %ls\\%ls%ls%ls\n", name, root,
sub_key, value ? L"\\" : L"", value ? value : L"");
LSTATUS status = value ? RegDeleteKeyValueW(key, sub_key, value)
: RegDeleteTreeW(key, sub_key);
if (status != ERROR_SUCCESS) {
mp_msg(log, status == ERROR_FILE_NOT_FOUND ? MSGL_DEBUG : MSGL_ERR,
"Failed to delete registry %s: %ls\\%ls%ls%ls -> %s\n", name,
root, sub_key, value ? L"\\" : L"", value ? value : L"",
mp_HRESULT_to_str(HRESULT_FROM_WIN32(status)));
}
}
static uint64_t get_file_size(const wchar_t *path)
{
WIN32_FILE_ATTRIBUTE_DATA fad;
if (!GetFileAttributesExW(path, GetFileExInfoStandard, &fad))
return 0;
return ((uint64_t)fad.nFileSizeHigh << 32) | fad.nFileSizeLow;
}
static wchar_t *w32_get_shortcut_path(struct mp_log *log)
{
LPWSTR shortcut_path = NULL;
LPWSTR programs_path = NULL;
if (SUCCEEDED(SHGetKnownFolderPath(&FOLDERID_Programs, 0, NULL, &programs_path))) {
PathAllocCombine(programs_path, MPV_NAME L".lnk",
PATHCCH_ALLOW_LONG_PATHS, &shortcut_path);
} else {
mp_msg(log, MSGL_ERR, "Failed to get Programs folder path.\n");
}
CoTaskMemFree(programs_path);
return shortcut_path;
}
#define REGISTER_AUTOPLAY_HANDLER(media, event, action) \
reg_add_str(log, root, KEY_MPV_PROG_ID(media) L"\\shell", NULL, L"open"); \
reg_add_str(log, root, KEY_MPV_PROG_ID(media) L"\\shell\\open\\command", \
NULL, L"mpv.exe " media L":// --" media L"-device=%L"); \
reg_add_str(log, root, \
KEY_AUTOPLAY L"\\Handlers\\MpvPlay" event "OnArrival", \
L"Action", L"" action); \
reg_add_str(log, root, \
KEY_AUTOPLAY L"\\Handlers\\MpvPlay" event "OnArrival", \
L"DefaultIcon", icon_path); \
reg_add_str(log, root, \
KEY_AUTOPLAY L"\\Handlers\\MpvPlay" event "OnArrival", \
L"InvokeProgID", MPV_PROG_ID(media)); \
reg_add_str(log, root, \
KEY_AUTOPLAY L"\\Handlers\\MpvPlay" event "OnArrival", \
L"InvokeVerb", L"open"); \
reg_add_str(log, root, \
KEY_AUTOPLAY L"\\Handlers\\MpvPlay" event "OnArrival", \
L"Provider", MPV_FRIENDLY_NAME); \
reg_add_str(log, root, \
KEY_AUTOPLAY L"\\EventHandlers\\Play" event "OnArrival", \
L"MpvPlay" event "OnArrival", L"");
static void w32_register(struct MPContext *mpctx)
{
void *tmp = talloc_new(NULL);
struct mp_log *log = mp_log_new(tmp, mpctx->log, "win32");
LPWSTR mpv_path = talloc_array(tmp, wchar_t, MP_PATH_MAX);
DWORD mpv_path_len = GetModuleFileNameW(NULL, mpv_path, MP_PATH_MAX);
if (!mpv_path_len || mpv_path_len == MP_PATH_MAX) {
mp_msg(log, MSGL_ERR, "Failed to get mpv path.\n");
return;
}
LPWSTR icon_path = talloc_array(tmp, wchar_t, mpv_path_len + 3);
memcpy(icon_path, mpv_path, mpv_path_len * sizeof(wchar_t));
icon_path[mpv_path_len + 0] = L',';
icon_path[mpv_path_len + 1] = L'0';
icon_path[mpv_path_len + 2] = L'\0';
HKEY root = is_admin() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
mp_msg(log, MSGL_INFO, "Registering mpv for %s...\n",
root == HKEY_LOCAL_MACHINE ? "all users" : "the current user");
// Register mpv as an application
// <https://learn.microsoft.com/windows/win32/shell/app-registration>
reg_add_str(log, root, KEY_MPV_APP_PATH(".exe"), NULL, mpv_path);
reg_add_dwr(log, root, KEY_MPV_APP_PATH(".exe"), L"UseUrl", 1);
char *rpath = mpctx->opts->w32_register_opts->rpath;
if (rpath) {
wchar_t *rpath_w = mp_from_utf8(tmp, rpath);
reg_add_str(log, root, KEY_MPV_APP_PATH(".exe"), L"Path", rpath_w);
reg_add_str(log, root, KEY_MPV_APP_PATH(".com"), L"Path", rpath_w);
}
// Name of the "Open With" entry in the context menu
// I prefer a simple "mpv" here, so let's skip this.
// reg_add_str(log, root, KEY_MPV_APP, L"FriendlyAppName", MPV_FRIENDLY_NAME);
// Add shell open command
reg_add_str(log, root, KEY_MPV_APP L"\\shell", NULL, L"open");
reg_add_str(log, root, KEY_MPV_APP L"\\shell\\open\\command", NULL,
L"mpv.exe -- \"%L\"");
// Register mpv capabilities and handlers
// Note that we do not register any extensions or protocols directly,
// as mpv does not own any. It is simply a media player that can open them.
// Windows will prompt the user to choose the default application for each
// file type encountered.
// Register only a single URL and file handler; there is no need to
// duplicate this for each supported protocol or file type.
// Register URL handler
reg_add_str(log, root, KEY_MPV_PROG_ID("url"), NULL, L"URL:mpv");
reg_add_str(log, root, KEY_MPV_PROG_ID("url"), L"URL Protocol", L"");
reg_add_dwr(log, root, KEY_MPV_PROG_ID("url"), L"EditFlags", FTA_Show);
reg_add_str(log, root, KEY_MPV_PROG_ID("url"), L"FriendlyTypeName",
L"mpv URL handler");
reg_add_str(log, root, KEY_MPV_PROG_ID("url") L"\\shell", NULL, L"open");
reg_add_str(log, root, KEY_MPV_PROG_ID("url") L"\\shell\\open\\command",
NULL, L"mpv.exe -- \"%L\"");
reg_add_str(log, root, KEY_MPV_PROG_ID("url") L"\\shell\\open\\ddeexec",
NULL, L"");
// Register URL protocols
char **safe_protocols = talloc_steal(tmp, stream_get_proto_list(true));
mp_assert(safe_protocols);
char *protocols_str = NULL;
for (int i = 0; safe_protocols[i]; ++i) {
reg_add_str(log, root, KEY_MPV_CAPABILITIES L"\\URLAssociations",
mp_from_utf8(tmp, safe_protocols[i]),
MPV_PROG_ID("url"));
if (!protocols_str) {
protocols_str = talloc_strdup(tmp, safe_protocols[i]);
} else {
protocols_str = talloc_asprintf_append(protocols_str, ":%s",
safe_protocols[i]);
}
}
if (protocols_str) {
reg_add_str(log, root, KEY_MPV_APP_PATH(".exe"), L"SupportedProtocols",
mp_from_utf8(tmp, protocols_str));
}
// Register file handler
reg_add_str(log, root, KEY_MPV_PROG_ID("file"), NULL, L"mpv");
reg_add_str(log, root, KEY_MPV_PROG_ID("file"), L"FriendlyTypeName", L"mpv Media File");
reg_add_dwr(log, root, KEY_MPV_PROG_ID("file"), L"EditFlags", FTA_OpenIsSafe);
reg_add_str(log, root, KEY_MPV_PROG_ID("file") L"\\shell", NULL, L"open");
reg_add_str(log, root, KEY_MPV_PROG_ID("file") L"\\shell\\open\\command",
NULL, L"mpv.exe -- \"%L\"");
// Register file associations
char **exts_groups[] = {
#if HAVE_LIBARCHIVE
mpctx->opts->archive_exts,
#endif
mpctx->opts->audio_exts,
mpctx->opts->image_exts,
mpctx->opts->playlist_exts,
mpctx->opts->video_exts,
};
for (int i = 0; i < MP_ARRAY_SIZE(exts_groups); ++i) {
char **exts = exts_groups[i];
for (int j = 0; exts && exts[j]; ++j) {
wchar_t *ext = mp_from_utf8(tmp, mp_tprintf(10, ".%s", exts[j]));
reg_add_str(log, root, KEY_MPV_APP L"\\SupportedTypes", ext, L"");
reg_add_str(log, root, KEY_MPV_CAPABILITIES L"\\FileAssociations",
ext, MPV_PROG_ID("file"));
}
}
// Connect above handlers to the application
reg_add_str(log, root, KEY_MPV_CAPABILITIES, L"ApplicationName", MPV_NAME);
reg_add_str(log, root, KEY_MPV_CAPABILITIES, L"ApplicationDescription", MPV_FRIENDLY_NAME);
// Register the application
reg_add_str(log, root, L"Software\\RegisteredApplications", MPV_NAME, KEY_MPV_CAPABILITIES);
// <https://learn.microsoft.com/windows/win32/shell/how-to-register-an-event-handler>
#if HAVE_CDDA
// Register CDDA handler
REGISTER_AUTOPLAY_HANDLER("cdda", "CDAudio", "Play CD-Audio");
#endif
#if HAVE_DVDNAV
// Register DVD handler
REGISTER_AUTOPLAY_HANDLER("dvd", "DVDMovie", "Play DVD movie");
#endif
#if HAVE_LIBBLURAY
// Register Blu-ray handler
REGISTER_AUTOPLAY_HANDLER("bluray", "BluRay", "Play Blu-ray movie");
#endif
// Create a shortcut in the Start Menu
// Note that this is required for SystemMediaTransportControls to detect the
// app correctly. Which is quite stupid, but it's the only way to make it work.
wchar_t *shortcut_path = w32_get_shortcut_path(log);
if (shortcut_path) {
create_shortcut(log, mpv_path, shortcut_path);
LocalFree(shortcut_path);
}
// Register uninstaller
// <https://learn.microsoft.com/windows/win32/msi/uninstall-registry-key>
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"DisplayName", L"mpv media player");
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"DisplayIcon", icon_path);
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"DisplayVersion", L"" VERSION);
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"Version", L"" VERSION);
reg_add_dwr(log, root, KEY_MPV_UNINSTALL, L"Language", 1033);
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"UninstallString", L"mpv.exe --no-config --unregister");
reg_add_dwr(log, root, KEY_MPV_UNINSTALL, L"NoModify", 1);
reg_add_dwr(log, root, KEY_MPV_UNINSTALL, L"NoRepair", 1);
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"Publisher", L"mpv");
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"HelpLink", L"https://mpv.io/manual");
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"Readme", L"https://mpv.io/community");
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"URLInfoAbout", L"https://mpv.io");
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"URLUpdateInfo", L"https://mpv.io/installation");
if (SUCCEEDED(PathCchRemoveFileSpec(mpv_path, MP_PATH_MAX)))
reg_add_str(log, root, KEY_MPV_UNINSTALL, L"InstallLocation", mpv_path);
uint64_t size = get_file_size(mpv_path);
if (size)
reg_add_dwr(log, root, KEY_MPV_UNINSTALL, L"EstimatedSize", size);
mp_msg(log, MSGL_INFO, "mpv has been successfully registered.\n");
mp_msg(log, MSGL_INFO, "Note: The mpv binary has not been copied or moved. Please keep it in the current directory.\n");
mp_msg(log, MSGL_INFO, "If you move it, simply run this process again to re-register.\n");
talloc_free(tmp);
}
static void w32_unregister(struct MPContext *mpctx)
{
struct mp_log *log = mp_log_new(NULL, mpctx->log, "win32");
HKEY root = is_admin() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
mp_msg(log, MSGL_INFO, "Unregistering mpv for %s...\n",
root == HKEY_LOCAL_MACHINE ? "all users" : "the current user");
reg_del(log, root, KEY_MPV_APP_PATH(".exe"), NULL);
reg_del(log, root, KEY_MPV_APP_PATH(".com"), NULL);
reg_del(log, root, KEY_MPV_APP, NULL);
reg_del(log, root, KEY_MPV_CAPABILITIES_APP, NULL);
reg_del(log, root, L"Software\\RegisteredApplications", MPV_NAME);
reg_del(log, root, KEY_MPV_PROG_ID("bluray"), NULL);
reg_del(log, root, KEY_MPV_PROG_ID("cdda"), NULL);
reg_del(log, root, KEY_MPV_PROG_ID("dvd"), NULL);
reg_del(log, root, KEY_MPV_PROG_ID("file"), NULL);
reg_del(log, root, KEY_MPV_PROG_ID("url"), NULL);
reg_del(log, root, KEY_AUTOPLAY L"\\Handlers\\MpvPlayCDAudioOnArrival", NULL);
reg_del(log, root, KEY_AUTOPLAY L"\\EventHandlers\\PlayCDAudioOnArrival",
L"MpvPlayCDAudioOnArrival");
reg_del(log, root, KEY_AUTOPLAY L"\\Handlers\\MpvPlayDVDMovieOnArrival", NULL);
reg_del(log, root, KEY_AUTOPLAY L"\\EventHandlers\\PlayDVDMovieOnArrival",
L"MpvPlayDVDMovieOnArrival");
reg_del(log, root, KEY_AUTOPLAY L"\\Handlers\\MpvPlayBluRayOnArrival", NULL);
reg_del(log, root, KEY_AUTOPLAY L"\\EventHandlers\\PlayBluRayOnArrival",
L"MpvPlayBluRayOnArrival");
wchar_t *shortcut_path = w32_get_shortcut_path(log);
if (shortcut_path) {
mp_msg(log, MSGL_V, "Removing shortcut: '%ls'\n", shortcut_path);
if (!DeleteFileW(shortcut_path)) {
DWORD err = GetLastError();
mp_msg(log, err == ERROR_FILE_NOT_FOUND ? MSGL_DEBUG : MSGL_ERR,
"Failed to delete shortcut: '%ls' -> %s\n", shortcut_path,
mp_LastError_to_str());
}
LocalFree(shortcut_path);
}
reg_del(log, root, KEY_MPV_UNINSTALL, NULL);
mp_msg(log, MSGL_INFO, "mpv has been successfully unregistered.\n");
mp_msg(log, MSGL_INFO, "You may now safely delete the mpv binary if desired.\n");
talloc_free(log);
}
bool mp_w32_handle_register(struct MPContext *mpctx)
{
struct w32_register_opts *opts = mpctx->opts->w32_register_opts;
if (!opts->register_opt && !opts->unregister)
return false;
if (opts->register_opt) {
w32_register(mpctx);
} else if (opts->unregister) {
w32_unregister(mpctx);
}
return true;
}

26
osdep/w32_register.h Normal file
View File

@@ -0,0 +1,26 @@
/*
* This file is part of mpv.
*
* mpv 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.
*
* mpv 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 mpv. If not, see <http://www.gnu.org/licenses/>.
*/
#pragma once
#include <stdbool.h>
struct MPContext;
// Handles the installation of mpv on Windows.
// Return true if mpv should exit.
bool mp_w32_handle_register(struct MPContext *mpctx);

View File

@@ -77,6 +77,10 @@ static const char def_config[] =
#include "osdep/win32/smtc.h"
#endif
#if HAVE_WIN32_DESKTOP
#include "osdep/w32_register.h"
#endif
#if HAVE_COCOA
#include "osdep/mac/app_bridge.h"
#endif
@@ -387,6 +391,11 @@ int mp_initialize(struct MPContext *mpctx, char **options)
if (handle_help_options(mpctx))
return 1; // help
#if HAVE_WIN32_DESKTOP
if (mp_w32_handle_register(mpctx))
return 1; // register/unregister
#endif
check_library_versions(mp_null_log, 0);
if (!mpctx->playlist->num_entries && !opts->player_idle_mode &&