Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 78 additions & 25 deletions Source/WindowsShellExtension/dllmain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
#define SHELLEXT_GUID "20669675-b281-4c4f-94fb-cb6fd3995545"
#endif // MEDIAINFO_QT

//#define MEDIAINFO_DEBUG // Uncomment to view debug printing using https://learn.microsoft.com/en-us/sysinternals/downloads/debugview
#ifdef MEDIAINFO_DEBUG
#define DebugPrintW(...) OutputDebugStringW(wil::str_printf<std::wstring>(L##__VA_ARGS__).c_str())
#else
#define DebugPrintW(...)
#endif // MEDIAINFO_DEBUG

constexpr const wchar_t* menu_entry_title = L"MediaInfo";
constexpr const wchar_t* exe_filename = L"MediaInfo.exe";

Expand Down Expand Up @@ -455,13 +462,9 @@ struct ExplorerCommandHandler : public winrt::implements<ExplorerCommandHandler,
IFACEMETHODIMP GetState(_In_opt_ IShellItemArray* items, _In_ BOOL okToBeSlow, _Out_ EXPCMDSTATE* cmdState) override {
// Provide state of File Explorer context menu entry
// Hide it if registry setting indicates that it should be disabled or file is unsupported, else it is enabled
UNREFERENCED_PARAMETER(okToBeSlow);

if (!okToBeSlow) {
*cmdState = ECS_DISABLED;
// returning E_PENDING requests that a new instance of this object be called back
// on a background thread so that it can do work that might be slow
return E_PENDING;
}
DebugPrintW("[MediaInfoShellExt] GetState called.\n");

#ifdef MEDIAINFO_QT
auto shellExtension{ RegGetBool(HKEY_CURRENT_USER, L"Software\\MediaArea.net\\MediaInfo", L"shellExtension") };
Expand All @@ -472,6 +475,7 @@ struct ExplorerCommandHandler : public winrt::implements<ExplorerCommandHandler,
if (ShellExtension.has_value() && !ShellExtension.value() && ShellExtension_Folder.has_value() && !ShellExtension_Folder.value())
#endif // MEDIAINFO_QT
{
DebugPrintW("[MediaInfoShellExt] Hidden by MediaInfo settings in registry.\n");
*cmdState = ECS_HIDDEN;
return S_OK;
}
Expand All @@ -487,12 +491,15 @@ struct ExplorerCommandHandler : public winrt::implements<ExplorerCommandHandler,
if (SUCCEEDED(items->GetItemAt(0, item.put()))) {
SFGAOF attribute{};
if (SUCCEEDED(item->GetAttributes(SFGAO_FOLDER | SFGAO_STREAM, &attribute))) {
if ((attribute & SFGAO_FOLDER) && !(attribute & SFGAO_STREAM))
if ((attribute & SFGAO_FOLDER) && !(attribute & SFGAO_STREAM)) {
DebugPrintW("[MediaInfoShellExt] Local folder.");
is_folder = true;
}
else {
wil::unique_cotaskmem_string path;
if (SUCCEEDED(item->GetDisplayName(SIGDN_FILESYSPATH, &path))) {
std::filesystem::path filepath{ path.get() };
DebugPrintW("[MediaInfoShellExt] Local file; extension: %s\n", filepath.extension().wstring().c_str());
// resolve shortcuts
if (filepath.extension().string() == ".url") {
std::string url;
Expand All @@ -511,6 +518,24 @@ struct ExplorerCommandHandler : public winrt::implements<ExplorerCommandHandler,
is_supported_extension = IsSupportedFileExtension(filepath.extension().string());
}
}
else {
// Handle MTP etc.
winrt::com_ptr<IShellItem2> pShellItem2;
if (SUCCEEDED(item->QueryInterface(IID_PPV_ARGS(&pShellItem2)))) {
wil::unique_cotaskmem_string pszExt;
HRESULT hr = pShellItem2->GetString(PKEY_FileExtension, &pszExt);
if (SUCCEEDED(hr) && pszExt && pszExt.get()[0] != L'\0') {
DebugPrintW("[MediaInfoShellExt] Non-local file; extension: %s\n", pszExt.get());
int sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, pszExt.get(), -1, nullptr, 0, nullptr, nullptr);
if (sizeNeeded > 0) {
std::string extension(sizeNeeded, 0);
WideCharToMultiByte(CP_UTF8, 0, pszExt.get(), -1, &extension[0], static_cast<int>(extension.size()), nullptr, nullptr);
extension.pop_back(); // Remove extra null-terminator for proper std::string behavior
is_supported_extension = IsSupportedFileExtension(extension);
}
}
}
}
}
}
}
Expand Down Expand Up @@ -548,6 +573,8 @@ struct ExplorerCommandHandler : public winrt::implements<ExplorerCommandHandler,
// Process items passed by File Explorer when context menu entry is invoked
UNREFERENCED_PARAMETER(bindCtx);

DebugPrintW("[MediaInfoShellExt] Invoke called.\n");

