1.0.5
This commit is contained in:
parent
44eb29e32b
commit
643f569caf
|
@ -8,13 +8,18 @@ This project adheres to [Semantic Versioning](http://semver.org/).
|
|||
## [1.0.5 / 5.55.5] - 2021-12-??
|
||||
|
||||
### Added
|
||||
-
|
||||
- sandbox top level exception handler to create crash dumps
|
||||
-- can be enabled per process or globally using "EnableMiniDump=process.exe,y" or "EnableMiniDump=y" respectivly
|
||||
-- the dump flags can be set as hex with MiniDumpFlags=0xAABBCCDD
|
||||
-- a presellected flag set for a verbose dump can be set with MiniDumpFlags=Extended
|
||||
-- note: Created dump files are located at: C:\Sandbox\%SANDBOX%
|
||||
|
||||
### Changed
|
||||
- improved sbiedll initialization a bit
|
||||
|
||||
### Fixed
|
||||
- fixed issue with forced process display [#1447](https://github.com/sandboxie-plus/Sandboxie/issues/1447)
|
||||
- fixed crash issue with GetClassName [#1448](https://github.com/sandboxie-plus/Sandboxie/issues/1448)
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -240,6 +240,7 @@
|
|||
<ClCompile Include="dllmain.c" />
|
||||
<ClCompile Include="dllmem.c" />
|
||||
<ClCompile Include="dllpath.c" />
|
||||
<ClCompile Include="dump.c" />
|
||||
<ClCompile Include="event.c" />
|
||||
<ClCompile Include="file.c" />
|
||||
<ClCompile Include="file_dir.c">
|
||||
|
@ -423,6 +424,7 @@
|
|||
<ClInclude Include="advapi.h" />
|
||||
<ClInclude Include="debug.h" />
|
||||
<ClInclude Include="dll.h" />
|
||||
<ClInclude Include="dump.h" />
|
||||
<ClInclude Include="guidlg.h" />
|
||||
<ClInclude Include="gui_p.h" />
|
||||
<ClInclude Include="hook.h" />
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
<ClCompile Include="advapi.c" />
|
||||
<ClCompile Include="crypt.c" />
|
||||
<ClCompile Include="custom.c" />
|
||||
<ClCompile Include="debug.c" />
|
||||
<ClCompile Include="dllmem.c" />
|
||||
<ClCompile Include="dllpath.c" />
|
||||
<ClCompile Include="event.c" />
|
||||
|
@ -161,7 +160,6 @@
|
|||
<ClCompile Include="dllmain.c">
|
||||
<Filter>hook</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="trace.c" />
|
||||
<ClCompile Include="..\..\common\my_ntdll.c">
|
||||
<Filter>common</Filter>
|
||||
</ClCompile>
|
||||
|
@ -211,10 +209,18 @@
|
|||
<ClCompile Include="..\..\common\wow64ext\wow64ext.cpp">
|
||||
<Filter>common\wow64ext</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="debug.c">
|
||||
<Filter>debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="trace.c">
|
||||
<Filter>debug</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dump.c">
|
||||
<Filter>debug</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="advapi.h" />
|
||||
<ClInclude Include="debug.h" />
|
||||
<ClInclude Include="dll.h" />
|
||||
<ClInclude Include="hook.h">
|
||||
<Filter>hook</Filter>
|
||||
|
@ -252,7 +258,6 @@
|
|||
<ClInclude Include="obj.h">
|
||||
<Filter>com</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="trace.h" />
|
||||
<ClInclude Include="..\..\common\stream.h">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
|
@ -298,6 +303,15 @@
|
|||
<ClInclude Include="..\..\common\my_version.h">
|
||||
<Filter>common</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="debug.h">
|
||||
<Filter>debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="trace.h">
|
||||
<Filter>debug</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="dump.h">
|
||||
<Filter>debug</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="resource.rc" />
|
||||
|
@ -356,6 +370,9 @@
|
|||
<Filter Include="common\wow64ext">
|
||||
<UniqueIdentifier>{c6a9654a-9f55-43ca-9cd1-7571bb879288}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="debug">
|
||||
<UniqueIdentifier>{db0f9820-8908-4325-96f9-69c82fa9e268}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Midl Include="pstore.idl">
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "obj.h"
|
||||
#include "trace.h"
|
||||
#include "debug.h"
|
||||
#include "dump.h"
|
||||
#include "core/low/lowdata.h"
|
||||
#include "common/my_version.h"
|
||||
|
||||
|
@ -529,6 +530,17 @@ _FX void Dll_InitExeEntry(void)
|
|||
//
|
||||
|
||||
SbieDll_StartCOM(TRUE);
|
||||
|
||||
//
|
||||
// setup own top level exception handler
|
||||
//
|
||||
|
||||
if(Config_GetSettingsForImageName_bool(L"EnableMiniDump", FALSE))
|
||||
Dump_Init();
|
||||
|
||||
//
|
||||
// once we return here the process images entrypoint will be called
|
||||
//
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
/*
|
||||
* Copyright 2021 DavidXanatos, xanasoft.com
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Dump Helper
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
#include "dll.h"
|
||||
#include "dump.h"
|
||||
|
||||
#include <dbghelp.h>
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Defines
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
typedef BOOL (__stdcall *P_MiniDumpWriteDump)(
|
||||
IN HANDLE hProcess,
|
||||
IN DWORD ProcessId,
|
||||
IN HANDLE hFile,
|
||||
IN MINIDUMP_TYPE DumpType,
|
||||
IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL
|
||||
IN CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL
|
||||
IN CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL
|
||||
);
|
||||
|
||||
typedef LPTOP_LEVEL_EXCEPTION_FILTER(*P_SetUnhandledExceptionFilter)(
|
||||
_In_opt_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Variables
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
static P_MiniDumpWriteDump __sys_MiniDumpWriteDump;
|
||||
static HMODULE Dump_DbgHelpMod;
|
||||
|
||||
#define DUMP_FLAGS_DEFAULT MiniDumpWithHandleData | \
|
||||
MiniDumpWithUnloadedModules | \
|
||||
MiniDumpFilterModulePaths | \
|
||||
MiniDumpWithProcessThreadData | \
|
||||
MiniDumpWithoutOptionalData | \
|
||||
MiniDumpIgnoreInaccessibleMemory | \
|
||||
MiniDumpFilterTriage //0x001205a4 thats what WerFault.exe uses
|
||||
|
||||
#define DUMP_FLAGS_EXTENDED MiniDumpWithFullMemory | \
|
||||
MiniDumpWithHandleData | \
|
||||
MiniDumpWithThreadInfo | \
|
||||
MiniDumpWithProcessThreadData | \
|
||||
MiniDumpWithFullMemoryInfo | \
|
||||
MiniDumpWithUnloadedModules | \
|
||||
MiniDumpWithFullAuxiliaryState | \
|
||||
MiniDumpIgnoreInaccessibleMemory | \
|
||||
MiniDumpWithTokenInformation
|
||||
|
||||
static MINIDUMP_TYPE Dump_Flags = DUMP_FLAGS_DEFAULT;
|
||||
static P_SetUnhandledExceptionFilter __sys_SetUnhandledExceptionFilter = NULL;
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Functions
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Dump_CrashHandlerExceptionFilter
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
LONGLONG GetCurCycle()
|
||||
{
|
||||
LONGLONG freq, now;
|
||||
QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
|
||||
QueryPerformanceCounter((LARGE_INTEGER*)&now);
|
||||
LONGLONG dwNow = ((now * 1000000) / freq);
|
||||
return dwNow; // returns time since system start in us
|
||||
}
|
||||
|
||||
static LONG __stdcall Dump_CrashHandlerExceptionFilter(EXCEPTION_POINTERS* pEx)
|
||||
{
|
||||
#ifdef _M_IX86
|
||||
if (pEx->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW)
|
||||
{
|
||||
// be sure that we have enought space...
|
||||
static char MyStack[1024 * 128];
|
||||
// it assumes that DS and SS are the same!!! (this is the case for Win32)
|
||||
// change the stack only if the selectors are the same (this is the case for Win32)
|
||||
//__asm push offset MyStack[1024*128];
|
||||
//__asm pop esp;
|
||||
__asm mov eax, offset MyStack[1024 * 128];
|
||||
__asm mov esp, eax;
|
||||
}
|
||||
#endif
|
||||
|
||||
SbieApi_Log(2224, L"%S [%S]", Dll_ImageName, Dll_BoxName);
|
||||
|
||||
BOOLEAN bSuccess = FALSE;
|
||||
HANDLE hFile;
|
||||
|
||||
wchar_t szMiniDumpFileName[128];
|
||||
Sbie_snwprintf(szMiniDumpFileName, 128, L"%s.%lld.dmp", Dll_ImageName, GetCurCycle);
|
||||
|
||||
wchar_t szMiniDumpFilePath[MAX_PATH] = { 0 };
|
||||
Sbie_snwprintf(szMiniDumpFilePath, MAX_PATH, L"%s\\%s", Dll_BoxFilePath, szMiniDumpFileName);
|
||||
//hFile = CreateFile(szMiniDumpFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
|
||||
UNICODE_STRING uni;
|
||||
OBJECT_ATTRIBUTES attr;
|
||||
RtlInitUnicodeString(&uni, szMiniDumpFilePath);
|
||||
InitializeObjectAttributes(&attr, &uni, OBJ_CASE_INSENSITIVE, NULL, 0);
|
||||
|
||||
IO_STATUS_BLOCK Iosb;
|
||||
extern P_NtCreateFile __sys_NtCreateFile;
|
||||
NTSTATUS status = __sys_NtCreateFile(&hFile, FILE_GENERIC_WRITE, &attr, &Iosb, NULL, FILE_ATTRIBUTE_NORMAL, FILE_SHARE_READ, FILE_OPEN_IF, FILE_SYNCHRONOUS_IO_NONALERT, NULL, 0);
|
||||
|
||||
//if (hFile != INVALID_HANDLE_VALUE)
|
||||
if (NT_SUCCESS(status))
|
||||
{
|
||||
MINIDUMP_EXCEPTION_INFORMATION stMDEI;
|
||||
stMDEI.ThreadId = GetCurrentThreadId();
|
||||
stMDEI.ExceptionPointers = pEx;
|
||||
stMDEI.ClientPointers = TRUE;
|
||||
// try to create an miniDump:
|
||||
if (__sys_MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, Dump_Flags, &stMDEI, NULL, NULL))
|
||||
{
|
||||
bSuccess = TRUE;
|
||||
}
|
||||
//CloseHandle(hFile);
|
||||
extern P_NtClose __sys_NtClose;
|
||||
__sys_NtClose(hFile);
|
||||
}
|
||||
|
||||
wchar_t szMiniDumpMessage[256];
|
||||
if (!bSuccess)
|
||||
Sbie_snwprintf(szMiniDumpMessage, 256, L"%s crashed!\r\nCrashdump creation failed.", Dll_ImageName);
|
||||
else
|
||||
Sbie_snwprintf(szMiniDumpMessage, 256, L"%s crashed!\r\nCrashdump saved to \"%s\".", Dll_ImageName, szMiniDumpFileName);
|
||||
SbieApi_MonitorPut2(MONITOR_OTHER | MONITOR_TRACE, szMiniDumpMessage, FALSE);
|
||||
|
||||
// or return one of the following:
|
||||
// - EXCEPTION_CONTINUE_SEARCH // this will trigger the "normal" OS error-dialog
|
||||
// - EXCEPTION_CONTINUE_EXECUTION
|
||||
// - EXCEPTION_EXECUTE_HANDLER // this will prevent the invocation of WerFault.exe
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Dump_SetUnhandledExceptionFilter
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
ALIGNED LPTOP_LEVEL_EXCEPTION_FILTER Dump_SetUnhandledExceptionFilter(
|
||||
_In_opt_ LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter
|
||||
)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Dump_SetUnhandledExceptionFilter
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
/*ALIGNED BOOLEAN Dump_MiniDumpWriteDump(
|
||||
_In_ HANDLE hProcess,
|
||||
_In_ DWORD ProcessId,
|
||||
_In_ HANDLE hFile,
|
||||
_In_ MINIDUMP_TYPE DumpType,
|
||||
_In_opt_ PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||||
_In_opt_ PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
||||
_In_opt_ PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
||||
)
|
||||
{
|
||||
while (! IsDebuggerPresent()) {
|
||||
OutputDebugString(L"BREAK\n");
|
||||
Sleep(500);
|
||||
}
|
||||
__debugbreak();
|
||||
|
||||
return __sys_MiniDumpWriteDump(hProcess, ProcessId, hFile, DumpType, ExceptionParam, UserStreamParam, CallbackParam);
|
||||
}*/
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Dump_Init
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
_FX int Dump_Init(void)
|
||||
{
|
||||
if (Dump_DbgHelpMod != NULL)
|
||||
return 2;
|
||||
|
||||
// Initialize the member, so we do not load the dll after the exception has occured
|
||||
// which might be not possible anymore...
|
||||
Dump_DbgHelpMod = LoadLibrary(L"dbghelp.dll");
|
||||
if (!Dump_DbgHelpMod )
|
||||
return 0;
|
||||
|
||||
/*if (_wcsicmp(Dll_ImageName, L"WerFault.exe") == 0) // fre experimenting only
|
||||
{
|
||||
P_MiniDumpWriteDump MiniDumpWriteDump = (P_MiniDumpWriteDump)GetProcAddress(Dump_DbgHelpMod, "MiniDumpWriteDump");
|
||||
SBIEDLL_HOOK(Dump_, MiniDumpWriteDump);
|
||||
return 1;
|
||||
}*/
|
||||
|
||||
__sys_MiniDumpWriteDump = (P_MiniDumpWriteDump)GetProcAddress(Dump_DbgHelpMod, "MiniDumpWriteDump");
|
||||
|
||||
// get the default preset
|
||||
WCHAR str[32];
|
||||
if (NT_SUCCESS(SbieApi_QueryConfAsIs(NULL, L"MiniDumpFlags", 0, str, sizeof(str) - sizeof(WCHAR)))) {
|
||||
if (_wcsnicmp(str, L"0x", 2) == 0)
|
||||
Dump_Flags = wcstol(str + 2, NULL, 16);
|
||||
else if (_wcsnicmp(str, L"extended", 3) == 0) // check only first 3 letters
|
||||
Dump_Flags = DUMP_FLAGS_EXTENDED;
|
||||
}
|
||||
|
||||
// Register Unhandled Exception-Filter:
|
||||
SetUnhandledExceptionFilter(Dump_CrashHandlerExceptionFilter);
|
||||
|
||||
// Additional call "PreventSetUnhandledExceptionFilter"...
|
||||
// See also: "SetUnhandledExceptionFilter" and VC8 (and later)
|
||||
// http://blog.kalmbachnet.de/?postid=75
|
||||
|
||||
SBIEDLL_HOOK(Dump_, SetUnhandledExceptionFilter);
|
||||
|
||||
//SbieApi_MonitorPut2(MONITOR_OTHER | MONITOR_TRACE, L"Minidump enabled", FALSE);
|
||||
return 1;
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* Copyright 2021 DavidXanatos, xanasoft.com
|
||||
*
|
||||
* 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 <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
// Sandboxie Trace
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
#ifndef _MY_DUMP_H
|
||||
#define _MY_DUMP_H
|
||||
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
int Dump_Init(void);
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
|
||||
#endif /* _MY_DUMP_H */
|
|
@ -293,7 +293,7 @@ static P_NtQueryInformationFile __sys_NtQueryInformationFile = NULL;
|
|||
static P_NtQueryDirectoryFileEx __sys_NtQueryDirectoryFileEx = NULL;
|
||||
static P_NtSetInformationFile __sys_NtSetInformationFile = NULL;
|
||||
static P_NtDeleteFile __sys_NtDeleteFile = NULL;
|
||||
static P_NtClose __sys_NtClose = NULL;
|
||||
P_NtClose __sys_NtClose = NULL;
|
||||
static P_NtCreateNamedPipeFile __sys_NtCreateNamedPipeFile = NULL;
|
||||
static P_NtCreateMailslotFile __sys_NtCreateMailslotFile = NULL;
|
||||
static P_NtReadFile __sys_NtReadFile = NULL;
|
||||
|
|
|
@ -742,8 +742,10 @@ _FX int Gui_GetClassNameW(
|
|||
n -= (ULONG)(clsnm_ptr - clsnm);
|
||||
if (n > nMaxCount - 1)
|
||||
n = nMaxCount - 1;
|
||||
wmemcpy(lpClassName, clsnm_ptr, n);
|
||||
lpClassName[n] = L'\0';
|
||||
if (lpClassName) {
|
||||
wmemcpy(lpClassName, clsnm_ptr, n);
|
||||
lpClassName[n] = L'\0';
|
||||
}
|
||||
} else
|
||||
n = 0;
|
||||
} else
|
||||
|
@ -754,8 +756,10 @@ _FX int Gui_GetClassNameW(
|
|||
n = nMaxCount - 1;
|
||||
else
|
||||
n = n0;
|
||||
wmemcpy(lpClassName, clsnm, n);
|
||||
lpClassName[n] = L'\0';
|
||||
if (lpClassName) {
|
||||
wmemcpy(lpClassName, clsnm, n);
|
||||
lpClassName[n] = L'\0';
|
||||
}
|
||||
}
|
||||
|
||||
Dll_Free(clsnm);
|
||||
|
@ -796,8 +800,10 @@ _FX int Gui_GetClassNameA(
|
|||
n -= (ULONG)(clsnm_ptr - clsnm);
|
||||
if (n > nMaxCount - 1)
|
||||
n = nMaxCount - 1;
|
||||
memcpy(lpClassName, clsnm_ptr, n);
|
||||
lpClassName[n] = '\0';
|
||||
if (lpClassName) {
|
||||
memcpy(lpClassName, clsnm_ptr, n);
|
||||
lpClassName[n] = '\0';
|
||||
}
|
||||
} else
|
||||
n = 0;
|
||||
} else
|
||||
|
@ -808,8 +814,10 @@ _FX int Gui_GetClassNameA(
|
|||
n = nMaxCount - 1;
|
||||
else
|
||||
n = n0;
|
||||
memcpy(lpClassName, clsnm, n);
|
||||
lpClassName[n] = L'\0';
|
||||
if (lpClassName) {
|
||||
memcpy(lpClassName, clsnm, n);
|
||||
lpClassName[n] = L'\0';
|
||||
}
|
||||
}
|
||||
|
||||
Dll_Free(clsnm);
|
||||
|
|
|
@ -120,14 +120,14 @@ void COptionsWindow::ShowTemplates()
|
|||
if (!CategoryFilter.isEmpty() && I.key().compare(CategoryFilter, Qt::CaseInsensitive) != 0)
|
||||
continue;
|
||||
|
||||
QString Name = I.value().first.mid(9);
|
||||
|
||||
if (!Name.isEmpty() && Name.indexOf(TextFilter, 0, Qt::CaseInsensitive) == -1)
|
||||
if (I.value().second.indexOf(TextFilter, 0, Qt::CaseInsensitive) == -1)
|
||||
continue;
|
||||
|
||||
if (I.key().isEmpty())
|
||||
continue; // dont show templates without a category (these are usually deprecated templates)
|
||||
|
||||
QString Name = I.value().first.mid(9);
|
||||
|
||||
QTreeWidgetItem* pItem = new QTreeWidgetItem();
|
||||
pItem->setText(0, GetCategoryName(I.key()));
|
||||
pItem->setData(1, Qt::UserRole, I.value().first);
|
||||
|
|
Loading…
Reference in New Issue