Skip to content
Permalink
Browse files
add folder support, extend long path support
  • Loading branch information
namazso committed Nov 26, 2019
1 parent c4b1273 commit 301ad5d7dfc7d43d74d5832d0bebafd669001ed0
@@ -112,6 +112,9 @@ FileHashTask::FileHashTask(const tstring& path, OpenHashTabPropPage* prop_page,
// Instead of exception, set _error because a failed file is still a finished
// file task. Finish mechanism will trigger on first block read

for (auto i = 0u; i < k_hashers_count; ++i)
_lparam_idx[i] = i;

for (auto& ctx : _hash_contexts)
mbedtls_md_init(&ctx);

@@ -86,6 +86,8 @@ class FileHashTask
int _match_state{};
bool _cancelled{};

uint8_t _lparam_idx[k_hashers_count];

public:
FileHashTask(const FileHashTask&) = delete;
FileHashTask(FileHashTask&&) = delete;
@@ -126,6 +128,13 @@ class FileHashTask
}

public:
LPARAM ToLparam(size_t hasher) const { return (LPARAM)&_lparam_idx[hasher]; }
static std::pair<FileHashTask*, size_t> FromLparam(LPARAM lparam)
{
const auto lp = (const std::uint8_t*)lparam;
return { CONTAINING_RECORD(lp - *lp, FileHashTask, _lparam_idx), *lp };
}

DWORD GetError() const { return _error; }
HANDLE GetHandle() const { return _handle; }
const hash_results_t& GetHashResult() const { return _hash_results; }
@@ -33,8 +33,9 @@ inline void SetTextFromTable(HWND hwnd, int control, UINT string_id)
SetDlgItemText(hwnd, control, utl::GetString(string_id).c_str());
};

OpenHashTabPropPage::OpenHashTabPropPage(std::list<tstring> files)
OpenHashTabPropPage::OpenHashTabPropPage(std::list<tstring> files, tstring base)
: _files(std::move(files))
, _base(std::move(base))
{
}

@@ -48,6 +49,11 @@ OpenHashTabPropPage::~OpenHashTabPropPage()