// Return if no items
if (!items)
return S_OK;
Expand Down Expand Up @@ -580,9 +607,10 @@ struct ExplorerCommandHandler : public winrt::implements<ExplorerCommandHandler,
auto result = items->GetItemAt(i, item.put());
if (SUCCEEDED(result)) {
wil::unique_cotaskmem_string path;
std::filesystem::path filepath;
result = item->GetDisplayName(SIGDN_FILESYSPATH, &path);
if (SUCCEEDED(result)) {
std::filesystem::path filepath{ path.get() };
filepath = path.get();
// Resolve shortcuts
if (filepath.extension().string() == ".url") {
std::string url;
Expand All @@ -594,21 +622,29 @@ struct ExplorerCommandHandler : public winrt::implements<ExplorerCommandHandler,
if (SUCCEEDED(ResolveIt(nullptr, filepath.wstring().c_str(), target_path, sizeof(target_path))))
filepath = target_path;
}
auto command{ wil::str_printf<std::wstring>(LR"-("%s" %s)-", module_path.c_str(), QuoteForCommandLineArg(filepath.wstring()).c_str()) };
wil::unique_process_information process_info;
STARTUPINFOW startup_info = { sizeof(startup_info) };
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
nullptr,
command.data(),
nullptr /* lpProcessAttributes */,
nullptr /* lpThreadAttributes */,
false /* bInheritHandles */,
CREATE_NO_WINDOW,
nullptr,
nullptr,
&startup_info,
&process_info));
}
else {
// It may be an MTP or other item with parsing name relative to the desktop
wil::unique_cotaskmem_string parsingName;
HRESULT hrParsing = item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &parsingName);
if (SUCCEEDED(hrParsing)) {
filepath = parsingName.get();
}
}

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JeromeMartinez What do you prefer? launch MediaInfo with empty so that user knows something gone wrong (MediaInfo usually is empty when failed to open file) or do not launch and user may wonder what happened, "did I click properly", "do I have to wait longer" etc. or if only one fail, "why I select 5 files but only 4 windows open".

Lower down with the loop for single instance, even if we do not append when fail, it will end up launching as well even if not a single file succeeds.

So far I have never seen it fail.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

launch MediaInfo with empty so that user knows something gone wrong

Is fine for me.

auto command{ wil::str_printf<std::wstring>(LR"-("%s" %s)-", module_path.c_str(), QuoteForCommandLineArg(filepath.wstring()).c_str()) };
wil::unique_process_information process_info;
STARTUPINFOW startup_info = { sizeof(startup_info) };
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
nullptr,
command.data(),
nullptr /* lpProcessAttributes */,
nullptr /* lpThreadAttributes */,
false /* bInheritHandles */,
CREATE_NO_WINDOW,
nullptr,
nullptr,
&startup_info,
&process_info));
}
}
return S_OK;
Expand All @@ -622,27 +658,43 @@ struct ExplorerCommandHandler : public winrt::implements<ExplorerCommandHandler,
auto result = items->GetItemAt(i, item.put());
if (SUCCEEDED(result)) {
wil::unique_cotaskmem_string path;
std::filesystem::path filepath;
result = item->GetDisplayName(SIGDN_FILESYSPATH, &path);
if (SUCCEEDED(result)) {
std::filesystem::path filepath{ path.get() };
// If succeeded then it is a usual local file path
// Only items that report SFGAO_FILESYSTEM have a file system path.
DebugPrintW("[MediaInfoShellExt] Processing local path: %s\n", path.get());
filepath = path.get();
// Resolve shortcuts
if (filepath.extension().string() == ".url") {
std::string url;
if (ExtractUrlFromShortcut(filepath, url))
filepath = url;
DebugPrintW("[MediaInfoShellExt] Resolved url file to: %s\n", filepath.wstring().c_str());
}
if (filepath.extension().string() == ".lnk") {
WCHAR target_path[MAX_PATH];
if (SUCCEEDED(ResolveIt(nullptr, filepath.wstring().c_str(), target_path, sizeof(target_path))))
filepath = target_path;
DebugPrintW("[MediaInfoShellExt] Resolved lnk shortcut to: %s\n", filepath.wstring().c_str());
}
}
else {
// It may be an MTP or other item with parsing name relative to the desktop
wil::unique_cotaskmem_string parsingName;
HRESULT hrParsing = item->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &parsingName);
if (SUCCEEDED(hrParsing)) {
filepath = parsingName.get();
DebugPrintW("[MediaInfoShellExt] Non-local path: %s\n", filepath.wstring().c_str());
}
// Append the item path to the existing command, adding quotes and escapes as needed
command = wil::str_printf<std::wstring>(LR"-(%s %s)-", command.c_str(), QuoteForCommandLineArg(filepath.wstring()).c_str());
}
// Append the item path to the existing command, adding quotes and escapes as needed
command = wil::str_printf<std::wstring>(LR"-(%s %s)-", command.c_str(), QuoteForCommandLineArg(filepath.wstring()).c_str());
}
}

// Invoke application using CreateProcess with the command string prepared above
DebugPrintW("[MediaInfoShellExt] Commandline to launch: %s\n", command.c_str());
wil::unique_process_information process_info;
STARTUPINFOW startup_info = { sizeof(startup_info) };
RETURN_IF_WIN32_BOOL_FALSE(CreateProcessW(
Expand Down Expand Up @@ -705,5 +757,6 @@ STDAPI DllCanUnloadNow(void) {
if (winrt::get_module_lock())
return S_FALSE;
winrt::clear_factory_cache();
DebugPrintW("[MediaInfoShellExt] DllCanUnloadNow returned S_OK.\n");
return S_OK;
}
3 changes: 3 additions & 0 deletions Source/WindowsShellExtension/pch.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,7 @@
#include <wil/stl.h>
#include <wil/win32_helpers.h>

// PROPERTYKEY
#include <propkey.h>

#endif //PCH_H
Loading