Files
hfiref0x 61848a3234 2.1.0
+ Centralize and modernize plugin subsystem definitions and fix related bugs, remove duplicate definitions.
+ Internal rearrangement.
2026-03-09 13:55:59 +07:00

1102 lines
23 KiB
C

/*******************************************************************************
*
* (C) COPYRIGHT AUTHORS, 2020 - 2026
*
* TITLE: UTILS.C
*
* VERSION: 1.21
*
* DATE: 07 Mar 2026
*
* Shared plugins runtime support functions and prototypes.
*
* THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
* ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED
* TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
* PARTICULAR PURPOSE.
*
*******************************************************************************/
#include "utils.h"
/*
* supSetWaitCursor
*
* Purpose:
*
* Sets cursor state.
*
*/
VOID supSetWaitCursor(
_In_ BOOL fSet
)
{
HCURSOR h = LoadCursor(NULL, fSet ? IDC_WAIT : IDC_ARROW);
if (h) {
SetCursor(h);
}
}
/*
* supMapSection
*
* Purpose:
*
* Return pointer to section mapped view.
*
*/
NTSTATUS supMapSection(
_In_ HANDLE SectionHandle,
_Out_ PVOID* BaseAddress,
_Out_ SIZE_T* ViewSize
)
{
NTSTATUS ntStatus;
SECTION_BASIC_INFORMATION sbi;
SIZE_T bytesReturned;
*BaseAddress = NULL;
*ViewSize = 0;
__try {
//
// Check if this is image mapped file.
//
ntStatus = NtQuerySection(SectionHandle,
SectionBasicInformation,
(PVOID)&sbi,
sizeof(SECTION_BASIC_INFORMATION),
&bytesReturned);
if (!NT_SUCCESS(ntStatus))
__leave;
if (!((sbi.AllocationAttributes & SEC_IMAGE) &&
(sbi.AllocationAttributes & SEC_FILE)))
{
ntStatus = STATUS_NOT_SUPPORTED;
__leave;
}
ntStatus = NtMapViewOfSection(SectionHandle,
NtCurrentProcess(),
BaseAddress,
0,
0,
NULL,
ViewSize,
ViewUnmap,
0,
PAGE_READONLY);
}
__finally {
if (AbnormalTermination())
ntStatus = STATUS_ACCESS_VIOLATION;
}
return ntStatus;
}
/*
* supSaveDialogExecute
*
* Purpose:
*
* Display SaveDialog.
*
*/
BOOL supSaveDialogExecute(
_In_ HWND OwnerWindow,
_Inout_ LPWSTR SaveFileName,
_In_ LPWSTR lpDialogFilter
)
{
OPENFILENAME tag1;
RtlSecureZeroMemory(&tag1, sizeof(OPENFILENAME));
tag1.lStructSize = sizeof(OPENFILENAME);
tag1.hwndOwner = OwnerWindow;
tag1.lpstrFilter = lpDialogFilter;
tag1.lpstrFile = SaveFileName;
tag1.nMaxFile = MAX_PATH;
tag1.lpstrInitialDir = NULL;
tag1.Flags = OFN_EXPLORER | OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT;
return GetSaveFileName(&tag1);
}
size_t supxEscStrlen(wchar_t* s)
{
size_t result, quoteCount;
wchar_t* s0;
if (s == NULL)
return 0;
s0 = s;
result = 2;
quoteCount = 0;
while (*s) {
if (*s == L'"')
++quoteCount;
++s;
}
return result + (s - s0) + quoteCount;
}
wchar_t* supxEscStrcpy(wchar_t* dst, wchar_t* src)
{
if (dst == NULL || src == NULL)
return dst;
*(dst++) = L'"';
while (*src != L'\0') {
if (*src == L'"') {
*(dst++) = L'"';
*(dst++) = L'"';
++src;
}
else {
*(dst++) = *src;
++src;
}
}
*(dst++) = L'"';
*dst = L'\0';
return dst;
}
/*
* supxListViewExportCSV
*
* Purpose:
*
* Export listview entries into file in csv format.
*
*/
BOOL supxListViewExportCSV(
_In_ HWND List,
_In_ PWCHAR FileName)
{
HWND hdr;
int pass, i, c, col_count, icount;
HDITEM ih;
LVITEM lvi;
PWCHAR text, buffer0, buffer;
BOOL result;
SIZE_T total_length, field_length;
DWORD iobytes;
HANDLE f;
WORD bom;
if (!List || !FileName)
return FALSE;
hdr = ListView_GetHeader(List);
if (!hdr)
return FALSE;
col_count = Header_GetItemCount(hdr);
if (col_count <= 0)
return FALSE;
icount = 1 + ListView_GetItemCount(List);
text = (PWCHAR)ntsupVirtualAlloc(32768 * sizeof(WCHAR));
if (!text)
return FALSE;
buffer0 = NULL;
buffer = NULL;
result = FALSE;
RtlZeroMemory(&ih, sizeof(HDITEM));
RtlZeroMemory(&lvi, sizeof(LVITEM));
ih.pszText = lvi.pszText = text;
ih.cchTextMax = lvi.cchTextMax = 32767;
for (pass = 0; pass < 2; ++pass) {
total_length = 0;
for (i = 0; i < icount; ++i) {
for (c = 0; c < col_count; ++c) {
text[0] = L'\0';
if (i == 0) {
ih.mask = HDI_TEXT | HDI_ORDER;
ih.iOrder = c;
Header_GetItem(hdr, c, &ih);
}
else {
lvi.mask = LVIF_TEXT;
lvi.iItem = i - 1;
lvi.iSubItem = c;
ListView_GetItem(List, &lvi);
}
field_length = supxEscStrlen(text);
total_length += field_length;
if (buffer) {
buffer = supxEscStrcpy(buffer, text);
}
if (c != col_count - 1) {
total_length += 1;
if (buffer) {
*(buffer++) = L',';
}
}
else {
total_length += 2;
if (buffer) {
*(buffer++) = L'\r';
*(buffer++) = L'\n';
}
}
}
}
if (buffer0 == NULL) {
buffer0 = (PWCHAR)ntsupVirtualAlloc((1 + total_length) * sizeof(WCHAR));
if (!buffer0)
break;
buffer = buffer0;
}
else {
f = CreateFile(FileName,
GENERIC_WRITE | SYNCHRONIZE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (f != INVALID_HANDLE_VALUE) {
bom = 0xFEFF;
if (WriteFile(f, &bom, sizeof(WORD), &iobytes, NULL) &&
iobytes == sizeof(WORD) &&
WriteFile(f, buffer0, (DWORD)(total_length * sizeof(WCHAR)), &iobytes, NULL) &&
iobytes == (DWORD)(total_length * sizeof(WCHAR)))
{
result = TRUE;
}
CloseHandle(f);
result = TRUE;
}
ntsupVirtualFree(buffer0);
buffer0 = NULL;
}
}
if (buffer0)
ntsupVirtualFree(buffer0);
ntsupVirtualFree(text);
return result;
}
/*
* supListViewExportToFile
*
* Purpose:
*
* Export listview contents to the specified file.
*
*/
BOOL supListViewExportToFile(
_In_ LPWSTR FileName,
_In_ HWND WindowHandle,
_In_ HWND ListView,
_In_ LPWSTR FileFilter
)
{
BOOL bResult = FALSE;
WCHAR szExportFileName[MAX_PATH + 1];
RtlSecureZeroMemory(&szExportFileName, sizeof(szExportFileName));
_strcpy(szExportFileName, FileName);
if (supSaveDialogExecute(WindowHandle,
(LPWSTR)&szExportFileName,
FileFilter))
{
SetCapture(WindowHandle);
supSetWaitCursor(TRUE);
bResult = supxListViewExportCSV(ListView, szExportFileName);
supSetWaitCursor(FALSE);
ReleaseCapture();
}
return bResult;
}
/*
* supStatusBarSetText
*
* Purpose:
*
* Display status in status bar part.
*
*/
VOID supStatusBarSetText(
_In_ HWND hwndStatusBar,
_In_ WPARAM partIndex,
_In_ LPWSTR lpText
)
{
SendMessage(hwndStatusBar, SB_SETTEXT, partIndex, (LPARAM)lpText);
}
/*
* supTreeListAddItem
*
* Purpose:
*
* Insert new treelist item.
*
*/
HTREEITEM supTreeListAddItem(
_In_ HWND TreeList,
_In_opt_ HTREEITEM hParent,
_In_ UINT mask,
_In_ UINT state,
_In_ UINT stateMask,
_In_opt_ LPWSTR pszText,
_In_opt_ PVOID subitems
)
{
TVINSERTSTRUCT tvitem;
PTL_SUBITEMS si = (PTL_SUBITEMS)subitems;
RtlSecureZeroMemory(&tvitem, sizeof(tvitem));
tvitem.hParent = hParent;
tvitem.item.mask = mask;
tvitem.item.state = state;
tvitem.item.stateMask = stateMask;
tvitem.item.pszText = pszText;
tvitem.hInsertAfter = TVI_LAST;
return TreeList_InsertTreeItem(TreeList, &tvitem, si);
}
/*
* supAddListViewColumn
*
* Purpose:
*
* Insert list view column.
*
*/
INT supAddListViewColumn(
_In_ HWND ListViewHwnd,
_In_ INT ColumnIndex,
_In_ INT SubItemIndex,
_In_ INT OrderIndex,
_In_ INT ImageIndex,
_In_ INT Format,
_In_ LPWSTR Text,
_In_ INT Width,
_In_ INT DpiValue
)
{
LVCOLUMN column;
column.mask = LVCF_TEXT | LVCF_SUBITEM | LVCF_FMT | LVCF_WIDTH | LVCF_ORDER | LVCF_IMAGE;
column.fmt = Format;
column.cx = ScaleDPI(Width, DpiValue);
column.pszText = Text;
column.iSubItem = SubItemIndex;
column.iOrder = OrderIndex;
column.iImage = ImageIndex;
return ListView_InsertColumn(ListViewHwnd, ColumnIndex, &column);
}
/*
* supTreeListAddCopyValueItem
*
* Purpose:
*
* Add copy to clipboard menu item depending on hit treelist header item.
*
*/
BOOL supTreeListAddCopyValueItem(
_In_ HMENU hMenu,
_In_ HWND hwndTreeList,
_In_ UINT uId,
_In_ UINT uPos,
_In_ LPARAM lParam,
_In_ INT* pSubItemHit
)
{
HDHITTESTINFO hti;
HD_ITEM hdItem;
WCHAR szHeaderText[MAX_PATH + 1];
WCHAR szItem[MAX_PATH * 2];
*pSubItemHit = -1;
hti.iItem = -1;
hti.pt.x = LOWORD(lParam);
hti.pt.y = HIWORD(lParam);
ScreenToClient(hwndTreeList, &hti.pt);
hti.pt.y = 1;
if (TreeList_HeaderHittest(hwndTreeList, &hti) < 0)
return FALSE;
RtlSecureZeroMemory(&hdItem, sizeof(hdItem));
szHeaderText[0] = 0;
hdItem.mask = HDI_TEXT;
hdItem.cchTextMax = RTL_NUMBER_OF(szHeaderText);
hdItem.pszText = szHeaderText;
if (TreeList_GetHeaderItem(hwndTreeList, hti.iItem, &hdItem)) {
*pSubItemHit = hti.iItem;
StringCchPrintf(szItem, RTL_NUMBER_OF(szItem), TEXT("Copy \"%ws\""), szHeaderText);
if (InsertMenu(hMenu, uPos, MF_BYCOMMAND, uId, szItem)) {
return TRUE;
}
}
return FALSE;
}
/*
* supGetItemText
*
* Purpose:
*
* Returns buffer with text from the given listview item.
*
* Returned buffer must be freed with supHeapFree after usage.
*
*/
LPWSTR supGetItemText(
_In_ HWND ListView,
_In_ INT nItem,
_In_ INT nSubItem,
_Out_opt_ PSIZE_T lpSize
)
{
INT len;
LPARAM sz = 0;
LV_ITEM item;
RtlSecureZeroMemory(&item, sizeof(item));
item.iItem = nItem;
item.iSubItem = nSubItem;
len = 128;
do {
len *= 2;
item.cchTextMax = len;
if (item.pszText) {
supHeapFree(item.pszText);
item.pszText = NULL;
}
item.pszText = (LPWSTR)supHeapAlloc(len * sizeof(WCHAR));
if (item.pszText == NULL)
break;
sz = SendMessage(ListView, LVM_GETITEMTEXT, (WPARAM)item.iItem, (LPARAM)&item);
} while (sz == (LPARAM)len - 1);
if (sz == 0) {
if (item.pszText) {
supHeapFree(item.pszText);
item.pszText = NULL;
}
}
if (lpSize) {
*lpSize = sz * sizeof(WCHAR);
}
return item.pszText;
}
/*
* supGetItemText2
*
* Purpose:
*
* Returns text from the given listview item.
*
*/
LPWSTR supGetItemText2(
_In_ HWND ListView,
_In_ INT nItem,
_In_ INT nSubItem,
_In_ WCHAR* pszText,
_In_ UINT cchText
)
{
LV_ITEM item;
RtlSecureZeroMemory(&item, sizeof(item));
item.iItem = nItem;
item.iSubItem = nSubItem;
item.pszText = pszText;
item.cchTextMax = (SIZE_T)cchText;
SendMessage(ListView, LVM_GETITEMTEXT, (WPARAM)item.iItem, (LPARAM)&item);
return item.pszText;
}
/*
* supClipboardCopy
*
* Purpose:
*
* Copy text to the clipboard.
*
*/
VOID supClipboardCopy(
_In_ LPWSTR lpText,
_In_ SIZE_T cbText
)
{
LPWSTR lptstrCopy;
HGLOBAL hglbCopy = NULL;
SIZE_T dwSize;
BOOL dataSet = FALSE;
if (!OpenClipboard(NULL))
return;
__try {
EmptyClipboard();
dwSize = cbText + sizeof(UNICODE_NULL);
hglbCopy = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, dwSize);
if (hglbCopy == NULL)
__leave;
lptstrCopy = (LPWSTR)GlobalLock(hglbCopy);
if (lptstrCopy == NULL)
__leave;
RtlCopyMemory(lptstrCopy, lpText, cbText);
GlobalUnlock(hglbCopy);
dataSet = SetClipboardData(CF_UNICODETEXT, hglbCopy) != NULL;
if (dataSet) {
hglbCopy = NULL;
}
}
__finally {
CloseClipboard();
if (hglbCopy != NULL) {
GlobalFree(hglbCopy);
}
}
}
/*
* supListViewCopyItemValueToClipboard
*
* Purpose:
*
* Copy selected item text to the clipboard.
*
*/
BOOL supListViewCopyItemValueToClipboard(
_In_ HWND hwndListView,
_In_ INT iItem,
_In_ INT iSubItem
)
{
SIZE_T cbText;
LPWSTR lpText;
if ((iSubItem < 0) || (iItem < 0))
return FALSE;
lpText = supGetItemText(hwndListView,
iItem,
iSubItem,
NULL);
if (lpText) {
cbText = _strlen(lpText) * sizeof(WCHAR);
supClipboardCopy(lpText, cbText);
supHeapFree(lpText);
return TRUE;
}
else {
if (OpenClipboard(NULL)) {
EmptyClipboard();
CloseClipboard();
}
}
return FALSE;
}
/*
* supFreeDuplicatedUnicodeString
*
* Purpose:
*
* Release memory allocated for duplicated string.
*
*/
_Success_(return)
BOOL supFreeDuplicatedUnicodeString(
_In_ HANDLE HeapHandle,
_Inout_ PUNICODE_STRING DuplicatedString,
_In_ BOOL DoZeroMemory
)
{
BOOL bResult = FALSE;
if (DuplicatedString->Buffer) {
bResult = RtlFreeHeap(HeapHandle, 0, DuplicatedString->Buffer);
if (DoZeroMemory) {
DuplicatedString->Buffer = NULL;
DuplicatedString->Length = DuplicatedString->MaximumLength = 0;
}
}
return bResult;
}
/*
* supDuplicateUnicodeString
*
* Purpose:
*
* Duplicate existing UNICODE_STRING to another without RtlDuplicateUnicodeString.
*
* Note: Use supFreeDuplicatedUnicodeString to release allocated memory.
*
*/
_Success_(return)
BOOL supDuplicateUnicodeString(
_In_ HANDLE HeapHandle,
_Out_ PUNICODE_STRING DestinationString,
_In_ PUNICODE_STRING SourceString
)
{
USHORT maxLength = SourceString->MaximumLength;
PWCHAR strBuffer;
if (maxLength == 0 || maxLength < SourceString->Length)
return FALSE;
strBuffer = (PWCHAR)RtlAllocateHeap(HeapHandle, HEAP_ZERO_MEMORY, (SIZE_T)maxLength);
if (strBuffer) {
DestinationString->Buffer = strBuffer;
DestinationString->MaximumLength = maxLength;
RtlCopyUnicodeString(DestinationString, SourceString);
return TRUE;
}
return FALSE;
}
//
// Conversion buffer size
//
#define CONVERT_NTNAME_BUFFER_SIZE 512
/*
* supConvertFileName
*
* Purpose:
*
* Translate Nt path name to Dos path name.
*
*/
BOOL supConvertFileName(
_In_ LPWSTR NtFileName,
_Inout_ LPWSTR DosFileName,
_In_ SIZE_T ccDosFileName
)
{
BOOL bFound = FALSE;
SIZE_T nLen;
WCHAR szDrive[3];
WCHAR szName[MAX_PATH];
WCHAR szTemp[CONVERT_NTNAME_BUFFER_SIZE];
WCHAR* pszTemp;
//
// All input parameters are validated by caller before.
//
//
// Drive template.
//
szDrive[0] = L'X';
szDrive[1] = L':';
szDrive[2] = 0;
//
// Query array of logical disk drive strings.
//
szTemp[0] = 0;
if (GetLogicalDriveStrings(RTL_NUMBER_OF(szTemp), szTemp) == 0)
return FALSE;
pszTemp = szTemp;
do {
//
// Copy the drive letter to the template string.
//
*szDrive = *pszTemp;
szName[0] = 0;
//
// Lookup each device name.
//
if (QueryDosDevice(szDrive, szName, MAX_PATH)) {
nLen = _strlen(szName);
if (nLen < MAX_PATH) {
//
// Match device name.
//
bFound = ((_strncmpi(NtFileName, szName, nLen) == 0)
&& *(NtFileName + nLen) == L'\\');
if (bFound) {
//
// Build output name.
//
StringCchPrintf(
DosFileName,
ccDosFileName,
TEXT("%ws%ws"),
szDrive,
NtFileName + nLen);
}
}
}
//
// Go to the next NULL character, i.e. the next drive name.
//
while (*pszTemp++);
} while (!bFound && *pszTemp);
return bFound;
}
/*
* supGetWin32FileName
*
* Purpose:
*
* Query filename by handle.
*
* Input buffer must be at least MAX_PATH length.
*
*/
BOOL supGetWin32FileName(
_In_ LPWSTR FileName,
_Inout_ LPWSTR Win32FileName,
_In_ SIZE_T ccWin32FileName
)
{
BOOL bResult = FALSE;
NTSTATUS status = STATUS_UNSUCCESSFUL;
HANDLE hFile = NULL;
UNICODE_STRING NtFileName;
OBJECT_ATTRIBUTES obja;
IO_STATUS_BLOCK iost;
ULONG memIO;
BYTE* Buffer = NULL;
if ((Win32FileName == NULL) || (ccWin32FileName < MAX_PATH)) {
SetLastError(ERROR_INSUFFICIENT_BUFFER);
return FALSE;
}
do {
RtlInitUnicodeString(&NtFileName, FileName);
InitializeObjectAttributes(&obja, &NtFileName, OBJ_CASE_INSENSITIVE, 0, NULL);
status = NtCreateFile(&hFile, SYNCHRONIZE, &obja, &iost, NULL, 0,
FILE_SHARE_VALID_FLAGS, FILE_OPEN,
FILE_SYNCHRONOUS_IO_NONALERT | FILE_NON_DIRECTORY_FILE, NULL, 0);
if (!NT_SUCCESS(status))
break;
memIO = 0;
status = NtQueryObject(hFile, ObjectNameInformation, NULL, 0, &memIO);
if (status != STATUS_INFO_LENGTH_MISMATCH)
break;
Buffer = (BYTE*)supHeapAlloc(memIO);
if (Buffer == NULL)
break;
status = NtQueryObject(hFile, ObjectNameInformation, Buffer, memIO, NULL);
if (!NT_SUCCESS(status))
break;
if (!supConvertFileName(((PUNICODE_STRING)Buffer)->Buffer, Win32FileName, ccWin32FileName))
break;
bResult = TRUE;
} while (FALSE);
if (hFile)
NtClose(hFile);
if (Buffer != NULL)
supHeapFree(Buffer);
return bResult;
}
/*
* supGetMaxCompareTwoFixedStrings
*
* Purpose:
*
* Returned value used in listview comparer functions.
*
*/
INT supGetMaxCompareTwoFixedStrings(
_In_ HWND ListView,
_In_ LPARAM lParam1,
_In_ LPARAM lParam2,
_In_ LPARAM lParamSort,
_In_ BOOL Inverse
)
{
INT nResult;
LPWSTR lpItem1 = NULL, lpItem2 = NULL, FirstToCompare, SecondToCompare;
WCHAR szString1[MAX_PATH + 1], szString2[MAX_PATH + 1];
szString1[0] = 0;
lpItem1 = supGetItemText2(
ListView,
(INT)lParam1,
(INT)lParamSort,
szString1,
MAX_PATH);
szString2[0] = 0;
lpItem2 = supGetItemText2(
ListView,
(INT)lParam2,
(INT)lParamSort,
szString2,
MAX_PATH);
if (Inverse) {
FirstToCompare = lpItem2;
SecondToCompare = lpItem1;
}
else {
FirstToCompare = lpItem1;
SecondToCompare = lpItem2;
}
nResult = _strcmpi(FirstToCompare, SecondToCompare);
return nResult;
}
/*
* supGetMaxOfTwoU64FromHex
*
* Purpose:
*
* Returned value used in listview comparer functions.
*
*/
INT supGetMaxOfTwoU64FromHex(
_In_ HWND ListView,
_In_ LPARAM lParam1,
_In_ LPARAM lParam2,
_In_ LPARAM lParamSort,
_In_ BOOL Inverse
)
{
INT nResult = 0;
LPWSTR lpItem1 = NULL, lpItem2 = NULL;
ULONG_PTR ad1, ad2;
WCHAR szText[32];
RtlSecureZeroMemory(&szText, sizeof(szText));
lpItem1 = supGetItemText2(
ListView,
(INT)lParam1,
(INT)lParamSort,
szText,
RTL_NUMBER_OF(szText));
ad1 = hextou64(&lpItem1[2]);
RtlSecureZeroMemory(&szText, sizeof(szText));
lpItem2 = supGetItemText2(
ListView,
(INT)lParam2,
(INT)lParamSort,
szText,
RTL_NUMBER_OF(szText));
ad2 = hextou64(&lpItem2[2]);
if (ad1 < ad2)
nResult = -1;
else if (ad1 > ad2)
nResult = 1;
if (Inverse)
nResult = -nResult;
return nResult;
}
/*
* supTreeListCopyItemValueToClipboard
*
* Purpose:
*
* Copy selected treelist item text to the clipboard.
*
*/
BOOL supTreeListCopyItemValueToClipboard(
_In_ HWND hwndTreeList,
_In_ INT tlSubItemHit
)
{
INT nIndex;
LPWSTR lpCopyData = NULL;
SIZE_T cbCopyData = 0;
TVITEMEX itemex;
WCHAR szText[MAX_PATH + 1];
TL_SUBITEMS_FIXED* pSubItems = NULL;
szText[0] = 0;
RtlSecureZeroMemory(&itemex, sizeof(itemex));
itemex.mask = TVIF_TEXT;
itemex.hItem = TreeList_GetSelection(hwndTreeList);
itemex.pszText = szText;
itemex.cchTextMax = MAX_PATH;
if (TreeList_GetTreeItem(hwndTreeList, &itemex, &pSubItems)) {
if ((tlSubItemHit > 0) && (pSubItems != NULL)) {
nIndex = (tlSubItemHit - 1);
if (nIndex < (INT)pSubItems->Count) {
lpCopyData = pSubItems->Text[nIndex];
cbCopyData = _strlen(lpCopyData) * sizeof(WCHAR);
}
}
else {
if (tlSubItemHit == 0) {
lpCopyData = szText;
cbCopyData = sizeof(szText); // copy everything
}
}
if (lpCopyData && cbCopyData) {
supClipboardCopy(lpCopyData, cbCopyData);
return TRUE;
}
else {
if (OpenClipboard(NULL)) {
EmptyClipboard();
CloseClipboard();
}
}
}
return FALSE;
}
/*
* supListViewAddCopyValueItem
*
* Purpose:
*
* Add copy to clipboard menu item depending on hit column.
*
*/
BOOL supListViewAddCopyValueItem(
_In_ HMENU hMenu,
_In_ HWND hwndLv,
_In_ UINT uId,
_In_ UINT uPos,
_In_ POINT * lpPoint,
_Out_ INT * pItemHit,
_Out_ INT * pColumnHit
)
{
LVHITTESTINFO lvht;
LVCOLUMN lvc;
WCHAR szItem[MAX_PATH * 2];
WCHAR szColumn[MAX_PATH + 1];
*pColumnHit = -1;
*pItemHit = -1;
RtlSecureZeroMemory(&lvht, sizeof(lvht));
lvht.pt.x = lpPoint->x;
lvht.pt.y = lpPoint->y;
ScreenToClient(hwndLv, &lvht.pt);
if (ListView_SubItemHitTest(hwndLv, &lvht) == -1)
return FALSE;
RtlSecureZeroMemory(&lvc, sizeof(lvc));
RtlSecureZeroMemory(&szColumn, sizeof(szColumn));
lvc.mask = LVCF_TEXT;
lvc.pszText = szColumn;
lvc.cchTextMax = MAX_PATH;
if (ListView_GetColumn(hwndLv, lvht.iSubItem, &lvc)) {
_strcpy(szItem, TEXT("Copy \""));
_strcat(szItem, szColumn);
_strcat(szItem, TEXT("\""));
if (InsertMenu(hMenu, uPos, MF_BYCOMMAND, uId, szItem)) {
*pColumnHit = lvht.iSubItem;
*pItemHit = lvht.iItem;
return TRUE;
}
}
return FALSE;
}