INT_PTR OpenHashTabPropPage::CustomDrawListView(LPARAM lparam, HWND list) const
{
// No hash to compare to - system colors
// Error processing file - system bg, red text
// Hash mismatch - red bg, white text for all
// Hash matches - green bg, white text for algo matching

const auto lplvcd = (LPNMLVCUSTOMDRAW)lparam;

switch (lplvcd->nmcd.dwDrawStage)
@@ -62,10 +68,6 @@ INT_PTR OpenHashTabPropPage::CustomDrawListView(LPARAM lparam, HWND list) const
{
switch (lplvcd->iSubItem)
{
// No hash to compare to - system colors
// Error processing file - system bg, red text
// Hash mismatch - red bg, white text for all
// Hash matches - green bg, white text for algo matching
case ColIndex_Hash:
{
LVITEM lvitem
@@ -74,7 +76,8 @@ INT_PTR OpenHashTabPropPage::CustomDrawListView(LPARAM lparam, HWND list) const
(int)lplvcd->nmcd.dwItemSpec
};
ListView_GetItem(list, &lvitem);
const auto file = (FileHashTask*)lvitem.lParam;
const auto file_hash = FileHashTask::FromLparam(lvitem.lParam);
const auto file = file_hash.first;
const auto match = file->GetMatchState();
if(file->GetError() != ERROR_SUCCESS)
{
@@ -90,10 +93,7 @@ INT_PTR OpenHashTabPropPage::CustomDrawListView(LPARAM lparam, HWND list) const
}
else // Match
{
TCHAR text[256];
ListView_GetItemText(list, (int)lplvcd->nmcd.dwItemSpec, ColIndex_Algorithm, text, (int)std::size(text));
const auto comp_alg = k_hashers_name[match];
if (0 == _tcscmp(comp_alg, text))
if((size_t)match == file_hash.second)
{
lplvcd->clrText = k_color_match_fg;
lplvcd->clrTextBk = k_color_match_bg;
@@ -209,7 +209,7 @@ INT_PTR OpenHashTabPropPage::DlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
}
else
{
TCHAR name[4097];
TCHAR name[PATHCCH_MAX_CCH];
ListView_GetItemText(phdr->hwndFrom, lvhtinfo.iItem, ColIndex_Filename, name, (int)std::size(name));
utl::SetClipboardText(hwnd, (tstring{ hash } +_T(" *") + name).c_str());
}
@@ -296,14 +296,29 @@ INT_PTR OpenHashTabPropPage::DlgProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM
const auto sel = ComboBox_GetCurSel(GetDlgItem(hwnd, IDC_COMBO_EXPORT));
if (sel >= 0 && sel < k_hashers_count)
{
const auto ext = tstring{ _T(".") } +hasher_get_extension_search_string(k_hashers[sel]);
// TODO: relativize sumfile contents to save path.
// This may sound trivial at first, but we can't use PathRelativeToPath because it doesn't support long paths.

const auto ext = tstring{ _T(".") } + hasher_get_extension_search_string(k_hashers[sel]);
const auto& file = *_files.begin();
const auto file_path = file.c_str();
const auto file_name = (LPCTSTR)PathFindFileName(file_path);
const auto dir = tstring{ file_path, file_name };
const auto name = _files.size() == 1 ? (tstring{ file_name } + ext) : ext;
const auto content = GetSumfileAsString((size_t)sel);
utl::SaveMemoryAsFile(hwnd, content.c_str(), content.size(), dir.c_str(), name.c_str());
const auto sumfile_path = utl::SaveDialog(hwnd, _base.c_str(), name.c_str());
if(!sumfile_path.empty())
{
const auto err = utl::SaveMemoryAsFile(sumfile_path.c_str(), content.c_str(), content.size());
if(err != ERROR_SUCCESS)
utl::FormattedMessageBox(
hwnd,
_T("Error"),
MB_ICONERROR | MB_OK,
_T("utl::SaveMemoryAsFile returned with error: %08X"),
err
);
}
}
break;
}
@@ -342,15 +357,15 @@ void OpenHashTabPropPage::AddFiles()

const auto sumfile_path = file.c_str();
const auto sumfile_name = (LPCTSTR)PathFindFileName(sumfile_path);
const auto base_path = tstring{ sumfile_path, sumfile_name };
const auto sumfile_base_path = tstring{ sumfile_path, sumfile_name };
for(auto& filesum : fsl)
{
// we disallow no filename in sumfile as main file
// we disallow no filename when sumfile is main file
if (filesum.first.empty())
continue;

const auto path = base_path + utl::UTF8ToTString(filesum.first.c_str());
AddFile(path, filesum.second, utl::UTF8ToTString(filesum.first.c_str()));
const auto path = sumfile_base_path + utl::UTF8ToTString(filesum.first.c_str());
AddFile(path, filesum.second);
}

// fall through - let it calculate the sumfile's sum, in case the user needs that
@@ -365,14 +380,31 @@ void OpenHashTabPropPage::AddFiles()
}
}

void OpenHashTabPropPage::AddFile(const tstring& path, const std::vector<std::uint8_t>& expected_hash, tstring display_name)
/*void OpenHashTabPropPage::AddFile(const tstring& path, const std::vector<std::uint8_t>& expected_hash, tstring display_name)
{
if (display_name.empty())
display_name = tstring{ PathFindFileName(path.c_str()) };
const auto task = new FileHashTask(path, this, std::move(display_name), expected_hash);
_file_tasks.push_back(task);
++_files_being_processed;
++_files_not_finished;
}*/

void OpenHashTabPropPage::AddFile(const tstring& path, const std::vector<std::uint8_t>& expected_hash)
{
auto dispname = utl::CanonicalizePath(path);

// If path looks like _base + filename use filename as displayname, else the canonical name.
// Optimally you'd use PathRelativePathTo for this, however that function not only doesn't support long paths, it also
// doesn't have a PathCch alternative on Win8+. Additionally, seeing ".." and similar in the Name part could be confusing
// to users, so printing full path instead is probably a better idea anyways.
if(dispname.size() >= _base.size())
if(std::equal(begin(_base), end(_base), begin(dispname)))
dispname = dispname.substr(_base.size());

const auto task = new FileHashTask(path, this, std::move(dispname), expected_hash);
_file_tasks.push_back(task);
++_files_not_finished;
}

std::vector<std::uint8_t> OpenHashTabPropPage::TryGetExpectedSumForFile(const tstring& path)
@@ -449,15 +481,15 @@ void OpenHashTabPropPage::Cancel()
for (auto file : _file_tasks)
file->SetCancelled();

while (_files_being_processed > 0)
while (_files_not_finished > 0)
_mm_pause();
}

