mirror of
https://github.com/QL-Win/QuickLook.git
synced 2026-05-08 02:09:51 +08:00
Add IDM support (Internet Download Manager)
This commit is contained in:
@@ -21,6 +21,7 @@
|
||||
#include "WoW64HookHelper.h"
|
||||
#include "DOpus.h"
|
||||
#include "MultiCommander.h"
|
||||
#include "IDMan.h"
|
||||
|
||||
#define EXPORT extern "C" __declspec(dllexport)
|
||||
|
||||
|
||||
@@ -0,0 +1,268 @@
|
||||
// Copyright © 2017-2026 QL-Win Contributors
|
||||
//
|
||||
// This file is part of QuickLook program.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "IDMan.h"
|
||||
#include <UIAutomation.h>
|
||||
|
||||
#pragma comment(lib, "UIAutomationCore.lib")
|
||||
|
||||
void IDMan::GetSelected(PWCHAR buffer)
|
||||
{
|
||||
// Step 1: Get the selected item name from the IDM list via UIAutomation
|
||||
WCHAR selectedName[MAX_PATH] = { L'\0' };
|
||||
if (!GetSelectedItemName(selectedName))
|
||||
return;
|
||||
|
||||
// Step 2: Resolve the file path from the IDM registry
|
||||
GetFilePath(selectedName, buffer);
|
||||
}
|
||||
|
||||
bool IDMan::GetSelectedItemName(PWCHAR nameBuffer)
|
||||
{
|
||||
HWND hwnd = GetForegroundWindow();
|
||||
if (hwnd == nullptr)
|
||||
return false;
|
||||
|
||||
IUIAutomation* pAutomation = nullptr;
|
||||
HRESULT hr = CoCreateInstance(
|
||||
__uuidof(CUIAutomation8),
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
__uuidof(IUIAutomation),
|
||||
reinterpret_cast<void**>(&pAutomation));
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
// Fallback to older interface
|
||||
hr = CoCreateInstance(
|
||||
__uuidof(CUIAutomation),
|
||||
nullptr,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
__uuidof(IUIAutomation),
|
||||
reinterpret_cast<void**>(&pAutomation));
|
||||
if (FAILED(hr))
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get UIAutomation element from the IDM window handle
|
||||
IUIAutomationElement* pIDMWindow = nullptr;
|
||||
hr = pAutomation->ElementFromHandle(hwnd, &pIDMWindow);
|
||||
if (FAILED(hr) || pIDMWindow == nullptr)
|
||||
{
|
||||
pAutomation->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Build OR condition: DataGrid || List
|
||||
IUIAutomationCondition* pDataGridCond = nullptr;
|
||||
VARIANT varDataGrid;
|
||||
VariantInit(&varDataGrid);
|
||||
varDataGrid.vt = VT_I4;
|
||||
varDataGrid.intVal = UIA_DataGridControlTypeId;
|
||||
pAutomation->CreatePropertyCondition(UIA_ControlTypePropertyId, varDataGrid, &pDataGridCond);
|
||||
|
||||
IUIAutomationCondition* pListCond = nullptr;
|
||||
VARIANT varList;
|
||||
VariantInit(&varList);
|
||||
varList.vt = VT_I4;
|
||||
varList.intVal = UIA_ListControlTypeId;
|
||||
pAutomation->CreatePropertyCondition(UIA_ControlTypePropertyId, varList, &pListCond);
|
||||
|
||||
IUIAutomationCondition* pOrCond = nullptr;
|
||||
pAutomation->CreateOrCondition(pDataGridCond, pListCond, &pOrCond);
|
||||
pDataGridCond->Release();
|
||||
pListCond->Release();
|
||||
|
||||
// Find the list view (DataGrid or List) inside the IDM window
|
||||
IUIAutomationElement* pListView = nullptr;
|
||||
pIDMWindow->FindFirst(TreeScope_Descendants, pOrCond, &pListView);
|
||||
pOrCond->Release();
|
||||
pIDMWindow->Release();
|
||||
|
||||
if (pListView == nullptr)
|
||||
{
|
||||
pAutomation->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
// Enumerate all children and find the selected item
|
||||
IUIAutomationCondition* pTrueCond = nullptr;
|
||||
pAutomation->CreateTrueCondition(&pTrueCond);
|
||||
|
||||
IUIAutomationElementArray* pChildren = nullptr;
|
||||
pListView->FindAll(TreeScope_Children, pTrueCond, &pChildren);
|
||||
pTrueCond->Release();
|
||||
pListView->Release();
|
||||
|
||||
if (pChildren == nullptr)
|
||||
{
|
||||
pAutomation->Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
int count = 0;
|
||||
pChildren->get_Length(&count);
|
||||
|
||||
for (int i = 0; i < count && !found; i++)
|
||||
{
|
||||
IUIAutomationElement* pItem = nullptr;
|
||||
if (SUCCEEDED(pChildren->GetElement(i, &pItem)) && pItem != nullptr)
|
||||
{
|
||||
IUIAutomationSelectionItemPattern* pSelPattern = nullptr;
|
||||
if (SUCCEEDED(pItem->GetCurrentPatternAs(
|
||||
UIA_SelectionItemPatternId,
|
||||
__uuidof(IUIAutomationSelectionItemPattern),
|
||||
reinterpret_cast<void**>(&pSelPattern))) &&
|
||||
pSelPattern != nullptr)
|
||||
{
|
||||
BOOL isSelected = FALSE;
|
||||
pSelPattern->get_CurrentIsSelected(&isSelected);
|
||||
pSelPattern->Release();
|
||||
|
||||
if (isSelected)
|
||||
{
|
||||
BSTR name = nullptr;
|
||||
if (SUCCEEDED(pItem->get_CurrentName(&name)) && name != nullptr)
|
||||
{
|
||||
wcscpy_s(nameBuffer, MAX_PATH, name);
|
||||
SysFreeString(name);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
pItem->Release();
|
||||
}
|
||||
}
|
||||
|
||||
pChildren->Release();
|
||||
pAutomation->Release();
|
||||
return found;
|
||||
}
|
||||
|
||||
void IDMan::GetFilePath(PCWSTR name, PWCHAR buffer)
|
||||
{
|
||||
// Extract the file extension (e.g. ".mp4")
|
||||
WCHAR ext[64] = { L'\0' };
|
||||
const WCHAR* dot = wcsrchr(name, L'.');
|
||||
if (dot != nullptr)
|
||||
wcscpy_s(ext, dot);
|
||||
|
||||
// Read the default download path from LocalPathW (stored as raw Unicode bytes)
|
||||
WCHAR defaultPath[MAX_PATH] = { L'\0' };
|
||||
{
|
||||
HKEY hBase = nullptr;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\DownloadManager", 0, KEY_READ, &hBase) == ERROR_SUCCESS)
|
||||
{
|
||||
BYTE data[MAX_PATH * sizeof(WCHAR)] = {};
|
||||
DWORD dataSize = sizeof(data);
|
||||
DWORD dataType = 0;
|
||||
if (RegQueryValueExW(hBase, L"LocalPathW", nullptr, &dataType, data, &dataSize) == ERROR_SUCCESS)
|
||||
{
|
||||
if (dataType == REG_BINARY)
|
||||
wcsncpy_s(defaultPath, MAX_PATH, reinterpret_cast<PWSTR>(data), dataSize / sizeof(WCHAR));
|
||||
else if (dataType == REG_SZ)
|
||||
wcsncpy_s(defaultPath, MAX_PATH, reinterpret_cast<PWSTR>(data), MAX_PATH - 1);
|
||||
}
|
||||
RegCloseKey(hBase);
|
||||
}
|
||||
}
|
||||
|
||||
// Try to match extension against FoldersTree entries
|
||||
if (ext[0] != L'\0')
|
||||
{
|
||||
HKEY hFolders = nullptr;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\DownloadManager\\FoldersTree", 0, KEY_READ, &hFolders) == ERROR_SUCCESS)
|
||||
{
|
||||
WCHAR subKeyName[MAX_PATH] = { L'\0' };
|
||||
DWORD subKeyIdx = 0;
|
||||
while (RegEnumKeyW(hFolders, subKeyIdx++, subKeyName, MAX_PATH) == ERROR_SUCCESS)
|
||||
{
|
||||
HKEY hSub = nullptr;
|
||||
if (RegOpenKeyExW(hFolders, subKeyName, 0, KEY_READ, &hSub) == ERROR_SUCCESS)
|
||||
{
|
||||
WCHAR maskValue[512] = { L'\0' };
|
||||
DWORD maskSize = sizeof(maskValue);
|
||||
if (RegQueryValueExW(hSub, L"mask", nullptr, nullptr, reinterpret_cast<LPBYTE>(maskValue), &maskSize) == ERROR_SUCCESS)
|
||||
{
|
||||
// maskValue is space-delimited list of extensions without dots (e.g. "mp4 mkv avi")
|
||||
WCHAR* ctx = nullptr;
|
||||
WCHAR* tok = wcstok_s(maskValue, L" ", &ctx);
|
||||
while (tok != nullptr)
|
||||
{
|
||||
WCHAR tokExt[64] = L".";
|
||||
wcscat_s(tokExt, tok);
|
||||
if (_wcsicmp(tokExt, ext) == 0)
|
||||
{
|
||||
// Compose: defaultPath\subKeyName\filename
|
||||
swprintf_s(buffer, MAX_PATH_EX, L"%s\\%s\\%s", defaultPath, subKeyName, name);
|
||||
RegCloseKey(hSub);
|
||||
RegCloseKey(hFolders);
|
||||
return;
|
||||
}
|
||||
tok = wcstok_s(nullptr, L" ", &ctx);
|
||||
}
|
||||
}
|
||||
RegCloseKey(hSub);
|
||||
}
|
||||
}
|
||||
RegCloseKey(hFolders);
|
||||
}
|
||||
|
||||
// Extension not in FoldersTree — use the default download folder
|
||||
if (defaultPath[0] != L'\0')
|
||||
{
|
||||
swprintf_s(buffer, MAX_PATH_EX, L"%s\\%s", defaultPath, name);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback: search individual download entries in the registry for FR_FNCD match
|
||||
HKEY hBase = nullptr;
|
||||
if (RegOpenKeyExW(HKEY_CURRENT_USER, L"Software\\DownloadManager", 0, KEY_READ, &hBase) == ERROR_SUCCESS)
|
||||
{
|
||||
WCHAR subKeyName[MAX_PATH] = { L'\0' };
|
||||
DWORD subKeyIdx = 0;
|
||||
while (RegEnumKeyW(hBase, subKeyIdx++, subKeyName, MAX_PATH) == ERROR_SUCCESS)
|
||||
{
|
||||
HKEY hSub = nullptr;
|
||||
if (RegOpenKeyExW(hBase, subKeyName, 0, KEY_READ, &hSub) == ERROR_SUCCESS)
|
||||
{
|
||||
WCHAR frFncd[MAX_PATH] = { L'\0' };
|
||||
DWORD frSize = sizeof(frFncd);
|
||||
if (RegQueryValueExW(hSub, L"FR_FNCD", nullptr, nullptr, reinterpret_cast<LPBYTE>(frFncd), &frSize) == ERROR_SUCCESS)
|
||||
{
|
||||
if (_wcsicmp(frFncd, name) == 0)
|
||||
{
|
||||
WCHAR localFileName[MAX_PATH_EX] = { L'\0' };
|
||||
DWORD lfSize = sizeof(localFileName);
|
||||
if (RegQueryValueExW(hSub, L"LocalFileName", nullptr, nullptr, reinterpret_cast<LPBYTE>(localFileName), &lfSize) == ERROR_SUCCESS)
|
||||
{
|
||||
wcscpy_s(buffer, MAX_PATH_EX, localFileName);
|
||||
RegCloseKey(hSub);
|
||||
RegCloseKey(hBase);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
RegCloseKey(hSub);
|
||||
}
|
||||
}
|
||||
RegCloseKey(hBase);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// Copyright © 2017-2026 QL-Win Contributors
|
||||
//
|
||||
// This file is part of QuickLook program.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program 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 General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "stdafx.h"
|
||||
|
||||
class IDMan
|
||||
{
|
||||
public:
|
||||
static void GetSelected(PWCHAR buffer);
|
||||
|
||||
private:
|
||||
static bool GetSelectedItemName(PWCHAR nameBuffer);
|
||||
static void GetFilePath(PCWSTR name, PWCHAR buffer);
|
||||
};
|
||||
@@ -218,6 +218,7 @@
|
||||
<ClInclude Include="DOpus.h" />
|
||||
<ClInclude Include="Everything.h" />
|
||||
<ClInclude Include="HelperMethods.h" />
|
||||
<ClInclude Include="IDMan.h" />
|
||||
<ClInclude Include="MultiCommander.h" />
|
||||
<ClInclude Include="rapidxml.hpp" />
|
||||
<ClInclude Include="WoW64HookHelper.h" />
|
||||
@@ -251,6 +252,7 @@
|
||||
<ClCompile Include="Everything.cpp" />
|
||||
<ClCompile Include="HelperMethods.cpp" />
|
||||
<ClCompile Include="DllExport.cpp" />
|
||||
<ClCompile Include="IDMan.cpp" />
|
||||
<ClCompile Include="MultiCommander.cpp" />
|
||||
<ClCompile Include="Shell32.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "Everything.h"
|
||||
#include "DOpus.h"
|
||||
#include "MultiCommander.h"
|
||||
#include "IDMan.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
@@ -65,6 +66,13 @@ Shell32::FocusedWindowType Shell32::GetFocusedWindowType()
|
||||
}
|
||||
if (wcscmp(classBuffer, L"#32770") == 0)
|
||||
{
|
||||
WCHAR titleBuffer[512] = { L'\0' };
|
||||
GetWindowText(hwndfg, titleBuffer, 512);
|
||||
if (wcsncmp(titleBuffer, L"Internet Download Manager", 25) == 0)
|
||||
{
|
||||
return IDM;
|
||||
}
|
||||
|
||||
if (FindWindowEx(hwndfg, nullptr, L"DUIViewWndClassName", nullptr) != nullptr)
|
||||
{
|
||||
if (!HelperMethods::IsExplorerSearchBoxFocused())
|
||||
@@ -99,6 +107,9 @@ void Shell32::GetCurrentSelection(PWCHAR buffer)
|
||||
case MULTICOMMANDER:
|
||||
MultiCommander::GetSelected(buffer);
|
||||
break;
|
||||
case IDM:
|
||||
IDMan::GetSelected(buffer);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -31,6 +31,7 @@ public:
|
||||
EVERYTHING,
|
||||
DOPUS,
|
||||
MULTICOMMANDER,
|
||||
IDM,
|
||||
};
|
||||
|
||||
static FocusedWindowType GetFocusedWindowType();
|
||||
|
||||
@@ -52,6 +52,7 @@
|
||||
<ClCompile Include="..\QuickLook.Native32\DOpus.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\Everything.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\HelperMethods.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\IDMan.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\MultiCommander.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\Shell32.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\stdafx.cpp">
|
||||
|
||||
@@ -38,6 +38,7 @@
|
||||
<ClCompile Include="..\QuickLook.Native32\DOpus.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\Everything.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\HelperMethods.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\IDMan.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\MultiCommander.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\Shell32.cpp" />
|
||||
<ClCompile Include="..\QuickLook.Native32\stdafx.cpp">
|
||||
|
||||
Reference in New Issue
Block a user