Add IDM support (Internet Download Manager)

This commit is contained in:
ema
2026-04-22 23:12:49 +08:00
parent 652a312cb6
commit 81b5828229
9 changed files with 317 additions and 1 deletions
@@ -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">