void OpenHashTabPropPage::FileCompletionCallback(FileHashTask* file)
{
std::lock_guard<std::mutex> guard(_list_view_lock);

const auto files_being_processed = --_files_being_processed;
const auto files_being_processed = --_files_not_finished;

if (_hwnd_deleted)
return;
@@ -466,7 +498,7 @@ void OpenHashTabPropPage::FileCompletionCallback(FileHashTask* file)
if (!list)
return;

const auto add_item = [list](LPCTSTR filename, LPCTSTR algorithm, LPCTSTR hash, FileHashTask* file)
const auto add_item = [list](LPCTSTR filename, LPCTSTR algorithm, LPCTSTR hash, LPARAM lparam)
{
LVITEM lvitem
{
@@ -477,7 +509,7 @@ void OpenHashTabPropPage::FileCompletionCallback(FileHashTask* file)
0,
(LPTSTR)_T("")
};
lvitem.lParam = (LPARAM)file;
lvitem.lParam = lparam;
const auto item = ListView_InsertItem(list, &lvitem);
ListView_SetItemText(list, item, ColIndex_Filename, (LPTSTR)filename);
ListView_SetItemText(list, item, ColIndex_Algorithm, (PTSTR)algorithm);
@@ -493,7 +525,7 @@ void OpenHashTabPropPage::FileCompletionCallback(FileHashTask* file)
auto& result = results[i];
TCHAR hash_str[MBEDTLS_MD_MAX_SIZE * 2 + 1];
utl::HashBytesToString(hash_str, result);
add_item(file->GetDisplayName().c_str(), k_hashers_name[i], hash_str, file);
add_item(file->GetDisplayName().c_str(), k_hashers_name[i], hash_str, file->ToLparam(i));
}
}
else
@@ -508,7 +540,7 @@ void OpenHashTabPropPage::FileCompletionCallback(FileHashTask* file)
_ARRAYSIZE(buf),
nullptr
);
add_item(file->GetDisplayName().c_str(), utl::GetString(IDS_ERROR).c_str(), buf, file);
add_item(file->GetDisplayName().c_str(), utl::GetString(IDS_ERROR).c_str(), buf, file->ToLparam(0));
}

if(files_being_processed == 0)
@@ -22,6 +22,8 @@ class OpenHashTabPropPage
{
std::list<tstring> _files;

tstring _base;

HWND _hwnd{};

std::mutex _list_view_lock;
@@ -30,7 +32,7 @@ class OpenHashTabPropPage

std::atomic<unsigned> _references{};

std::atomic<unsigned> _files_being_processed{};
std::atomic<unsigned> _files_not_finished{};

bool _is_sumfile{ false };

@@ -50,13 +52,13 @@ class OpenHashTabPropPage
std::string GetSumfileAsString(size_t hasher);

void AddFiles();
void AddFile(const tstring& path, const std::vector<std::uint8_t>& expected_hash = {}, tstring display_name = {});
void AddFile(const tstring& path, const std::vector<std::uint8_t>& expected_hash = {});
static std::vector<std::uint8_t> TryGetExpectedSumForFile(const tstring& path);
void ProcessFiles();
void Cancel();

public:
OpenHashTabPropPage(std::list<tstring> files);
OpenHashTabPropPage(std::list<tstring> files, tstring base);

UINT Create(HWND hwnd, LPPROPSHEETPAGE ppsp) { return 1; }

@@ -39,7 +39,7 @@ HRESULT STDMETHODCALLTYPE COpenHashTabShlExt::Initialize(
};
InitCommonControlsEx(&iccex);

// Read the list of folders from the data object. They're stored in HDROP
// Read the list of folders from the data object. They're stored in HDROP
// form, so just get the HDROP handle and then use the drag 'n' drop APIs
// on it.
FORMATETC etc
@@ -73,21 +73,68 @@ HRESULT STDMETHODCALLTYPE COpenHashTabShlExt::Initialize(

for (auto i = 0u; i < file_count; i++)
{
// NT paths can be 32k long, plus one for null terminator
TCHAR file_name[0x8001];
TCHAR file_name[PATHCCH_MAX_CCH];

// Get the next filename.
if (0 == DragQueryFile(drop, i, file_name, (UINT)std::size(file_name)))
continue;

// Skip directories. TODO: Maybe a setting entry for recursing into them?
if (PathIsDirectory(file_name))
continue;

// Add the filename to our list of files to act on.
_files.emplace_back(file_name);
}

if (_files.empty())
return E_FAIL;

const auto& shortest = std::min_element(
begin(_files),
end(_files),
[](const tstring& a, const tstring& b)
{
return a.size() < b.size();
}
);

const auto pb = shortest->c_str();

// if PathFindFileName it returns pb, making base path "". This is intended.
_base = tstring{ pb, (LPCWSTR)PathFindFileName(pb) };

// for each directory in _files remove it from the list add it's content to the end.
// since we push elements to the end end iterator is intentionally not saved.
for(auto it = begin(_files); it != end(_files);)
{
if (PathIsDirectory(it->c_str()))
{
WIN32_FIND_DATA find_data;
const auto find_handle = FindFirstFile(utl::MakePathLongCompatible(*it + _T("\\*")).c_str(), &find_data);

DWORD error;

if (find_handle != INVALID_HANDLE_VALUE)
{
do
_files.push_back(*it + _T("\\") + find_data.cFileName);
while (FindNextFile(find_handle, &find_data) != 0);
error = GetLastError();
FindClose(find_handle);
}
else
{
error = GetLastError();
}

// TODO: maybe handle error differently?
(void)error;

_files.erase(it++);
}
else
{
++it;
}
}

// Release resources.
GlobalUnlock(stg.hGlobal);
ReleaseStgMedium(&stg);
@@ -123,7 +170,7 @@ HRESULT STDMETHODCALLTYPE COpenHashTabShlExt::AddPages(
psp.pszTitle = tab_name.c_str();
psp.pcRefParent = (UINT*)&_AtlModule.m_nLockCnt;

const auto page = new OpenHashTabPropPage(_files);
const auto page = new OpenHashTabPropPage(_files, _base);
const auto hpage = utl::MakePropPageWrapper(psp, page);

if (hpage)
@@ -38,6 +38,7 @@ class ATL_NO_VTABLE COpenHashTabShlExt :
{
protected:
std::list<tstring> _files;
tstring _base;
public:
COpenHashTabShlExt() = default;
@@ -10,7 +10,7 @@ HKCR
}
}
}
NoRemove *
NoRemove AllFileSystemObjects
{
NoRemove shellex
{
@@ -43,6 +43,7 @@
#include <CommCtrl.h>
#include <windowsx.h>
#include <VersionHelpers.h>
#include <pathcch.h>

// STL
#include <cstdint>

0 comments on commit 301ad5d

Please sign in to comment.