diff --git a/CHANGELOG.md b/CHANGELOG.md index e35c9149..15b8918a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/). + +## [1.0.1 / 5.40.2] - 2020-06-01 + +### Added +- Created a new Qt based UI names SandMan (Sandboxie Manager) +- Resource monitor now shows the PID +- Added basic API call log using updated BSA LogApiDll + + +### Changed +- reworked resource monitor to work with multiple event consumers +- reworked log to work with multiple event consumers + + + ## [5.40.1] - 2020-04-10 ### Added diff --git a/Sandboxie/Sandbox.sln b/Sandboxie/Sandbox.sln index 18588707..bff10041 100644 --- a/Sandboxie/Sandbox.sln +++ b/Sandboxie/Sandbox.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.960 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SboxSvc", "core\svc\SboxSvc.vcxproj", "{2D3DBCAE-883E-54A6-F8F6-11228D989033}" ProjectSection(ProjectDependencies) = postProject @@ -69,7 +69,7 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SbieControl", "apps\control {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} = {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} EndProjectSection EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KmdUtil", "install\kmdutil\KmdUtil.vcxproj", "{0BF4988E-2325-4426-8CDC-BD221E4FB68C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "kmdutil", "install\kmdutil\KmdUtil.vcxproj", "{0BF4988E-2325-4426-8CDC-BD221E4FB68C}" ProjectSection(ProjectDependencies) = postProject {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} = {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} EndProjectSection @@ -88,6 +88,17 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SboxHostDll", "SboxHostDll\ {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} = {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} EndProjectSection EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{666D2193-3CF6-4358-8312-67A0C2B09E35}" + ProjectSection(SolutionItems) = preProject + ..\CHANGELOG.md = ..\CHANGELOG.md + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "com", "com", "{7495BFF6-A576-4B96-9071-10CF956368FC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{E9D1318A-FAF0-4EF8-8561-FCB03862AC99}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "apps", "apps", "{0301861F-98D8-4767-BA7D-E146DE2E0C92}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution SbieDebug|Win32 = SbieDebug|Win32 @@ -240,4 +251,23 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {2D3DBCAE-883E-54A6-F8F6-11228D989033} = {E9D1318A-FAF0-4EF8-8561-FCB03862AC99} + {8B9E1B9D-FB3C-3009-9196-4315871BCD73} = {0301861F-98D8-4767-BA7D-E146DE2E0C92} + {E40CC819-6990-DA28-3E1F-6708BC98E37B} = {7495BFF6-A576-4B96-9071-10CF956368FC} + {8E0EAA5B-6F5B-E0E2-338A-453EF2B548E4} = {E9D1318A-FAF0-4EF8-8561-FCB03862AC99} + {41453A79-CA9B-ABCA-981C-5242AFC72DDF} = {7495BFF6-A576-4B96-9071-10CF956368FC} + {5410C534-4858-C748-86AD-0567A2451FDE} = {7495BFF6-A576-4B96-9071-10CF956368FC} + {8055A629-631E-84F5-8F3C-1908F264C81D} = {7495BFF6-A576-4B96-9071-10CF956368FC} + {42DB5510-0268-4655-B483-B9D6E4E48D62} = {7495BFF6-A576-4B96-9071-10CF956368FC} + {255002EC-9FC7-422E-B497-BE2CC5012B2D} = {E9D1318A-FAF0-4EF8-8561-FCB03862AC99} + {4019C5EB-8D1E-40E4-B7D1-5601B4B27288} = {E9D1318A-FAF0-4EF8-8561-FCB03862AC99} + {D16E291A-1F8A-4B19-AE07-0AF8CB7CCBD0} = {0301861F-98D8-4767-BA7D-E146DE2E0C92} + {0BF4988E-2325-4426-8CDC-BD221E4FB68C} = {0301861F-98D8-4767-BA7D-E146DE2E0C92} + {B8D7002B-0468-44E7-93A7-94327A5D7C7A} = {0301861F-98D8-4767-BA7D-E146DE2E0C92} + {3A42A9F3-E0C7-4633-9570-381802D6647D} = {E9D1318A-FAF0-4EF8-8561-FCB03862AC99} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {8CC68B2E-A32E-409D-8D3F-F68AF524E29C} + EndGlobalSection EndGlobal diff --git a/Sandboxie/SandboxDrv.sln b/Sandboxie/SandboxDrv.sln index a22b7dae..1ec480b6 100644 --- a/Sandboxie/SandboxDrv.sln +++ b/Sandboxie/SandboxDrv.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 14 -VisualStudioVersion = 14.0.25420.1 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1022 MinimumVisualStudioVersion = 10.0.40219.1 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SboxDrv", "core\drv\SboxDrv.vcxproj", "{4019C5EB-8D1E-40E4-B7D1-5601B4B27288}" ProjectSection(ProjectDependencies) = postProject @@ -27,6 +27,14 @@ Global SbieRelease|x64 = SbieRelease|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Debug|Win32.ActiveCfg = SbieDebug|Win32 + {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Debug|Win32.Build.0 = SbieDebug|Win32 + {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Debug|x64.ActiveCfg = SbieDebug|x64 + {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Debug|x64.Build.0 = SbieDebug|x64 + {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Release|Win32.ActiveCfg = SbieRelease|Win32 + {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Release|Win32.Build.0 = SbieRelease|Win32 + {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Release|x64.ActiveCfg = SbieRelease|x64 + {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.Release|x64.Build.0 = SbieRelease|x64 {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieDebug|Win32.ActiveCfg = SbieDebug|Win32 {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieDebug|Win32.Build.0 = SbieDebug|Win32 {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieDebug|x64.ActiveCfg = SbieDebug|x64 @@ -35,6 +43,14 @@ Global {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieRelease|Win32.Build.0 = SbieRelease|Win32 {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieRelease|x64.ActiveCfg = SbieRelease|x64 {4019C5EB-8D1E-40E4-B7D1-5601B4B27288}.SbieRelease|x64.Build.0 = SbieRelease|x64 + {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Debug|Win32.ActiveCfg = SbieRelease|Win32 + {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Debug|Win32.Build.0 = SbieRelease|Win32 + {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Debug|x64.ActiveCfg = SbieRelease|x64 + {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Debug|x64.Build.0 = SbieRelease|x64 + {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Release|Win32.ActiveCfg = SbieRelease|Win32 + {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Release|Win32.Build.0 = SbieRelease|Win32 + {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Release|x64.ActiveCfg = SbieRelease|x64 + {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.Release|x64.Build.0 = SbieRelease|x64 {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieDebug|Win32.ActiveCfg = SbieRelease|Win32 {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieDebug|Win32.Build.0 = SbieRelease|Win32 {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieDebug|x64.ActiveCfg = SbieRelease|x64 @@ -43,6 +59,14 @@ Global {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieRelease|Win32.Build.0 = SbieRelease|Win32 {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieRelease|x64.ActiveCfg = SbieRelease|x64 {63B0DDD2-5E3B-EF38-F711-9652D2EB73B3}.SbieRelease|x64.Build.0 = SbieRelease|x64 + {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Debug|Win32.ActiveCfg = SbieRelease|Win32 + {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Debug|Win32.Build.0 = SbieRelease|Win32 + {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Debug|x64.ActiveCfg = SbieRelease|Win32 + {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Debug|x64.Build.0 = SbieRelease|Win32 + {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Release|Win32.ActiveCfg = SbieRelease|Win32 + {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Release|Win32.Build.0 = SbieRelease|Win32 + {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Release|x64.ActiveCfg = SbieRelease|Win32 + {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.Release|x64.Build.0 = SbieRelease|Win32 {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.SbieDebug|Win32.ActiveCfg = SbieRelease|Win32 {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.SbieDebug|Win32.Build.0 = SbieRelease|Win32 {7BA01954-12F1-4CEE-BA97-FAD3250D9776}.SbieDebug|x64.ActiveCfg = SbieRelease|Win32 @@ -55,4 +79,7 @@ Global GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {806E7BFC-90B6-4D60-A232-3321EA1A60BA} + EndGlobalSection EndGlobal diff --git a/Sandboxie/apps/control/MessageDialog.cpp b/Sandboxie/apps/control/MessageDialog.cpp index 3028e214..d8a308a2 100644 --- a/Sandboxie/apps/control/MessageDialog.cpp +++ b/Sandboxie/apps/control/MessageDialog.cpp @@ -94,6 +94,8 @@ CMessageDialog::CMessageDialog(CWnd *pParentWnd, int mode) m_buf_len = (8 * 1024); m_buf = malloc_WCHAR(m_buf_len); + m_last_message_number = 0; + if (mode == MSGDLG_NORMAL) ReloadConf(); @@ -250,19 +252,23 @@ void CMessageDialog::OnTimer() while (1) { ULONG len = m_buf_len; - LONG status = SbieApi_GetWork(CMyApp::m_session_id, m_buf, &len); - if (status != 0) - break; + ULONG message_number = m_last_message_number; + ULONG code = -1; + LONG status = SbieApi_GetMessage(&message_number, CMyApp::m_session_id, &code, m_buf, len); + if (status != 0) + break; // error or no more entries - ULONG *type = (ULONG *)m_buf; - if (*type != API_LOG_MESSAGE) + //if (message_number != m_last_message_number + 1) + // we missed something + m_last_message_number = message_number; + + if (code == 0) + continue; // empty dummy + + if (/*code == MSG_2199 &&*/ m_firsttime) continue; - ULONG code = type[1]; - if (code == MSG_2199 && m_firsttime) - continue; - - WCHAR *str1 = (WCHAR *)&type[2]; + WCHAR *str1 = m_buf; ULONG str1_len = wcslen(str1); WCHAR *str2 = str1 + str1_len + 1; ULONG str2_len = wcslen(str2); diff --git a/Sandboxie/apps/control/MessageDialog.h b/Sandboxie/apps/control/MessageDialog.h index 684dca1c..7044117e 100644 --- a/Sandboxie/apps/control/MessageDialog.h +++ b/Sandboxie/apps/control/MessageDialog.h @@ -55,6 +55,8 @@ class CMessageDialog : public CBaseDialog CPtrArray m_hidden; + ULONG m_last_message_number; + BOOL IsHiddenMessage( ULONG code, const WCHAR *detail_1, const WCHAR *detail_2); diff --git a/Sandboxie/apps/control/MonitorDialog.cpp b/Sandboxie/apps/control/MonitorDialog.cpp index bfdc88b5..617ee099 100644 --- a/Sandboxie/apps/control/MonitorDialog.cpp +++ b/Sandboxie/apps/control/MonitorDialog.cpp @@ -41,6 +41,8 @@ CMonitorDialog::CMonitorDialog(CWnd *pParentWnd) m_username[0] = L'\0'; m_username[255] = L'\0'; m_username_len = wcslen(m_username); + + m_last_entry_seq_num = 0; } @@ -105,15 +107,22 @@ void CMonitorDialog::OnIdle() static const WCHAR *_Separator = L" -------------------------------"; CListBox *listbox = (CListBox *)GetDlgItem(ID_MESSAGE_LIST); - WCHAR name[280]; + WCHAR name[300]; while (1) { + ULONG seq_num = m_last_entry_seq_num; USHORT type; - SbieApi_MonitorGet(&type, &name[12]); + ULONG64 pid; + SbieApi_MonitorGetEx(&seq_num, &type, &pid, &name[12]); if ((! type) || (! name[12])) break; + if(seq_num != m_last_entry_seq_num + 1) + SbieApi_Log(MSG_1242, L"Resource access logger overflow!"); // MSG_MONITOR_OVERFLOW + m_last_entry_seq_num = seq_num; + + // privacy protection, hide username while (m_username_len) { WCHAR *username_ptr = wcsstr(&name[12], m_username); if (! username_ptr) @@ -126,12 +135,11 @@ void CMonitorDialog::OnIdle() name[10] = L' '; name[9] = L' '; if (type & MONITOR_OPEN) { - type &= ~MONITOR_OPEN; name[9] = L'O'; } else if (type & MONITOR_DENY) { - type &= ~MONITOR_DENY; name[9] = L'X'; } + type &= 0x0FFF; const WCHAR *PrefixPtr = _Unknown; if (type == MONITOR_PIPE) @@ -152,6 +160,8 @@ void CMonitorDialog::OnIdle() PrefixPtr = _Other; wcsncpy(name, PrefixPtr, 9); + wsprintf(&name[wcslen(name)], L"; PID: %I64u", pid); + int index = listbox->AddString(name); WCHAR oldname[280]; diff --git a/Sandboxie/apps/control/MonitorDialog.h b/Sandboxie/apps/control/MonitorDialog.h index 0b461293..248c4d23 100644 --- a/Sandboxie/apps/control/MonitorDialog.h +++ b/Sandboxie/apps/control/MonitorDialog.h @@ -30,6 +30,7 @@ class CMonitorDialog : public CBaseDialog WCHAR m_username[256]; ULONG m_username_len; + ULONG m_last_entry_seq_num; virtual BOOL OnInitDialog(); diff --git a/Sandboxie/common/my_version.h b/Sandboxie/common/my_version.h index b7635fbe..60ce9939 100644 --- a/Sandboxie/common/my_version.h +++ b/Sandboxie/common/my_version.h @@ -22,7 +22,7 @@ #define MY_VERSION_BINARY 5,40 #define MY_VERSION_STRING "5.40" -#define MY_VERSION_STRING_EX "5.40.1" +#define MY_VERSION_STRING_EX "5.40.2" // These #defines are used by either Resource Compiler, or by NSIC installer #define SBIE_INSTALLER_PATH "..\\Bin\\" diff --git a/Sandboxie/common/win32_ntddk.h b/Sandboxie/common/win32_ntddk.h index 279fefcb..91457079 100644 --- a/Sandboxie/common/win32_ntddk.h +++ b/Sandboxie/common/win32_ntddk.h @@ -218,7 +218,7 @@ typedef struct _OBJECT_BASIC_INFORMATION { } OBJECT_BASIC_INFORMATION, *POBJECT_BASIC_INFORMATION; typedef struct _OBJECT_NAME_INFORMATION { - UNICODE_STRING ObjectName; + UNICODE_STRING Name; } OBJECT_NAME_INFORMATION, *POBJECT_NAME_INFORMATION; typedef struct __PUBLIC_OBJECT_TYPE_INFORMATION { @@ -921,6 +921,24 @@ typedef enum _PROCESSINFOCLASS { ProcessDebugObjectHandle, // 30 ProcessDebugFlags, ProcessHandleTracing, + ProcessIoPriority, + ProcessExecuteFlags, + ProcessResourceManagement, // ProcessTlsInformation + ProcessCookie, + ProcessImageInformation, + ProcessCycleTime, + ProcessPagePriority, + ProcessInstrumentationCallback, // 40 + ProcessThreadStackAllocation, + ProcessWorkingSetWatchEx, + ProcessImageFileNameWin32, + ProcessImageFileMapping, + ProcessAffinityUpdateMode, + ProcessMemoryAllocationMode, + ProcessGroupInformation, + ProcessTokenVirtualizationEnabled, + ProcessConsoleHostProcess, + ProcessWindowInformation, // 50 MaxProcessInfoClass // MaxProcessInfoClass should always be the last enum } PROCESSINFOCLASS; @@ -931,7 +949,7 @@ typedef struct _PROCESS_BASIC_INFORMATION { LONG BasePriority; // was type KPRIORITY ULONG_PTR UniqueProcessId; ULONG_PTR InheritedFromUniqueProcessId; -} PROCESS_BASIC_INFORMATION; +} PROCESS_BASIC_INFORMATION, *PPROCESS_BASIC_INFORMATION; typedef struct _PROCESS_IMAGE_FILE_NAME { USHORT Length; @@ -1241,56 +1259,110 @@ typedef enum _SYSTEM_INFORMATION_CLASS { SystemProcessorInformation, SystemPerformanceInformation, SystemTimeOfDayInformation, - SystemNotImplemented1, + SystemPathInformation, SystemProcessInformation, // 5 - SystemCallCounts, - SystemConfigurationInformation, - SystemProcessorTimes, - SystemGlobalFlag, - SystemNotImplemented2, // 10 + SystemCallCountInformation, + SystemDeviceInformation, + SystemProcessorPerformanceInformation, + SystemFlagsInformation, + SystemCallTimeInformation, // 10 SystemModuleInformation, // 11 - SystemLockInformation, - SystemNotImplemented3, - SystemNotImplemented4, - SystemNotImplemented5, + SystemLocksInformation, + SystemStackTraceInformation, + SystemPagedPoolInformation, + SystemNonPagedPoolInformation, SystemHandleInformation, SystemObjectInformation, - SystemPagefileInformation, - SystemInstructionEmulationCounts, - SystemInvalidInfoClass1, - SystemCacheInformation, - SystemPoolTagInformation, - SystemProcessorStatistics, - SystemDpcInformation, - SystemNotImplemented6, - SystemLoadImage, - SystemUnloadImage, - SystemTimeAdjustment, - SystemNotImplemented7, - SystemNotImplemented8, - SystemNotImplemented9, + SystemPageFileInformation, + SystemVdmInstemulInformation, + SystemVdmBopInformation, + SystemFileCacheInformation, + SystemPoolTagInformation, + SystemInterruptInformation, + SystemDpcBehaviorInformation, + SystemFullMemoryInformation, + SystemLoadGdiDriverInformation, + SystemUnloadGdiDriverInformation, + SystemTimeAdjustmentInformation, + SystemSummaryMemoryInformation, + SystemMirrorMemoryInformation, + SystemPerformanceTraceInformation, SystemCrashDumpInformation, SystemExceptionInformation, // 33 SystemCrashDumpStateInformation, SystemKernelDebuggerInformation, SystemContextSwitchInformation, SystemRegistryQuotaInformation, // 37 - SystemLoadAndCallImage, - SystemPrioritySeparation, - SystemNotImplemented10, - SystemNotImplemented11, - SystemInvalidInfoClass2, - SystemInvalidInfoClass3, - SystemTimeZoneInformation, + SystemExtendServiceTableInformation, + SystemPrioritySeperation, + SystemVerifierAddDriverInformation, + SystemVerifierRemoveDriverInformation, + SystemProcessorIdleInformation, + SystemLegacyDriverInformation, + SystemCurrentTimeZoneInformation, SystemLookasideInformation, // 45 - SystemSetTimeSlipEvent, + SystemTimeSlipNotification, SystemSessionCreate, SystemSessionDetach, - SystemInvalidInfoClass4, + SystemSessionInformation, SystemRangeStartInformation, // 50 SystemVerifierInformation, - SystemAddVerifier, - SystemSessionProcessesInformation + SystemVerifierThunkExtend, + SystemSessionProcessInformation, + SystemLoadGdiDriverInSystemSpace, + SystemNumaProcessorMap, + SystemPrefetcherInformation, + SystemExtendedProcessInformation, + SystemRecommendedSharedDataAlignment, + SystemComPlusPackage, + SystemNumaAvailableMemory, + SystemProcessorPowerInformation, + SystemEmulationBasicInformation, // WOW64 + SystemEmulationProcessorInformation, // WOW64 + SystemExtendedHandleInformation, + SystemLostDelayedWriteInformation, + SystemBigPoolInformation, + SystemSessionPoolTagInformation, + SystemSessionMappedViewInformation, + SystemHotpatchInformation, + SystemObjectSecurityMode, + SystemWatchdogTimerHandler, + SystemWatchdogTimerInformation, + SystemLogicalProcessorInformation, + SystemWow64SharedInformationObsolete, + SystemRegisterFirmwareTableInformationHandler, + SystemFirmwareTableInformation, + SystemModuleInformationEx, + SystemVerifierTriageInformation, + SystemSuperfetchInformation, + SystemMemoryListInformation, + SystemFileCacheInformationEx, + SystemThreadPriorityClientIdInformation, + SystemProcessorIdleCycleTimeInformation, + SystemVerifierCancellationInformation, + SystemProcessorPowerInformationEx, + SystemRefTraceInformation, + SystemSpecialPoolInformation, + SystemProcessIdInformation, + SystemErrorPortInformation, + SystemBootEnvironmentInformation, + SystemHypervisorInformation, + SystemVerifierInformationEx, + SystemTimeZoneInformation, + SystemImageFileExecutionOptionsInformation, + SystemCoverageInformation, + SystemPrefetchPatchInformation, + SystemVerifierFaultsInformation, + SystemSystemPartitionInformation, + SystemSystemDiskInformation, + SystemProcessorPerformanceDistribution, + SystemNumaProximityNodeInformation, + SystemDynamicTimeZoneInformation, + SystemCodeIntegrityInformation, + SystemProcessorMicrocodeUpdateInformation, + SystemProcessorBrandString, + SystemVirtualAddressInformation, + MaxSystemInfoClass } SYSTEM_INFORMATION_CLASS; typedef struct _MODULE_INFO { @@ -1711,10 +1783,11 @@ __declspec(dllimport) NTSTATUS NtUnloadDriver( //--------------------------------------------------------------------------- typedef enum _MEMORY_INFORMATION_CLASS { - MemoryBasicInformation, - MemoryWorkingSetList, - MemorySectionName, - MemoryBasicVlmInformation + MemoryBasicInformation, + MemoryWorkingSetInformation, + MemoryMappedFilenameInformation, + MemoryRegionInformation, + MemoryWorkingSetExInformation } MEMORY_INFORMATION_CLASS; __declspec(dllimport) NTSTATUS NtAllocateVirtualMemory( diff --git a/Sandboxie/core/dll/SboxDll32.def b/Sandboxie/core/dll/SboxDll32.def index 7dec6ca8..c8b219e2 100644 --- a/Sandboxie/core/dll/SboxDll32.def +++ b/Sandboxie/core/dll/SboxDll32.def @@ -26,7 +26,8 @@ SbieApi_GetFileName=_SbieApi_GetFileName@12 SbieApi_GetHomePath=_SbieApi_GetHomePath@16 SbieApi_GetUnmountHive=_SbieApi_GetUnmountHive@4 SbieApi_GetVersion=_SbieApi_GetVersion@4 -SbieApi_GetWork=_SbieApi_GetWork@12 +;;; SbieApi_GetWork=_SbieApi_GetWork@12 +SbieApi_GetMessage=_SbieApi_GetMessage@20 SbieApi_HookTramp=_SbieApi_HookTramp@8 @@ -34,6 +35,7 @@ SbieApi_IsBoxEnabled=_SbieApi_IsBoxEnabled@4 SbieApi_MonitorControl=_SbieApi_MonitorControl@8 SbieApi_MonitorGet=_SbieApi_MonitorGet@8 +SbieApi_MonitorGetEx=_SbieApi_MonitorGetEx@16 SbieApi_MonitorPut=_SbieApi_MonitorPut@8 SbieApi_MonitorPut2=_SbieApi_MonitorPut2@12 diff --git a/Sandboxie/core/dll/debug.c b/Sandboxie/core/dll/debug.c index 1f8367b2..9c34e114 100644 --- a/Sandboxie/core/dll/debug.c +++ b/Sandboxie/core/dll/debug.c @@ -202,6 +202,7 @@ _FX int Debug_Init(void) // break // + OutputDebugString(L"Dll_ImageName: "); OutputDebugString(Dll_ImageName); //if (_wcsicmp(Dll_ImageName, L"explorer.exe") == 0) { @@ -223,7 +224,6 @@ _FX int Debug_Init(void) if (0) #endif { - while (! IsDebuggerPresent()) { OutputDebugString(L"BREAK\n"); Sleep(500); diff --git a/Sandboxie/core/dll/debug.h b/Sandboxie/core/dll/debug.h index 0cae8582..2f34f52b 100644 --- a/Sandboxie/core/dll/debug.h +++ b/Sandboxie/core/dll/debug.h @@ -28,6 +28,7 @@ #undef WITH_DEBUG +//#define WITH_DEBUG //--------------------------------------------------------------------------- @@ -35,7 +36,8 @@ #ifdef WITH_DEBUG -#define BREAK_IMAGE_1 L"java.exe" // L"jp2launcher.exe" +//#define BREAK_IMAGE_1 L"java.exe" // L"jp2launcher.exe" +#define BREAK_IMAGE_1 L"TestTarget.exe" // L"jp2launcher.exe" int Debug_Init(void); diff --git a/Sandboxie/core/dll/dllmain.c b/Sandboxie/core/dll/dllmain.c index 095f5462..92125cea 100644 --- a/Sandboxie/core/dll/dllmain.c +++ b/Sandboxie/core/dll/dllmain.c @@ -211,6 +211,10 @@ _FX void Dll_InitInjected(void) ULONG BoxKeyPathLen; ULONG BoxIpcPathLen; +#ifdef WITH_DEBUG + OutputDebugString(L"SbieDll: Dll_InitInjected"); +#endif WITH_DEBUG + // // confirm the process is sandboxed before going further // @@ -690,7 +694,7 @@ _FX ULONG_PTR Dll_Ordinal1( { Ldr_Inject_Init(bHostInject); } - + // // conclude the detour by passing control back to the original // RtlFindActivationContextSectionString. the detour code used diff --git a/Sandboxie/core/dll/file.c b/Sandboxie/core/dll/file.c index 0de256a3..a08a62ff 100644 --- a/Sandboxie/core/dll/file.c +++ b/Sandboxie/core/dll/file.c @@ -461,7 +461,7 @@ _FX NTSTATUS File_GetName( if (! NT_SUCCESS(status)) return status; - uni = &((OBJECT_NAME_INFORMATION *)name)->ObjectName; + uni = &((OBJECT_NAME_INFORMATION *)name)->Name; #ifdef WOW64_FS_REDIR // diff --git a/Sandboxie/core/dll/file_init.c b/Sandboxie/core/dll/file_init.c index 3d98c1f3..3e6eb3f2 100644 --- a/Sandboxie/core/dll/file_init.c +++ b/Sandboxie/core/dll/file_init.c @@ -1730,7 +1730,7 @@ _FX void File_GetSetDeviceMap(WCHAR *DeviceMap96) } else { UNICODE_STRING *uni = - &((OBJECT_NAME_INFORMATION *)dirname)->ObjectName; + &((OBJECT_NAME_INFORMATION *)dirname)->Name; length = uni->Length / sizeof(WCHAR); if (length > 95) length = 95; diff --git a/Sandboxie/core/dll/ipc.c b/Sandboxie/core/dll/ipc.c index 333b809d..82de4581 100644 --- a/Sandboxie/core/dll/ipc.c +++ b/Sandboxie/core/dll/ipc.c @@ -575,7 +575,7 @@ _FX NTSTATUS Ipc_GetName( if (! NT_SUCCESS(status)) return status; - *OutTruePath = ((OBJECT_NAME_INFORMATION *)name)->ObjectName.Buffer; + *OutTruePath = ((OBJECT_NAME_INFORMATION *)name)->Name.Buffer; if (! *OutTruePath) { @@ -588,7 +588,7 @@ _FX NTSTATUS Ipc_GetName( } name = (*OutTruePath) - + ((OBJECT_NAME_INFORMATION *)name)->ObjectName.Length + + ((OBJECT_NAME_INFORMATION *)name)->Name.Length / sizeof(WCHAR); if (objname_len) { @@ -972,10 +972,10 @@ _FX void Ipc_AdjustPortPath(UNICODE_STRING *ObjectName) status = Obj_GetObjectName(handle, name, &length); if (NT_SUCCESS(status) && - name->ObjectName.Length >= ParentLength * sizeof(WCHAR) && - 0 == _wcsnicmp(name->ObjectName.Buffer, Buffer, ParentLength)) { + name->Name.Length >= ParentLength * sizeof(WCHAR) && + 0 == _wcsnicmp(name->Name.Buffer, Buffer, ParentLength)) { - wmemcpy(Buffer, name->ObjectName.Buffer, ParentLength); + wmemcpy(Buffer, name->Name.Buffer, ParentLength); } Dll_Free(name); diff --git a/Sandboxie/core/dll/ldr_init.c b/Sandboxie/core/dll/ldr_init.c index 63424880..e64d72bc 100644 --- a/Sandboxie/core/dll/ldr_init.c +++ b/Sandboxie/core/dll/ldr_init.c @@ -399,7 +399,7 @@ _FX void Ldr_FixImagePath(void) // // if this is a forced program, we need to get the image path from - // the kernel (i.e. NtQueryVirtualMemory with MemorySectionName), + // the kernel (i.e. NtQueryVirtualMemory with MemoryMappedFilenameInformation), // in case the image path contains symbolic links or reparse points. // such links would have been translated by NtCreateFile if the // program was started by another program in the sandbox. so if @@ -471,7 +471,7 @@ _FX WCHAR *Ldr_FixImagePath_2(void) BufferLength = 256; NameUni = Dll_AllocTemp((ULONG)BufferLength + sizeof(WCHAR) * 2); status = __sys_NtQueryVirtualMemory( - NtCurrentProcess(), (void *)Ldr_ImageBase, MemorySectionName, + NtCurrentProcess(), (void *)Ldr_ImageBase, MemoryMappedFilenameInformation, NameUni, BufferLength, &BufferLength); if (status == STATUS_INFO_LENGTH_MISMATCH || @@ -480,7 +480,7 @@ _FX WCHAR *Ldr_FixImagePath_2(void) Dll_Free(NameUni); NameUni = Dll_AllocTemp((ULONG)BufferLength + sizeof(WCHAR) * 2); status = __sys_NtQueryVirtualMemory( - NtCurrentProcess(), (void *)Ldr_ImageBase, MemorySectionName, + NtCurrentProcess(), (void *)Ldr_ImageBase, MemoryMappedFilenameInformation, NameUni, BufferLength, &BufferLength); } diff --git a/Sandboxie/core/dll/obj.c b/Sandboxie/core/dll/obj.c index a2bc6974..811f22fd 100644 --- a/Sandboxie/core/dll/obj.c +++ b/Sandboxie/core/dll/obj.c @@ -308,7 +308,7 @@ _FX NTSTATUS Obj_NtQueryVirtualMemory( // if the request is not for an object name, then call the system // - if (MemoryInformationClass != MemorySectionName) { + if (MemoryInformationClass != MemoryMappedFilenameInformation) { return __sys_NtQueryVirtualMemory( ProcessHandle, BaseAddress, MemoryInformationClass, @@ -328,7 +328,7 @@ _FX NTSTATUS Obj_NtQueryVirtualMemory( } status = __sys_NtQueryVirtualMemory( - ProcessHandle, BaseAddress, MemorySectionName, + ProcessHandle, BaseAddress, MemoryMappedFilenameInformation, name, maxlen, &outlen); if (status == STATUS_INFO_LENGTH_MISMATCH || @@ -340,7 +340,7 @@ _FX NTSTATUS Obj_NtQueryVirtualMemory( name = Dll_AllocTemp((ULONG)maxlen); status = __sys_NtQueryVirtualMemory( - ProcessHandle, BaseAddress, MemorySectionName, + ProcessHandle, BaseAddress, MemoryMappedFilenameInformation, name, maxlen, &outlen); } diff --git a/Sandboxie/core/dll/sbieapi.c b/Sandboxie/core/dll/sbieapi.c index a85e6762..2f848278 100644 --- a/Sandboxie/core/dll/sbieapi.c +++ b/Sandboxie/core/dll/sbieapi.c @@ -252,7 +252,7 @@ _FX LONG SbieApi_GetVersion( // SbieApi_GetWork //--------------------------------------------------------------------------- - +/* _FX LONG SbieApi_GetWork( ULONG SessionId, void *Buffer, @@ -273,6 +273,40 @@ _FX LONG SbieApi_GetWork( return status; } +*/ + +//--------------------------------------------------------------------------- +// SbieApi_GetMessage +//--------------------------------------------------------------------------- + + +_FX LONG SbieApi_GetMessage( + ULONG* MessageNum, + ULONG SessionId, + ULONG *MessageId, + wchar_t *Buffer, + ULONG Length) +{ + NTSTATUS status; + __declspec(align(8)) UNICODE_STRING64 msgtext; + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_GET_MESSAGE_ARGS *args = (API_GET_MESSAGE_ARGS *)parms; + + msgtext.MaximumLength = (USHORT)Length; + msgtext.Buffer = (ULONG_PTR)Buffer; + msgtext.Length = 0; + + memzero(parms, sizeof(parms)); + args->func_code = API_GET_MESSAGE; + args->msg_num.val = MessageNum; + args->session_id.val = SessionId; + args->msgid.val = MessageId; + args->msgtext.val = &msgtext; + + status = SbieApi_Ioctl(parms); + + return status; +} //--------------------------------------------------------------------------- @@ -1411,6 +1445,40 @@ _FX LONG SbieApi_MonitorGet( } +//--------------------------------------------------------------------------- +// SbieApi_MonitorGetEx +//--------------------------------------------------------------------------- + + +_FX LONG SbieApi_MonitorGetEx( + ULONG *SeqNum, + USHORT *Type, + ULONG64 *Pid, + WCHAR *Name) // WCHAR [256] +{ + NTSTATUS status; + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_MONITOR_GET_EX_ARGS *args = (API_MONITOR_GET_EX_ARGS *)parms; + + args->func_code = API_MONITOR_GET_EX; + args->name_seq.val64 = (ULONG64)(ULONG_PTR)SeqNum; + args->name_type.val64 = (ULONG64)(ULONG_PTR)Type; + args->name_pid.val64 = (ULONG64)(ULONG_PTR)Pid; + args->name_len.val64 = 256 * sizeof(WCHAR); + args->name_ptr.val64 = (ULONG64)(ULONG_PTR)Name; + status = SbieApi_Ioctl(parms); + + if (!NT_SUCCESS(status)) { + if (Type) + *Type = 0; + if (Name) + *Name = L'\0'; + } + + return status; +} + + //--------------------------------------------------------------------------- // SbieApi_GetUnmountHive //--------------------------------------------------------------------------- diff --git a/Sandboxie/core/dll/sbieapi.h b/Sandboxie/core/dll/sbieapi.h index 98bb82d3..f0967261 100644 --- a/Sandboxie/core/dll/sbieapi.h +++ b/Sandboxie/core/dll/sbieapi.h @@ -62,11 +62,19 @@ SBIEAPI_EXPORT SBIEAPI_EXPORT LONG SbieApi_GetVersion( WCHAR *version_string); // WCHAR [16] -SBIEAPI_EXPORT +/*SBIEAPI_EXPORT LONG SbieApi_GetWork( ULONG SessionId, void *Buffer, - ULONG *Length); + ULONG *Length);*/ + +SBIEAPI_EXPORT +LONG SbieApi_GetMessage( + ULONG* MessageNum, + ULONG SessionId, + ULONG *MessageId, + wchar_t *Buffer, + ULONG Length); SBIEAPI_EXPORT LONG SbieApi_Log( ULONG msgid, const WCHAR *format, ...); @@ -196,6 +204,13 @@ LONG SbieApi_MonitorGet( USHORT *Type, WCHAR *Name); // WCHAR [256] +SBIEAPI_EXPORT +LONG SbieApi_MonitorGetEx( + ULONG *SeqNum, + USHORT *Type, + ULONG64 *Pid, + WCHAR *Name); // WCHAR [256] + //--------------------------------------------------------------------------- @@ -288,8 +303,6 @@ LONG SbieApi_QuerySymbolicLink( SBIEAPI_EXPORT LONG SbieApi_ReloadConf(ULONG session_id); -SBIEAPI_EXPORT -LONG SbieApi_ReloadConf2(ULONG session_id, const WCHAR *config_path); SBIEAPI_EXPORT LONG SbieApi_QueryConf( diff --git a/Sandboxie/core/dll/scm_create.c b/Sandboxie/core/dll/scm_create.c index 833f19fe..eaeb233f 100644 --- a/Sandboxie/core/dll/scm_create.c +++ b/Sandboxie/core/dll/scm_create.c @@ -825,7 +825,7 @@ _FX BOOL SbieDll_StartBoxedService(const WCHAR *ServiceName, BOOLEAN WithAdd) ULONG retries, error; //WCHAR text[130]; - //Sbie_swprintf(text, L"StartBoxedService; name: '%s'; pid: %d", ServiceName, GetCurrentProcessId()); // fix-me: pottential buffer overflow + //Sbie_swprintf(text, L"StartBoxedService; name: '%s'", ServiceName); // fix-me: pottential buffer overflow //SbieApi_MonitorPut(MONITOR_OTHER, text); // @@ -1096,7 +1096,7 @@ _FX BOOL Scm_StartServiceW( return FALSE; WCHAR text[130]; - Sbie_swprintf(text, L"StartService; name: '%s'; pid: %d", ServiceName, GetCurrentProcessId()); // fix-me: pottential buffer overflow + Sbie_swprintf(text, L"StartService: %s", ServiceName); // fix-me: pottential buffer overflow SbieApi_MonitorPut(MONITOR_OTHER, text); if (Scm_IsBoxedService(ServiceName)) @@ -1263,7 +1263,7 @@ _FX BOOL Scm_StartServiceCtrlDispatcherX( } //WCHAR text[130]; - //Sbie_swprintf(text, L"StartServiceCtrlDispatcher; name: '%s'; pid %d", ServiceName, GetCurrentProcessId()); // fix-me: pottential buffer overflow + //Sbie_swprintf(text, L"StartServiceCtrlDispatcher; name: '%s'", ServiceName); // fix-me: pottential buffer overflow //SbieApi_MonitorPut(MONITOR_OTHER, text); // diff --git a/Sandboxie/core/dll/secure.c b/Sandboxie/core/dll/secure.c index 4d7a0477..030c3b8f 100644 --- a/Sandboxie/core/dll/secure.c +++ b/Sandboxie/core/dll/secure.c @@ -668,7 +668,7 @@ _FX NTSTATUS Secure_NtSetSecurityObject( OBJECT_NAME_INFORMATION *name = (OBJECT_NAME_INFORMATION *)name_space; status = Obj_GetObjectName(Handle, name, &name_len); - if (NT_SUCCESS(status) && name->ObjectName.Length == 0) { + if (NT_SUCCESS(status) && name->Name.Length == 0) { IsUnnamedObject = TRUE; } diff --git a/Sandboxie/core/dll/support.c b/Sandboxie/core/dll/support.c index 10a71675..0518e1d8 100644 --- a/Sandboxie/core/dll/support.c +++ b/Sandboxie/core/dll/support.c @@ -636,6 +636,7 @@ static UCHAR Support_BuiltinDomainRid[12] = { #endif /* SBIEDLL_FORMATMESSAGE_ONLY */ +#if 1 //--------------------------------------------------------------------------- // SbieDll_FormatMessage_2 @@ -844,3 +845,5 @@ _FX WCHAR *SbieDll_FormatMessage2( ins[2] = ins2; return SbieDll_FormatMessage(code, ins); } + +#endif \ No newline at end of file diff --git a/Sandboxie/core/drv/SboxDrv.vcxproj b/Sandboxie/core/drv/SboxDrv.vcxproj index 4bc83364..60f54022 100644 --- a/Sandboxie/core/drv/SboxDrv.vcxproj +++ b/Sandboxie/core/drv/SboxDrv.vcxproj @@ -304,6 +304,7 @@ + @@ -384,6 +385,7 @@ + diff --git a/Sandboxie/core/drv/api.c b/Sandboxie/core/drv/api.c index 12185839..16262777 100644 --- a/Sandboxie/core/drv/api.c +++ b/Sandboxie/core/drv/api.c @@ -25,6 +25,7 @@ #include "util.h" #include "hook.h" #include "common/my_version.h" +#include "log_buff.h" //--------------------------------------------------------------------------- @@ -49,7 +50,7 @@ static BOOLEAN Api_FastIo_DEVICE_CONTROL( ULONG IoControlCode, IO_STATUS_BLOCK *IoStatus, DEVICE_OBJECT *DeviceObject); -static void Api_DelWork(API_WORK_ITEM *work_item); +//static void Api_DelWork(API_WORK_ITEM *work_item); //--------------------------------------------------------------------------- @@ -59,6 +60,8 @@ static NTSTATUS Api_GetVersion(PROCESS *proc, ULONG64 *parms); static NTSTATUS Api_LogMessage(PROCESS *proc, ULONG64 *parms); +static NTSTATUS Api_GetMessage(PROCESS *proc, ULONG64 *parms); + static NTSTATUS Api_GetWork(PROCESS *proc, ULONG64 *parms); static NTSTATUS Api_GetHomePath(PROCESS *proc, ULONG64 *parms); @@ -92,9 +95,11 @@ volatile HANDLE Api_ServiceProcessId = NULL; static PERESOURCE Api_LockResource = NULL; -static LIST Api_WorkList; +//static LIST Api_WorkList; static BOOLEAN Api_WorkListInitialized = FALSE; +static LOG_BUFFER* Api_LogBuffer = NULL; + static volatile LONG Api_UseCount = -1; @@ -108,11 +113,17 @@ _FX BOOLEAN Api_Init(void) NTSTATUS status; UNICODE_STRING uni; + // + // initialize log buffer + // + + Api_LogBuffer = log_buffer_init(8 * 8 * 1024); + // // initialize work list // - List_Init(&Api_WorkList); + //List_Init(&Api_WorkList); if (! Mem_GetLockResource(&Api_LockResource, TRUE)) return FALSE; @@ -168,6 +179,7 @@ _FX BOOLEAN Api_Init(void) Api_SetFunction(API_GET_VERSION, Api_GetVersion); Api_SetFunction(API_GET_WORK, Api_GetWork); Api_SetFunction(API_LOG_MESSAGE, Api_LogMessage); + Api_SetFunction(API_GET_MESSAGE, Api_GetMessage); Api_SetFunction(API_GET_HOME_PATH, Api_GetHomePath); Api_SetFunction(API_SET_SERVICE_PORT, Api_SetServicePort); @@ -195,8 +207,6 @@ _FX BOOLEAN Api_Init(void) _FX void Api_Unload(void) { - API_WORK_ITEM *work_item; - if (Api_DeviceObject) { IoDeleteDevice(Api_DeviceObject); Api_DeviceObject = NULL; @@ -209,12 +219,18 @@ _FX void Api_Unload(void) if (Api_WorkListInitialized) { - while (1) { + if (Api_LogBuffer) { + log_buffer_free(Api_LogBuffer); + Api_LogBuffer = NULL; + } + + /*API_WORK_ITEM *work_item; + while (1) { work_item = List_Head(&Api_WorkList); if (! work_item) break; Api_DelWork(work_item); - } + }*/ Mem_FreeLockResource(&Api_LockResource); @@ -612,6 +628,136 @@ _FX NTSTATUS Api_LogMessage(PROCESS *proc, ULONG64 *parms) } +//--------------------------------------------------------------------------- +// Api_AddMessage +//--------------------------------------------------------------------------- + + +_FX void Api_AddMessage( + NTSTATUS error_code, + const WCHAR *string1, ULONG string1_len, + const WCHAR *string2, ULONG string2_len, + ULONG session_id) +{ + KIRQL irql; + + if (!Api_WorkListInitialized) // if (!Api_LogBuffer) + return; + + // + // add work at the end of the work list + // + + irql = Api_EnterCriticalSection(); + + ULONG entry_size = sizeof(ULONG) // session_id + + sizeof(ULONG) // error_code + + (string1_len + 1) * sizeof(WCHAR) + + (string2_len + 1) * sizeof(WCHAR); + + CHAR* write_ptr = log_buffer_push_entry((LOG_BUFFER_SIZE_T)entry_size, Api_LogBuffer); + if (write_ptr) { + //[session_id 4][error_code 4][string1 n*2][\0 2][string2 n*2][\0 2] + WCHAR null_char = L'\0'; + log_buffer_push_bytes((CHAR*)&session_id, sizeof(ULONG), &write_ptr, Api_LogBuffer); + log_buffer_push_bytes((CHAR*)&error_code, sizeof(ULONG), &write_ptr, Api_LogBuffer); + log_buffer_push_bytes((CHAR*)string1, string1_len * sizeof(WCHAR), &write_ptr, Api_LogBuffer); + log_buffer_push_bytes((CHAR*)&null_char, sizeof(WCHAR), &write_ptr, Api_LogBuffer); + log_buffer_push_bytes((CHAR*)string2, string2_len * sizeof(WCHAR), &write_ptr, Api_LogBuffer); + log_buffer_push_bytes((CHAR*)&null_char, sizeof(WCHAR), &write_ptr, Api_LogBuffer); + } + // else // this can only happen when the entire buffer is to small to hold this entire entry + // if loging fails we can't log this error :/ + + Api_LeaveCriticalSection(irql); +} + + +//--------------------------------------------------------------------------- +// Api_GetMessage +//--------------------------------------------------------------------------- + + +_FX NTSTATUS Api_GetMessage(PROCESS *proc, ULONG64 *parms) +{ + API_GET_MESSAGE_ARGS *args = (API_GET_MESSAGE_ARGS *)parms; + NTSTATUS status = STATUS_SUCCESS; + UNICODE_STRING64 *msgtext; + WCHAR *msgtext_buffer; + KIRQL irql; + + if (proc) + return STATUS_NOT_IMPLEMENTED; + + ProbeForRead(args->msg_num.val, sizeof(ULONG), sizeof(ULONG)); + ProbeForWrite(args->msg_num.val, sizeof(ULONG), sizeof(ULONG)); + + ProbeForWrite(args->msgid.val, sizeof(ULONG), sizeof(ULONG)); + + msgtext = args->msgtext.val; + if (!msgtext) + return STATUS_INVALID_PARAMETER; + ProbeForRead(msgtext, sizeof(UNICODE_STRING64), sizeof(ULONG)); + ProbeForWrite(msgtext, sizeof(UNICODE_STRING64), sizeof(ULONG)); + + msgtext_buffer = (WCHAR *)msgtext->Buffer; + if (!msgtext_buffer) + return STATUS_INVALID_PARAMETER; + + irql = Api_EnterCriticalSection(); + + __try { + + CHAR* read_ptr = log_buffer_get_next(*args->msg_num.val, Api_LogBuffer); + + if (!read_ptr) { + + status = STATUS_NO_MORE_ENTRIES; + + } else { + + LOG_BUFFER_SIZE_T entry_size = log_buffer_get_size(&read_ptr, Api_LogBuffer); + LOG_BUFFER_SEQ_T seq_number = log_buffer_get_seq_num(&read_ptr, Api_LogBuffer); + *args->msg_num.val = seq_number; + //[session_id 4][error_code 4][string1 n*2][\0 2][string2 n*2][\0 2] + ULONG session_id; + log_buffer_get_bytes((CHAR*)&session_id, 4, &read_ptr, Api_LogBuffer); + + if (session_id == args->session_id.val) { + + log_buffer_get_bytes((CHAR*)args->msgid.val, 4, &read_ptr, Api_LogBuffer); + SIZE_T msg_length = entry_size - (4 + 4); + + if (msg_length <= msgtext->MaximumLength) + { + msgtext->Length = (USHORT)msg_length; + ProbeForWrite(msgtext_buffer, msg_length, sizeof(WCHAR)); + memcpy(msgtext_buffer, read_ptr, msg_length); + } + else + { + status = STATUS_BUFFER_TOO_SMALL; + } + + } else { + // this entry is not for us, so we return an empty result to maintain sequence consistency + + *args->msgid.val = 0; + + } + + } + + } __except (EXCEPTION_EXECUTE_HANDLER) { + status = GetExceptionCode(); + } + + Api_LeaveCriticalSection(irql); + + return status; +} + + //--------------------------------------------------------------------------- // Api_SendServiceMessage //--------------------------------------------------------------------------- @@ -709,7 +855,7 @@ _FX BOOLEAN Api_SendServiceMessage(ULONG msgid, ULONG data_len, void *data) //--------------------------------------------------------------------------- -_FX BOOLEAN Api_AddWork(API_WORK_ITEM *work_item) +/*_FX BOOLEAN Api_AddWork(API_WORK_ITEM *work_item) { KIRQL irql; @@ -734,7 +880,7 @@ _FX BOOLEAN Api_AddWork(API_WORK_ITEM *work_item) return TRUE; return TRUE; -} +}*/ //--------------------------------------------------------------------------- @@ -742,13 +888,13 @@ _FX BOOLEAN Api_AddWork(API_WORK_ITEM *work_item) //--------------------------------------------------------------------------- -_FX void Api_DelWork(API_WORK_ITEM *work_item) +/*_FX void Api_DelWork(API_WORK_ITEM *work_item) { // this assumes Api_WorkList is already locked using Api_Lock List_Remove(&Api_WorkList, work_item); Mem_Free(work_item, work_item->length); -} +}*/ //--------------------------------------------------------------------------- @@ -758,7 +904,9 @@ _FX void Api_DelWork(API_WORK_ITEM *work_item) _FX NTSTATUS Api_GetWork(PROCESS *proc, ULONG64 *parms) { - API_GET_WORK_ARGS *args = (API_GET_WORK_ARGS *)parms; + return STATUS_NOT_IMPLEMENTED; + + /*API_GET_WORK_ARGS *args = (API_GET_WORK_ARGS *)parms; NTSTATUS status; void *buffer_ptr; ULONG buffer_len; @@ -834,7 +982,7 @@ _FX NTSTATUS Api_GetWork(PROCESS *proc, ULONG64 *parms) Api_LeaveCriticalSection(irql); - return status; + return status;*/ } diff --git a/Sandboxie/core/drv/api.h b/Sandboxie/core/drv/api.h index 8459af45..8458c5b8 100644 --- a/Sandboxie/core/drv/api.h +++ b/Sandboxie/core/drv/api.h @@ -38,7 +38,7 @@ //--------------------------------------------------------------------------- -typedef struct _API_WORK_ITEM { +/*typedef struct _API_WORK_ITEM { LIST_ELEM list_elem; ULONG length; // length includes both header and data @@ -47,7 +47,7 @@ typedef struct _API_WORK_ITEM { ULONG data[1]; -} API_WORK_ITEM; +} API_WORK_ITEM;*/ typedef struct _Sbie_SeFilterTokenArg { @@ -110,7 +110,19 @@ BOOLEAN Api_SendServiceMessage(ULONG msgid, ULONG data_len, void *data); // allocate work_item from Driver_Pool, and initialize type, length and data // -BOOLEAN Api_AddWork(API_WORK_ITEM *work_item); +//BOOLEAN Api_AddWork(API_WORK_ITEM *work_item); + + +// +// Add message to log buffer +// + +void Api_AddMessage( + NTSTATUS error_code, + const WCHAR *string1, ULONG string1_len, + const WCHAR *string2, ULONG string2_len, + ULONG session_id); + // // Copies boxname parameter from user diff --git a/Sandboxie/core/drv/api_defs.h b/Sandboxie/core/drv/api_defs.h index 31744fe7..65d1b38b 100644 --- a/Sandboxie/core/drv/api_defs.h +++ b/Sandboxie/core/drv/api_defs.h @@ -147,6 +147,8 @@ enum { API_GET_WPAD_PORT, API_SET_GAME_CONFIG_STORE_PORT, API_SET_SMART_CARD_PORT, + API_MONITOR_GET_EX, + API_GET_MESSAGE, API_LAST }; @@ -189,12 +191,12 @@ API_ARGS_FIELD(WCHAR *,string) API_ARGS_CLOSE(API_GET_VERSION_ARGS) -API_ARGS_BEGIN(API_GET_WORK_ARGS) +/*API_ARGS_BEGIN(API_GET_WORK_ARGS) API_ARGS_FIELD(ULONG,session_id) API_ARGS_FIELD(void *,buffer) API_ARGS_FIELD(ULONG,buffer_len) API_ARGS_FIELD(ULONG *,result_len_ptr) -API_ARGS_CLOSE(API_GET_WORK_ARGS) +API_ARGS_CLOSE(API_GET_WORK_ARGS)*/ API_ARGS_BEGIN(API_LOG_MESSAGE_ARGS) @@ -203,6 +205,12 @@ API_ARGS_FIELD(ULONG,msgid) API_ARGS_FIELD(UNICODE_STRING64 *,msgtext) API_ARGS_CLOSE(API_LOG_MESSAGE_ARGS) +API_ARGS_BEGIN(API_GET_MESSAGE_ARGS) +API_ARGS_FIELD(ULONG *, msg_num) +API_ARGS_FIELD(ULONG, session_id) +API_ARGS_FIELD(ULONG *, msgid) +API_ARGS_FIELD(UNICODE_STRING64 *, msgtext) +API_ARGS_CLOSE(API_GET_MESSAGE_ARGS) API_ARGS_BEGIN(API_QUERY_PROCESS_ARGS) API_ARGS_FIELD(HANDLE,process_id) @@ -311,6 +319,14 @@ API_ARGS_FIELD(ULONG,name_len) API_ARGS_FIELD(WCHAR *,name_ptr) API_ARGS_CLOSE(API_MONITOR_GET_PUT_ARGS) +API_ARGS_BEGIN(API_MONITOR_GET_EX_ARGS) +API_ARGS_FIELD(ULONG *, name_seq) +API_ARGS_FIELD(USHORT *, name_type) +API_ARGS_FIELD(ULONG64 *, name_pid) +API_ARGS_FIELD(ULONG, name_len) +API_ARGS_FIELD(WCHAR *, name_ptr) +API_ARGS_CLOSE(API_MONITOR_GET_EX_ARGS) + API_ARGS_BEGIN(API_MONITOR_PUT2_ARGS) API_ARGS_FIELD(USHORT *,name_type) API_ARGS_FIELD(ULONG,name_len) diff --git a/Sandboxie/core/drv/file.c b/Sandboxie/core/drv/file.c index d790df1d..f3704d2b 100644 --- a/Sandboxie/core/drv/file.c +++ b/Sandboxie/core/drv/file.c @@ -989,7 +989,7 @@ _FX NTSTATUS File_Generic_MyParseProc( if (Session_MonitorCount && device_type != FILE_DEVICE_PHYSICAL_NETCARD) - Session_MonitorPut(MONITOR_IGNORE, ignore_str + 4); + Session_MonitorPut(MONITOR_IGNORE, ignore_str + 4, proc->pid); Mem_Free(ignore_str, ignore_str_len); } @@ -1504,12 +1504,11 @@ skip_due_to_home_folder: mon_type |= MONITOR_OPEN; else mon_type |= MONITOR_DENY; - Session_MonitorPut(mon_type, mon_name); + Session_MonitorPut(mon_type, mon_name, proc->pid); } else if (ShouldMonitorAccess) { - Session_MonitorPut( - MONITOR_FILE_OR_KEY | MONITOR_DENY, Name->Name.Buffer); + Session_MonitorPut(MONITOR_FILE_OR_KEY | MONITOR_DENY, Name->Name.Buffer, proc->pid); } else if (msg1313 && status == STATUS_ACCESS_DENIED && device_type == FILE_DEVICE_DISK diff --git a/Sandboxie/core/drv/gui_xp.c b/Sandboxie/core/drv/gui_xp.c index 84461781..13b3a36f 100644 --- a/Sandboxie/core/drv/gui_xp.c +++ b/Sandboxie/core/drv/gui_xp.c @@ -1334,7 +1334,7 @@ _FX ULONG_PTR Gui_NtUserPostThreadMessage( --nptr; *nptr = L':'; --nptr; *nptr = L'$'; - Session_MonitorPut(mon_type, nptr); + Session_MonitorPut(mon_type, nptr, proc->pid); Mem_Free(nbuf, nlen); } diff --git a/Sandboxie/core/drv/ipc.c b/Sandboxie/core/drv/ipc.c index 143b4e07..bed0d356 100644 --- a/Sandboxie/core/drv/ipc.c +++ b/Sandboxie/core/drv/ipc.c @@ -940,7 +940,7 @@ _FX NTSTATUS Ipc_CheckGenericObject( mon_type |= MONITOR_OPEN; else mon_type |= MONITOR_DENY; - Session_MonitorPut(mon_type, mon_name); + Session_MonitorPut(mon_type, mon_name, proc->pid); } // DbgPrint("Process <%06d> Status <%08X> Object <%S>\n", proc->pid, status, Name->Name.Buffer); diff --git a/Sandboxie/core/drv/key.c b/Sandboxie/core/drv/key.c index f8d3eb72..03fa7b63 100644 --- a/Sandboxie/core/drv/key.c +++ b/Sandboxie/core/drv/key.c @@ -470,8 +470,7 @@ _FX NTSTATUS Key_MyParseProc_2(OBJ_PARSE_PROC_ARGS_2) if (ShouldMonitorAccess) { - Session_MonitorPut( - MONITOR_FILE_OR_KEY | MONITOR_DENY, Name->Name.Buffer); + Session_MonitorPut(MONITOR_FILE_OR_KEY | MONITOR_DENY, Name->Name.Buffer, proc->pid); } Mem_Free(Name, NameLength); diff --git a/Sandboxie/core/drv/log.c b/Sandboxie/core/drv/log.c index a53a537e..d3fdff8d 100644 --- a/Sandboxie/core/drv/log.c +++ b/Sandboxie/core/drv/log.c @@ -25,7 +25,6 @@ #include "api.h" #include "util.h" - //--------------------------------------------------------------------------- // Functions //--------------------------------------------------------------------------- @@ -36,11 +35,11 @@ static void Log_Event_Msg( const WCHAR *string1, const WCHAR *string2); -static void Log_Popup_Msg_2( +/*static void Log_Popup_Msg_2( NTSTATUS error_code, const WCHAR *string1, ULONG string1_len, const WCHAR *string2, ULONG string2_len, - ULONG session_id); + ULONG session_id);*/ //--------------------------------------------------------------------------- @@ -133,14 +132,16 @@ _FX void Log_Popup_Msg( if ((Driver_OsVersion >= DRIVER_WINDOWS_VISTA) && (session_id == 0)) session_id = 1; - Log_Popup_Msg_2( + //Log_Popup_Msg_2( + Api_AddMessage( error_code, string1, string1_len, string2, string2_len, session_id); // // log message to SbieSvc and trigger SbieSvc to wake up and collect it // - Log_Popup_Msg_2( + //Log_Popup_Msg_2( + Api_AddMessage( error_code, string1, string1_len, string2, string2_len, -1); string1_len = 0; @@ -155,7 +156,7 @@ _FX void Log_Popup_Msg( //--------------------------------------------------------------------------- -_FX void Log_Popup_Msg_2( +/*_FX void Log_Popup_Msg_2( NTSTATUS error_code, const WCHAR *string1, ULONG string1_len, const WCHAR *string2, ULONG string2_len, @@ -202,7 +203,7 @@ _FX void Log_Popup_Msg_2( Api_AddWork(work_item); } -} +}*/ //--------------------------------------------------------------------------- diff --git a/Sandboxie/core/drv/session.c b/Sandboxie/core/drv/session.c index 8ce8663a..7105ea15 100644 --- a/Sandboxie/core/drv/session.c +++ b/Sandboxie/core/drv/session.c @@ -26,6 +26,7 @@ #include "api.h" #include "process.h" #include "obj.h" +#include "log_buff.h" //--------------------------------------------------------------------------- @@ -70,9 +71,7 @@ struct _SESSION { // resource monitor // - WCHAR *monitor_buf; - WCHAR *monitor_read_ptr; - WCHAR *monitor_write_ptr; + LOG_BUFFER* monitor_log; }; @@ -110,6 +109,8 @@ static NTSTATUS Session_Api_MonitorPut2(PROCESS *proc, ULONG64 *parms); static NTSTATUS Session_Api_MonitorGet(PROCESS *proc, ULONG64 *parms); +static NTSTATUS Session_Api_MonitorGetEx(PROCESS *proc, ULONG64 *parms); + //--------------------------------------------------------------------------- @@ -150,6 +151,7 @@ _FX BOOLEAN Session_Init(void) Api_SetFunction(API_MONITOR_PUT, Session_Api_MonitorPut); Api_SetFunction(API_MONITOR_PUT2, Session_Api_MonitorPut2); Api_SetFunction(API_MONITOR_GET, Session_Api_MonitorGet); + Api_SetFunction(API_MONITOR_GET_EX, Session_Api_MonitorGetEx); // // initialize set of recognized objects types for Session_Api_MonitorPut @@ -338,8 +340,8 @@ _FX void Session_Cancel(HANDLE ProcessId) while (session) { if ((session->leader_pid == ProcessId) || (! ProcessId)) { - if (session->monitor_buf) { - ExFreePoolWithTag(session->monitor_buf, tzuk); + if (session->monitor_log) { + log_buffer_free(session->monitor_log); InterlockedDecrement(&Session_MonitorCount); } @@ -573,7 +575,7 @@ _FX BOOLEAN Session_IsForceDisabled(ULONG SessionId) //--------------------------------------------------------------------------- -_FX void Session_MonitorPut(USHORT type, const WCHAR *name) +_FX void Session_MonitorPut(USHORT type, const WCHAR *name, HANDLE pid) { SESSION *session; KIRQL irql; @@ -582,41 +584,22 @@ _FX void Session_MonitorPut(USHORT type, const WCHAR *name) if (! session) return; - if (session->monitor_buf && *name) { + if (session->monitor_log && *name) { - WCHAR *buf0 = session->monitor_buf; - WCHAR *wptr = session->monitor_write_ptr; - WCHAR *rptr = session->monitor_read_ptr; + ULONG64 pid64 = (ULONG64)pid; + SIZE_T data_len = (wcslen(name) + 1) * 2; - ULONG name_len = wcslen(name) + 1; + //[Type 2][PID 8][Data n*2] + SIZE_T entry_size = 2 + 8 + data_len; - BOOLEAN first = TRUE; - - while (name_len) { - - if (first) { - first = FALSE; - *wptr = type; - } else { - *wptr = *name; - ++name; - --name_len; - } - - ++wptr; - if (wptr >= buf0 + SESSION_MONITOR_BUF_SIZE) - wptr = buf0; - if (wptr == rptr) { - Log_Msg0(MSG_MONITOR_OVERFLOW); - *buf0 = L'\0'; - wptr = buf0; - session->monitor_read_ptr = buf0; - break; - } - } - - *wptr = L'\0'; - session->monitor_write_ptr = wptr; + CHAR* write_ptr = log_buffer_push_entry((LOG_BUFFER_SIZE_T)entry_size, session->monitor_log); + if (write_ptr) { + log_buffer_push_bytes((CHAR*)&type, 2, &write_ptr, session->monitor_log); + log_buffer_push_bytes((CHAR*)&pid64, 8, &write_ptr, session->monitor_log); + log_buffer_push_bytes((CHAR*)name, data_len, &write_ptr, session->monitor_log); + } + else // this can only happen when the entire buffer is to small to hold this entire entry + Log_Msg0(MSG_MONITOR_OVERFLOW); } Session_Unlock(irql); @@ -650,7 +633,7 @@ _FX NTSTATUS Session_Api_MonitorControl(PROCESS *proc, ULONG64 *parms) *out_flag = FALSE; session = Session_Get(FALSE, -1, &irql); if (session) { - if (session->monitor_buf) + if (session->monitor_log) *out_flag = TRUE; Session_Unlock(irql); } @@ -676,24 +659,18 @@ _FX NTSTATUS Session_Api_MonitorControl(PROCESS *proc, ULONG64 *parms) session = Session_Get(FALSE, -1, &irql); if (session) { - if (EnableMonitor && (! session->monitor_buf)) { + if (EnableMonitor && (! session->monitor_log)) { - session->monitor_buf = ExAllocatePoolWithTag(PagedPool, - SESSION_MONITOR_BUF_SIZE * sizeof(WCHAR), tzuk); - session->monitor_write_ptr = session->monitor_buf; - session->monitor_read_ptr = session->monitor_buf; - if (session->monitor_buf) { - *session->monitor_buf = L'\0'; + session->monitor_log = log_buffer_init(SESSION_MONITOR_BUF_SIZE * sizeof(WCHAR)); + if (session->monitor_log) { InterlockedIncrement(&Session_MonitorCount); } else Log_Msg0(MSG_1201); - } else if ((! EnableMonitor) && session->monitor_buf) { + } else if ((! EnableMonitor) && session->monitor_log) { - ExFreePoolWithTag(session->monitor_buf, tzuk); - session->monitor_buf = NULL; - session->monitor_write_ptr = session->monitor_buf; - session->monitor_read_ptr = session->monitor_buf; + log_buffer_free(session->monitor_log); + session->monitor_log = NULL; InterlockedDecrement(&Session_MonitorCount); } @@ -895,7 +872,7 @@ _FX NTSTATUS Session_Api_MonitorPut2(PROCESS *proc, ULONG64 *parms) name[1] = L'\0'; } - Session_MonitorPut(type, name); + Session_MonitorPut(type, name, proc->pid); } Mem_Free(name, 260 * sizeof(WCHAR)); @@ -908,12 +885,25 @@ _FX NTSTATUS Session_Api_MonitorPut2(PROCESS *proc, ULONG64 *parms) // Session_Api_MonitorGet //--------------------------------------------------------------------------- - _FX NTSTATUS Session_Api_MonitorGet(PROCESS *proc, ULONG64 *parms) { - API_MONITOR_GET_PUT_ARGS *args = (API_MONITOR_GET_PUT_ARGS *)parms; + API_MONITOR_GET_PUT_ARGS *args = (API_MONITOR_GET_PUT_ARGS *)parms; + API_MONITOR_GET_EX_ARGS args2 = { args->func_code, 0, args->name_type.val64, 0, args->name_len.val64, args->name_ptr.val64 }; + + return Session_Api_MonitorGetEx(proc, (ULONG64*)&args2); +} + +//--------------------------------------------------------------------------- +// Session_Api_MonitorGet +//--------------------------------------------------------------------------- + +_FX NTSTATUS Session_Api_MonitorGetEx(PROCESS *proc, ULONG64 *parms) +{ + API_MONITOR_GET_EX_ARGS *args = (API_MONITOR_GET_EX_ARGS *)parms; NTSTATUS status; + ULONG *seq_num; USHORT *user_type; + ULONG64 *user_pid; ULONG name_len; WCHAR *user_name; SESSION *session; @@ -922,9 +912,19 @@ _FX NTSTATUS Session_Api_MonitorGet(PROCESS *proc, ULONG64 *parms) if (proc) return STATUS_NOT_IMPLEMENTED; + seq_num = args->name_seq.val; + if (seq_num != NULL) { + ProbeForRead(seq_num, sizeof(ULONG), sizeof(ULONG)); + ProbeForWrite(seq_num, sizeof(ULONG), sizeof(ULONG)); + } + user_type = args->name_type.val; ProbeForWrite(user_type, sizeof(USHORT), sizeof(USHORT)); + user_pid = args->name_pid.val; + if(user_pid != NULL) + ProbeForWrite(user_pid, sizeof(ULONG64), sizeof(ULONG64)); + name_len = args->name_len.val / sizeof(WCHAR); if ((! name_len) || name_len > 256) return STATUS_INVALID_PARAMETER; @@ -932,6 +932,8 @@ _FX NTSTATUS Session_Api_MonitorGet(PROCESS *proc, ULONG64 *parms) ProbeForWrite(user_name, name_len * sizeof(WCHAR), sizeof(WCHAR)); *user_type = 0; + if (user_pid != NULL) + *user_pid = 0; *user_name = L'\0'; status = STATUS_SUCCESS; @@ -941,39 +943,31 @@ _FX NTSTATUS Session_Api_MonitorGet(PROCESS *proc, ULONG64 *parms) __try { - if (session->monitor_buf) { + if (session->monitor_log) { - WCHAR *buf0 = session->monitor_buf; - WCHAR *rptr = session->monitor_read_ptr; - BOOLEAN first = TRUE; + CHAR* read_ptr = NULL; + if (seq_num != NULL) + read_ptr = log_buffer_get_next(*seq_num, session->monitor_log); + else if (session->monitor_log->buffer_size > 0) // for compatybility with older versions we return the oldest entry + read_ptr = session->monitor_log->buffer_start_ptr; - if (*rptr) { + if (read_ptr != NULL) { + LOG_BUFFER_SIZE_T entry_size = log_buffer_get_size(&read_ptr, session->monitor_log); + LOG_BUFFER_SEQ_T seq_number = log_buffer_get_seq_num(&read_ptr, session->monitor_log); + if (seq_num != NULL) + *seq_num = seq_number; + //[Type 2][PID 8][Data n*2] + log_buffer_get_bytes((CHAR*)user_type, 2, &read_ptr, session->monitor_log); + ULONG64 pid64; + log_buffer_get_bytes((CHAR*)&pid64, 8, &read_ptr, session->monitor_log); + if (user_pid != NULL) + *user_pid = pid64; + log_buffer_get_bytes((CHAR*)user_name, entry_size - (2 + 8), &read_ptr, session->monitor_log); + } - while (*rptr) { - - if (first) { - first = FALSE; - *user_type = *rptr; - } else if (name_len) { - *user_name = *rptr; - ++user_name; - --name_len; - } else - status = STATUS_BUFFER_OVERFLOW; - - ++rptr; - if (rptr >= buf0 + SESSION_MONITOR_BUF_SIZE) - rptr = buf0; - } - - ++rptr; - if (rptr >= buf0 + SESSION_MONITOR_BUF_SIZE) - rptr = buf0; - session->monitor_read_ptr = rptr; - - if (name_len) - *user_name = L'\0'; - } + // for compatybility with older versions we fall back to clearing the returned entry + if (seq_num != NULL) + log_buffer_pop_entry(session->monitor_log); } } __except (EXCEPTION_EXECUTE_HANDLER) { diff --git a/Sandboxie/core/drv/session.h b/Sandboxie/core/drv/session.h index 25a6da99..86dd5e4d 100644 --- a/Sandboxie/core/drv/session.h +++ b/Sandboxie/core/drv/session.h @@ -41,7 +41,7 @@ void Session_Cancel(HANDLE ProcessId); BOOLEAN Session_IsForceDisabled(ULONG SessionId); -void Session_MonitorPut(USHORT type, const WCHAR *name); +void Session_MonitorPut(USHORT type, const WCHAR *name, HANDLE pid); //--------------------------------------------------------------------------- diff --git a/Sandboxie/core/drv/thread.c b/Sandboxie/core/drv/thread.c index 9aebfbeb..7dd38c28 100644 --- a/Sandboxie/core/drv/thread.c +++ b/Sandboxie/core/drv/thread.c @@ -1034,7 +1034,7 @@ _FX NTSTATUS Thread_CheckObject_Common( --nptr; *nptr = L':'; --nptr; *nptr = L'$'; - Session_MonitorPut(mon_type, nptr); + Session_MonitorPut(mon_type, nptr, proc->pid); Mem_Free(nbuf, nlen); } diff --git a/Sandboxie/core/svc/DriverAssist.cpp b/Sandboxie/core/svc/DriverAssist.cpp index 07f24e1c..553b0b7d 100644 --- a/Sandboxie/core/svc/DriverAssist.cpp +++ b/Sandboxie/core/svc/DriverAssist.cpp @@ -58,6 +58,8 @@ DriverAssist::DriverAssist() m_Threads = NULL; m_DriverReady = false; + m_last_message_number = 0; + InitializeCriticalSection(&m_LogMessage_CritSec); InitializeCriticalSection(&m_critSecHostInjectedSvcs); } diff --git a/Sandboxie/core/svc/DriverAssist.h b/Sandboxie/core/svc/DriverAssist.h index 7b242249..5ccd62d6 100644 --- a/Sandboxie/core/svc/DriverAssist.h +++ b/Sandboxie/core/svc/DriverAssist.h @@ -96,7 +96,7 @@ private: void LogMessage(); - void LogMessage_Single(void *data); + void LogMessage_Single(ULONG code, wchar_t* data); void LogMessage_Multi(ULONG msgid, const WCHAR *path, const WCHAR *text); void LogMessage_Write(const WCHAR *path, const WCHAR *text); @@ -139,6 +139,8 @@ private: volatile bool m_DriverReady; + ULONG m_last_message_number; + // // critical sections // diff --git a/Sandboxie/core/svc/DriverAssistLog.cpp b/Sandboxie/core/svc/DriverAssistLog.cpp index 56718e40..b521908b 100644 --- a/Sandboxie/core/svc/DriverAssistLog.cpp +++ b/Sandboxie/core/svc/DriverAssistLog.cpp @@ -50,7 +50,9 @@ void DriverAssist::LogMessage() break; ULONG len = m_workItemLen; - ULONG status = SbieApi_GetWork(-1, m_workItemBuf, &len); + ULONG message_number = m_last_message_number; + ULONG code = -1; + ULONG status = SbieApi_GetMessage(&message_number, -1, &code, (wchar_t*)m_workItemBuf, len); if (status == STATUS_BUFFER_TOO_SMALL) { HeapFree(GetProcessHeap(), 0, m_workItemBuf); @@ -60,11 +62,16 @@ void DriverAssist::LogMessage() } if (status != 0) - break; + break; // error or no more entries - WORK_ITEM *work = (WORK_ITEM *)m_workItemBuf; - if (work->type == API_LOG_MESSAGE) - LogMessage_Single(work->data); + //if (message_number != m_last_message_number + 1) + // we missed something + m_last_message_number = message_number; + + if (code == 0) + break; // empty dummy + + LogMessage_Single(code, (wchar_t*)m_workItemBuf); } if (m_workItemBuf) @@ -79,7 +86,7 @@ void DriverAssist::LogMessage() //--------------------------------------------------------------------------- -void DriverAssist::LogMessage_Single(void *data) +void DriverAssist::LogMessage_Single(ULONG code, wchar_t* data) { // // check if logging is enabled @@ -108,16 +115,15 @@ void DriverAssist::LogMessage_Single(void *data) // get log message // - ULONG *msgidptr = (ULONG *)data; - if (*msgidptr == MSG_2199) + if (code == MSG_2199) return; - WCHAR *str1 = (WCHAR *)(msgidptr + 1); + WCHAR *str1 = data; ULONG str1_len = wcslen(str1); WCHAR *str2 = str1 + str1_len + 1; ULONG str2_len = wcslen(str2); - WCHAR *text = SbieDll_FormatMessage2(*msgidptr, str1, str2); + WCHAR *text = SbieDll_FormatMessage2(code, str1, str2); if (! text) return; @@ -152,7 +158,7 @@ void DriverAssist::LogMessage_Single(void *data) LogMessage_Write(path, text); - LogMessage_Multi(*msgidptr, path, text); + LogMessage_Multi(code, path, text); LocalFree(text); } diff --git a/Sandboxie/install/ParseVersion.bat b/Sandboxie/install/ParseVersion.bat index 11017a03..25221c7a 100644 --- a/Sandboxie/install/ParseVersion.bat +++ b/Sandboxie/install/ParseVersion.bat @@ -12,7 +12,7 @@ echo. > %OUTPUT% for /F "tokens=3*" %%A in ('findstr /R "^#define.SBIE_INSTALLER_PATH\>" %INPUT%') do ( echo ^^!define SBIE_INSTALLER_PATH %%A) >> %OUTPUT% -for /F "tokens=3*" %%A in ('findstr /R "^#define.MY_VERSION_STRING\>" %INPUT%') do ( echo ^^!define VERSION %%A) >> %OUTPUT% +for /F "tokens=3*" %%A in ('findstr /R "^#define.MY_VERSION_STRING_EX\>" %INPUT%') do ( echo ^^!define VERSION %%A) >> %OUTPUT% for /F "tokens=3*" %%A in ('findstr /R "^#define.MY_PRODUCT_NAME_STRING\>" %INPUT%') do ( set C=%%A %%B& echo ^^!define PRODUCT_FULL_NAME !C! & set C=!C: =!& echo ^^!define PRODUCT_NAME !C!) >> %OUTPUT% diff --git a/Sandboxie/install/SandboxieVS.nsi b/Sandboxie/install/SandboxieVS.nsi index f31e119c..b5534f96 100644 --- a/Sandboxie/install/SandboxieVS.nsi +++ b/Sandboxie/install/SandboxieVS.nsi @@ -27,8 +27,8 @@ SetCompressor /SOLID /FINAL lzma ;---------------------------------------------------------------------------- ; these are the build-time config settings. Need to be cmd line args or something better. ; pick either 32 or 64 bit -!define _BUILDARCH Win32 -;!define _BUILDARCH x64 +;!define _BUILDARCH Win32 +!define _BUILDARCH x64 ; uncomment this line if you want to make the special versions that download VC Redist ;!define INCLUDE_VCREDIST_DNLD @@ -764,21 +764,21 @@ FunctionEnd ; We download various files during install just to keep stats on activity Function DownloadStatPng - - Pop $0 ; Get the parameter (file name to download) - ${If} ${RunningX64} - SetRegView 64 - ${EndIf} - ReadRegStr $1 HKLM "SOFTWARE\Microsoft\Cryptography" "MachineGuid" - StrCpy $2 "https://www.sandboxie.com/img/$0?SessionId=$1" - - ;NSISdl::download_quiet /TIMEOUT=3000 $2 $TEMP\$0 - inetc::get /SILENT /CONNECTTIMEOUT=5000 /RECEIVETIMEOUT=5000 $2 $TEMP\$0 /END - Pop $0 ;Get the return value - ;MessageBox MB_OK|MB_ICONSTOP "DownloadStatPng: $2$\n$0" - ${If} ${RunningX64} - SetRegView 32 - ${EndIf} +; +; Pop $0 ; Get the parameter (file name to download) +; ${If} ${RunningX64} +; SetRegView 64 +; ${EndIf} +; ReadRegStr $1 HKLM "SOFTWARE\Microsoft\Cryptography" "MachineGuid" +; StrCpy $2 "https://www.sandboxie.com/img/$0?SessionId=$1" +; +; ;NSISdl::download_quiet /TIMEOUT=3000 $2 $TEMP\$0 +; inetc::get /SILENT /CONNECTTIMEOUT=5000 /RECEIVETIMEOUT=5000 $2 $TEMP\$0 /END +; Pop $0 ;Get the return value +; ;MessageBox MB_OK|MB_ICONSTOP "DownloadStatPng: $2$\n$0" +; ${If} ${RunningX64} +; SetRegView 32 +; ${EndIf} FunctionEnd diff --git a/Sandboxie/install/kmdutil/kmdutil.vcxproj b/Sandboxie/install/kmdutil/kmdutil.vcxproj index c2bc00df..d94d736c 100644 --- a/Sandboxie/install/kmdutil/kmdutil.vcxproj +++ b/Sandboxie/install/kmdutil/kmdutil.vcxproj @@ -70,9 +70,7 @@ - - $(WDKPATH)\lib\crt\amd64;$(LibraryPath) - + diff --git a/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.cpp b/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.cpp new file mode 100644 index 00000000..36ea785e --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.cpp @@ -0,0 +1,235 @@ +#include "stdafx.h" +#include "CheckableMessageBox.h" + +#include +#include +#include +#include +#include + +/*! + \class Utils::CCheckableMessageBox + + \brief A messagebox suitable for questions with a + "Do not ask me again" checkbox. + + Emulates the QMessageBox API with + static conveniences. The message label can open external URLs. +*/ + + +class CCheckableMessageBoxPrivate +{ +public: + CCheckableMessageBoxPrivate(QDialog *q) + : clickedButton(0) + { + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + + pixmapLabel = new QLabel(q); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); + pixmapLabel->setSizePolicy(sizePolicy); + pixmapLabel->setVisible(false); + + QSpacerItem *pixmapSpacer = + new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + + messageLabel = new QLabel(q); + messageLabel->setMinimumSize(QSize(300, 0)); + messageLabel->setSizePolicy(QSizePolicy::Expanding, messageLabel->sizePolicy().verticalPolicy()); + messageLabel->setWordWrap(true); + messageLabel->setOpenExternalLinks(true); + messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); + + QSpacerItem *checkBoxRightSpacer = + new QSpacerItem(1, 1, QSizePolicy::Expanding, QSizePolicy::Minimum); + QSpacerItem *buttonSpacer = + new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); + + checkBox = new QCheckBox(q); + checkBox->setText(CCheckableMessageBox::tr("Do not ask again")); + + buttonBox = new QDialogButtonBox(q); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(pixmapLabel); + verticalLayout->addItem(pixmapSpacer); + + QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); + horizontalLayout_2->addLayout(verticalLayout); + horizontalLayout_2->addWidget(messageLabel); + + QHBoxLayout *horizontalLayout = new QHBoxLayout(); + horizontalLayout->addWidget(checkBox); + horizontalLayout->addItem(checkBoxRightSpacer); + + QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); + verticalLayout_2->addLayout(horizontalLayout_2); + verticalLayout_2->addLayout(horizontalLayout); + verticalLayout_2->addItem(buttonSpacer); + verticalLayout_2->addWidget(buttonBox); + } + + QLabel *pixmapLabel; + QLabel *messageLabel; + QCheckBox *checkBox; + QDialogButtonBox *buttonBox; + QAbstractButton *clickedButton; +}; + +CCheckableMessageBox::CCheckableMessageBox(QWidget *parent) : + QDialog(parent), + d(new CCheckableMessageBoxPrivate(this)) +{ + setModal(true); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept())); + connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject())); + connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)), + SLOT(slotClicked(QAbstractButton*))); +} + +CCheckableMessageBox::~CCheckableMessageBox() +{ + delete d; +} + +void CCheckableMessageBox::slotClicked(QAbstractButton *b) +{ + d->clickedButton = b; +} + +QAbstractButton *CCheckableMessageBox::clickedButton() const +{ + return d->clickedButton; +} + +QDialogButtonBox::StandardButton CCheckableMessageBox::clickedStandardButton() const +{ + if (d->clickedButton) + return d->buttonBox->standardButton(d->clickedButton); + return QDialogButtonBox::NoButton; +} + +QString CCheckableMessageBox::text() const +{ + return d->messageLabel->text(); +} + +void CCheckableMessageBox::setText(const QString &t) +{ + d->messageLabel->setText(t); +} + +QPixmap CCheckableMessageBox::iconPixmap() const +{ + if (const QPixmap *p = d->pixmapLabel->pixmap()) + return QPixmap(*p); + return QPixmap(); +} + +void CCheckableMessageBox::setIconPixmap(const QPixmap &p) +{ + d->pixmapLabel->setPixmap(p); + d->pixmapLabel->setVisible(!p.isNull()); +} + +bool CCheckableMessageBox::isChecked() const +{ + return d->checkBox->isChecked(); +} + +void CCheckableMessageBox::setChecked(bool s) +{ + d->checkBox->setChecked(s); +} + +QString CCheckableMessageBox::checkBoxText() const +{ + return d->checkBox->text(); +} + +void CCheckableMessageBox::setCheckBoxText(const QString &t) +{ + d->checkBox->setText(t); +} + +bool CCheckableMessageBox::isCheckBoxVisible() const +{ + return d->checkBox->isVisible(); +} + +void CCheckableMessageBox::setCheckBoxVisible(bool v) +{ + d->checkBox->setVisible(v); +} + +QDialogButtonBox::StandardButtons CCheckableMessageBox::standardButtons() const +{ + return d->buttonBox->standardButtons(); +} + +void CCheckableMessageBox::setStandardButtons(QDialogButtonBox::StandardButtons s) +{ + d->buttonBox->setStandardButtons(s); +} + +QPushButton *CCheckableMessageBox::button(QDialogButtonBox::StandardButton b) const +{ + return d->buttonBox->button(b); +} + +QPushButton *CCheckableMessageBox::addButton(const QString &text, QDialogButtonBox::ButtonRole role) +{ + return d->buttonBox->addButton(text, role); +} + +QDialogButtonBox::StandardButton CCheckableMessageBox::defaultButton() const +{ + foreach (QAbstractButton *b, d->buttonBox->buttons()) + if (QPushButton *pb = qobject_cast(b)) + if (pb->isDefault()) + return d->buttonBox->standardButton(pb); + return QDialogButtonBox::NoButton; +} + +void CCheckableMessageBox::setDefaultButton(QDialogButtonBox::StandardButton s) +{ + if (QPushButton *b = d->buttonBox->button(s)) { + b->setDefault(true); + b->setFocus(); + } +} + +QDialogButtonBox::StandardButton +CCheckableMessageBox::question(QWidget *parent, + const QString &title, + const QString &question, + const QString &checkBoxText, + bool *checkBoxSetting, + QDialogButtonBox::StandardButtons buttons, + QDialogButtonBox::StandardButton defaultButton, + QMessageBox::Icon icon) +{ + CCheckableMessageBox mb(parent); + mb.setWindowTitle(title); + mb.setIconPixmap(QMessageBox::standardIcon(icon)); + mb.setText(question); + mb.setCheckBoxText(checkBoxText); + mb.setChecked(*checkBoxSetting); + mb.setStandardButtons(buttons); + mb.setDefaultButton(defaultButton); + mb.exec(); + *checkBoxSetting = mb.isChecked(); + return mb.clickedStandardButton(); +} + +QMessageBox::StandardButton CCheckableMessageBox::dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton db) +{ + return static_cast(int(db)); +} + diff --git a/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.h b/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.h new file mode 100644 index 00000000..56026b8f --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/CheckableMessageBox.h @@ -0,0 +1,75 @@ +#ifndef CHECKABLEMESSAGEBOX_H +#define CHECKABLEMESSAGEBOX_H + +#include +#include + +class CCheckableMessageBoxPrivate; + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CCheckableMessageBox : public QDialog +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) + Q_PROPERTY(bool isChecked READ isChecked WRITE setChecked) + Q_PROPERTY(QString checkBoxText READ checkBoxText WRITE setCheckBoxText) + Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) + Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) + +public: + explicit CCheckableMessageBox(QWidget *parent = NULL); + virtual ~CCheckableMessageBox(); + + static QDialogButtonBox::StandardButton + question(QWidget *parent, + const QString &title, + const QString &question, + const QString &checkBoxText, + bool *checkBoxSetting, + QDialogButtonBox::StandardButtons buttons = QDialogButtonBox::Yes|QDialogButtonBox::No, + QDialogButtonBox::StandardButton defaultButton = QDialogButtonBox::No, + QMessageBox::Icon icon = QMessageBox::Question); + + QString text() const; + void setText(const QString &); + + bool isChecked() const; + void setChecked(bool s); + + QString checkBoxText() const; + void setCheckBoxText(const QString &); + + bool isCheckBoxVisible() const; + void setCheckBoxVisible(bool); + + QDialogButtonBox::StandardButtons standardButtons() const; + void setStandardButtons(QDialogButtonBox::StandardButtons s); + QPushButton *button(QDialogButtonBox::StandardButton b) const; + QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); + + QDialogButtonBox::StandardButton defaultButton() const; + void setDefaultButton(QDialogButtonBox::StandardButton s); + + // See static QMessageBox::standardPixmap() + QPixmap iconPixmap() const; + void setIconPixmap (const QPixmap &p); + + // Query the result + QAbstractButton *clickedButton() const; + QDialogButtonBox::StandardButton clickedStandardButton() const; + + // Conversion convenience + static QMessageBox::StandardButton dialogButtonBoxToMessageBoxButton(QDialogButtonBox::StandardButton); + +private slots: + void slotClicked(QAbstractButton *b); + +private: + CCheckableMessageBoxPrivate *d; +}; + + + +#endif // CHECKABLEMESSAGEBOX_H diff --git a/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.cpp b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.cpp new file mode 100644 index 00000000..d90f3572 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.cpp @@ -0,0 +1,193 @@ +#include "stdafx.h" +#include "ComboInputDialog.h" + +#include +#include +#include +#include +#include + + +class CComboInputDialogPrivate +{ +public: + CComboInputDialogPrivate(QDialog *q) + : clickedButton(0) + { + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + + pixmapLabel = new QLabel(q); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); + pixmapLabel->setSizePolicy(sizePolicy); + pixmapLabel->setVisible(false); + + QSpacerItem *pixmapSpacer = + new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + + messageLabel = new QLabel(q); + messageLabel->setMinimumSize(QSize(300, 0)); + messageLabel->setWordWrap(true); + messageLabel->setOpenExternalLinks(true); + messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); + + QSpacerItem *buttonSpacer = + new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); + + combo = new QComboBox(q); + + + buttonBox = new QDialogButtonBox(q); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(pixmapLabel); + verticalLayout->addItem(pixmapSpacer); + + QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); + horizontalLayout_2->addLayout(verticalLayout); + horizontalLayout_2->addWidget(messageLabel); + + QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); + verticalLayout_2->addLayout(horizontalLayout_2); + verticalLayout_2->addWidget(combo); + verticalLayout_2->addItem(buttonSpacer); + verticalLayout_2->addWidget(buttonBox); + } + + QLabel *pixmapLabel; + QLabel *messageLabel; + QComboBox* combo; + QDialogButtonBox *buttonBox; + QAbstractButton *clickedButton; +}; + +CComboInputDialog::CComboInputDialog(QWidget *parent) : + QDialog(parent), + d(new CComboInputDialogPrivate(this)) +{ + setModal(true); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept())); + connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject())); + connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)), + SLOT(slotClicked(QAbstractButton*))); +} + +CComboInputDialog::~CComboInputDialog() +{ + delete d; +} + +void CComboInputDialog::slotClicked(QAbstractButton *b) +{ + d->clickedButton = b; +} + +QAbstractButton *CComboInputDialog::clickedButton() const +{ + return d->clickedButton; +} + +QDialogButtonBox::StandardButton CComboInputDialog::clickedStandardButton() const +{ + if (d->clickedButton) + return d->buttonBox->standardButton(d->clickedButton); + return QDialogButtonBox::NoButton; +} + +QString CComboInputDialog::text() const +{ + return d->messageLabel->text(); +} + +void CComboInputDialog::setText(const QString &t) +{ + d->messageLabel->setText(t); +} + +void CComboInputDialog::addItem(const QString& t, const QVariant & v) +{ + d->combo->addItem(t, v); +} + +void CComboInputDialog::setEditable(bool b) +{ + d->combo->setEditable(b); +} + +QString CComboInputDialog::value() const +{ + return d->combo->currentText(); +} + +void CComboInputDialog::setValue(const QString &t) +{ + int idx = d->combo->findText(t); + if(idx == -1) + d->combo->setEditText(t); + else + d->combo->setCurrentIndex(idx); +} + +QVariant CComboInputDialog::data() const +{ + return d->combo->currentData(); +} + +void CComboInputDialog::setData(const QVariant & v) +{ + d->combo->setCurrentIndex(d->combo->findData(v)); +} + +QPixmap CComboInputDialog::iconPixmap() const +{ + if (const QPixmap *p = d->pixmapLabel->pixmap()) + return QPixmap(*p); + return QPixmap(); +} + +void CComboInputDialog::setIconPixmap(const QPixmap &p) +{ + d->pixmapLabel->setPixmap(p); + d->pixmapLabel->setVisible(!p.isNull()); +} + +QDialogButtonBox::StandardButtons CComboInputDialog::standardButtons() const +{ + return d->buttonBox->standardButtons(); +} + +void CComboInputDialog::setStandardButtons(QDialogButtonBox::StandardButtons s) +{ + d->buttonBox->setStandardButtons(s); +} + +QPushButton *CComboInputDialog::button(QDialogButtonBox::StandardButton b) const +{ + return d->buttonBox->button(b); +} + +QPushButton *CComboInputDialog::addButton(const QString &text, QDialogButtonBox::ButtonRole role) +{ + return d->buttonBox->addButton(text, role); +} + +QDialogButtonBox::StandardButton CComboInputDialog::defaultButton() const +{ + foreach (QAbstractButton *b, d->buttonBox->buttons()) + if (QPushButton *pb = qobject_cast(b)) + if (pb->isDefault()) + return d->buttonBox->standardButton(pb); + return QDialogButtonBox::NoButton; +} + +void CComboInputDialog::setDefaultButton(QDialogButtonBox::StandardButton s) +{ + if (QPushButton *b = d->buttonBox->button(s)) { + b->setDefault(true); + b->setFocus(); + } +} diff --git a/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.h b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.h new file mode 100644 index 00000000..bee2a394 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/ComboInputDialog.h @@ -0,0 +1,61 @@ +#ifndef HCOMBOINPUTDIALOG_H +#define HCOMBOINPUTDIALOG_H + +#include +#include + +class CComboInputDialogPrivate; + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CComboInputDialog: public QDialog +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) + Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) + Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) + +public: + explicit CComboInputDialog(QWidget *parent = NULL); + virtual ~CComboInputDialog(); + + QString text() const; + void setText(const QString &); + + void addItem(const QString&, const QVariant & = QVariant()); + void setEditable(bool); + + QString value() const; + void setValue(const QString &); + + QVariant data() const; + void setData(const QVariant &); + + + QDialogButtonBox::StandardButtons standardButtons() const; + void setStandardButtons(QDialogButtonBox::StandardButtons s); + QPushButton *button(QDialogButtonBox::StandardButton b) const; + QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); + + QDialogButtonBox::StandardButton defaultButton() const; + void setDefaultButton(QDialogButtonBox::StandardButton s); + + // See static QMessageBox::standardPixmap() + QPixmap iconPixmap() const; + void setIconPixmap (const QPixmap &p); + + // Query the result + QAbstractButton *clickedButton() const; + QDialogButtonBox::StandardButton clickedStandardButton() const; + +private slots: + void slotClicked(QAbstractButton *b); + +private: + CComboInputDialogPrivate *d; +}; + + + +#endif // HCOMBOINPUTDIALOG_H diff --git a/SandboxiePlus/MiscHelpers/Common/Common.cpp b/SandboxiePlus/MiscHelpers/Common/Common.cpp new file mode 100644 index 00000000..dfddd2c5 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/Common.cpp @@ -0,0 +1,321 @@ +#include "stdafx.h" +#include "Common.h" + +#ifdef USE_OPENSSL +#include +#endif + +#ifndef WIN32 // vswprintf +#include +#include + + +int vswprintf_l(wchar_t * _String, size_t _Count, const wchar_t * _Format, va_list _Ap) +{ + wchar_t _Format_l[1025]; + ASSERT(wcslen(_Format) < 1024); + wcscpy(_Format_l, _Format); + + for(int i=0; igenerate64(); +#endif + return Rand64; +} + +QString GetRand64Str(bool forUrl) +{ + quint64 Rand64 = GetRand64(); + QString sRand64 = QByteArray((char*)&Rand64,sizeof(quint64)).toBase64(); + if(forUrl) + sRand64.replace("+","-").replace("/","_"); + return sRand64.replace("=",""); +} + + +int GetRandomInt(int iMin, int iMax) +{ + return QRandomGenerator::system()->bounded(iMin, iMax); +} + +StrPair Split2(const QString& String, QString Separator, bool Back) +{ + int Sep = Back ? String.lastIndexOf(Separator) : String.indexOf(Separator); + if(Sep != -1) + return qMakePair(String.left(Sep).trimmed(), String.mid(Sep+Separator.length()).trimmed()); + return qMakePair(String.trimmed(), QString()); +} + +QStringList SplitStr(const QString& String, QString Separator) +{ + QStringList List = String.split(Separator); + for(int i=0; i < List.count(); i++) + { + List[i] = List[i].trimmed(); + if(List[i].isEmpty()) + List.removeAt(i--); + } + return List; +} + +QString FormatSize(quint64 Size, int Precision) +{ + double Div; + if(Size > (quint64)(Div = 1.0*1024*1024*1024*1024*1024*1024)) + return QString::number(double(Size)/Div, 'f', Precision) + " EB"; + if(Size > (quint64)(Div = 1.0*1024*1024*1024*1024*1024)) + return QString::number(double(Size)/Div, 'f', Precision) + " PB"; + if(Size > (quint64)(Div = 1.0*1024*1024*1024*1024)) + return QString::number(double(Size)/Div, 'f', Precision) + " TB"; + if(Size > (quint64)(Div = 1.0*1024*1024*1024)) + return QString::number(double(Size)/Div, 'f', Precision) + " GB"; + if(Size > (quint64)(Div = 1.0*1024*1024)) + return QString::number(double(Size)/Div, 'f', Precision) + " MB"; + if(Size > (quint64)(Div = 1.0*1024)) + return QString::number(double(Size)/Div, 'f', Precision) + " KB"; + return QString::number(double(Size)) + "B"; +} + +QString FormatRate(quint64 Size, int Precision) +{ + return FormatSize(Size, Precision) + "/s"; +} + +QString FormatUnit(quint64 Size, int Precision) +{ + double Div; + if(Size > (quint64)(Div = 1.0*1000*1000*1000*1024*1000*1000)) + return QString::number(double(Size)/Div, 'f', Precision) + " E"; + if(Size > (quint64)(Div = 1.0*1000*1000*1000*1000*1000)) + return QString::number(double(Size)/Div, 'f', Precision) + " P"; + if(Size > (quint64)(Div = 1.0*1000*1000*1000*1000)) + return QString::number(double(Size)/Div, 'f', Precision) + " T"; + if(Size > (quint64)(Div = 1.0*1000*1000*1000)) + return QString::number(double(Size)/Div, 'f', Precision) + " G"; + if(Size > (quint64)(Div = 1.0*1000*1000)) + return QString::number(double(Size)/Div, 'f', Precision) + " M"; + if(Size > (quint64)(Div = 1.0*1000)) + return QString::number(double(Size)/Div, 'f', Precision) + " K"; + return QString::number(double(Size)); +} + + +QString FormatTime(quint64 Time, bool ms) +{ + int miliseconds = 0; + if (ms) { + miliseconds = Time % 1000; + Time /= 1000; + } + int seconds = Time % 60; + Time /= 60; + int minutes = Time % 60; + Time /= 60; + int hours = Time % 24; + int days = Time / 24; + if(ms && (minutes == 0) && (hours == 0) && (days == 0)) + return QString().sprintf("%02d.%04d", seconds, miliseconds); + if((hours == 0) && (days == 0)) + return QString().sprintf("%02d:%02d", minutes, seconds); + if (days == 0) + return QString().sprintf("%02d:%02d:%02d", hours, minutes, seconds); + return QString().sprintf("%dd%02d:%02d:%02d", days, hours, minutes, seconds); +} + +QString FormatNumber(quint64 Number) +{ + QString String = QString::number(Number); + for (int i = String.length() - 3; i > 0; i -= 3) + String.insert(i, QString::fromWCharArray(L"\u202F")); // L"\u2009" + return String; +} + +QString FormatAddress(quint64 Address, int length) +{ + return "0x" + QString::number(Address, 16).rightJustified(length, '0'); +} + +bool ReadFromDevice(QIODevice* dev, char* data, int len, int timeout) +{ + while (dev->bytesAvailable() < len) { + if (!dev->waitForReadyRead(timeout)) + return false; + } + return dev->read(data, len) == len; +} + + +void GrayScale (QImage& Image) +{ + if (Image.depth () == 32) + { + uchar* r = (Image.bits ()); + uchar* g = (Image.bits () + 1); + uchar* b = (Image.bits () + 2); + + uchar* end = (Image.bits() + Image.byteCount ()); + while (r != end) + { + *r = *g = *b = (((*r + *g) >> 1) + *b) >> 1; // (r + b + g) / 3 + + r += 4; + g += 4; + b += 4; + } + } + else + { + for (int i = 0; i < Image.colorCount (); i++) + { + uint r = qRed (Image.color (i)); + uint g = qGreen (Image.color (i)); + uint b = qBlue (Image.color (i)); + + uint gray = (((r + g) >> 1) + b) >> 1; + + Image.setColor (i, qRgba (gray, gray, gray, qAlpha (Image.color (i)))); + } + } +} + +QIcon MakeActionIcon(const QString& IconFile) +{ + QImage Image(IconFile); + QIcon Icon; + Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Normal); + GrayScale(Image); + Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Disabled); + return Icon; +} + +QAction* MakeAction(QToolBar* pParent, const QString& IconFile, const QString& Text) +{ + QAction* pAction = new QAction(Text, pParent); + pAction->setIcon(MakeActionIcon(IconFile)); + pParent->addAction(pAction); + return pAction; +} + +QMenu* MakeMenu(QMenu* pParent, const QString& Text, const QString& IconFile) +{ + if(!IconFile.isEmpty()) + { + QImage Image(IconFile); + QIcon Icon; + Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Normal); + GrayScale(Image); + Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Disabled); + return pParent->addMenu(Icon, Text); + } + return pParent->addMenu(Text); +} + +QAction* MakeAction(QMenu* pParent, const QString& Text, const QString& IconFile) +{ + QAction* pAction = new QAction(Text, pParent); + if(!IconFile.isEmpty()) + { + QImage Image(IconFile); + QIcon Icon; + Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Normal); + GrayScale(Image); + Icon.addPixmap(QPixmap::fromImage(Image), QIcon::Disabled); + pAction->setIcon(Icon); + } + pParent->addAction(pAction); + return pAction; +} + +QAction* MakeAction(QActionGroup* pGroup, QMenu* pParent, const QString& Text, const QVariant& Data) +{ + QAction* pAction = new QAction(Text, pParent); + pAction->setCheckable(true); + pAction->setData(Data); + pAction->setActionGroup(pGroup); + pParent->addAction(pAction); + return pAction; +} \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/Common.h b/SandboxiePlus/MiscHelpers/Common/Common.h new file mode 100644 index 00000000..3cbaae45 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/Common.h @@ -0,0 +1,92 @@ +#pragma once + +#include "../mischelpers_global.h" + +#ifndef WIN32 +MISCHELPERS_EXPORT int vswprintf_l(wchar_t * _String, size_t _Count, const wchar_t * _Format, va_list _Ap); +#endif + +MISCHELPERS_EXPORT time_t GetTime(); +MISCHELPERS_EXPORT quint64 GetCurTick(); + + +MISCHELPERS_EXPORT quint64 GetRand64(); +MISCHELPERS_EXPORT QString GetRand64Str(bool forUrl = true); + +MISCHELPERS_EXPORT int GetRandomInt(int iMin, int iMax); + +MISCHELPERS_EXPORT typedef QPair StrPair; +MISCHELPERS_EXPORT StrPair Split2(const QString& String, QString Separator = "=", bool Back = false); +MISCHELPERS_EXPORT QStringList SplitStr(const QString& String, QString Separator); + +MISCHELPERS_EXPORT QString UnEscape(QString Text); + +MISCHELPERS_EXPORT QString FormatSize(quint64 Size, int Precision = 2); +__inline QString FormatSizeEx(quint64 Size, bool bEx) { return bEx && (Size == 0) ? QString() : FormatSize(Size); } +MISCHELPERS_EXPORT QString FormatRate(quint64 Size, int Precision = 2); +__inline QString FormatRateEx(quint64 Size, bool bEx) { return bEx && (Size == 0) ? QString() : FormatRate(Size); } +MISCHELPERS_EXPORT QString FormatUnit(quint64 Size, int Precision = 0); +MISCHELPERS_EXPORT QString FormatTime(quint64 Time, bool ms = false); +MISCHELPERS_EXPORT QString FormatNumber(quint64 Number); +__inline QString FormatNumberEx(quint64 Number, bool bEx) { return bEx && (Number == 0) ? QString() : FormatNumber(Number); } +MISCHELPERS_EXPORT QString FormatAddress(quint64 Address, int length = 16); + + +inline bool operator < (const QHostAddress &key1, const QHostAddress &key2) +{ + // Note: toIPv6Address also works for IPv4 addresses + Q_IPV6ADDR ip1 = key1.toIPv6Address(); + Q_IPV6ADDR ip2 = key2.toIPv6Address(); + return memcmp(&ip1, &ip2, sizeof(Q_IPV6ADDR)) < 0; +} + +template +QVariantList toVariantList( const QList &list ) +{ + QVariantList newList; + foreach( const T &item, list ) + newList << item; + + return newList; +} + +template +QList reversed( const QList & in ) { + QList result; + result.reserve( in.size() ); // reserve is new in Qt 4.7 + std::reverse_copy( in.begin(), in.end(), std::back_inserter( result ) ); + return result; +} + +template +class CScoped +{ +public: + CScoped(T* Val = NULL) {m_Val = Val;} + ~CScoped() {delete m_Val;} + + CScoped& operator=(const CScoped& Scoped) {ASSERT(0); return *this;} // copying is explicitly forbidden + CScoped& operator=(T* Val) {ASSERT(!m_Val); m_Val = Val; return *this;} + + inline T* Val() const {return m_Val;} + inline T* &Val() {return m_Val;} + inline T* Detache() {T* Val = m_Val; m_Val = NULL; return Val;} + inline T* operator->() const {return m_Val;} + inline T& operator*() const {return *m_Val;} + inline operator T*() const {return m_Val;} + +private: + T* m_Val; +}; + +MISCHELPERS_EXPORT bool ReadFromDevice(QIODevice* dev, char* data, int len, int timeout = 5000); + + +MISCHELPERS_EXPORT void GrayScale (QImage& Image); + +MISCHELPERS_EXPORT QIcon MakeActionIcon(const QString& IconFile); +MISCHELPERS_EXPORT QAction* MakeAction(QToolBar* pParent, const QString& IconFile, const QString& Text = ""); +MISCHELPERS_EXPORT QMenu* MakeMenu(QMenu* pParent, const QString& Text, const QString& IconFile = ""); +MISCHELPERS_EXPORT QAction* MakeAction(QMenu* pParent, const QString& Text, const QString& IconFile = ""); +MISCHELPERS_EXPORT QAction* MakeAction(QActionGroup* pGroup, QMenu* pParent, const QString& Text, const QVariant& Data); + diff --git a/SandboxiePlus/MiscHelpers/Common/DebugHelpers.cpp b/SandboxiePlus/MiscHelpers/Common/DebugHelpers.cpp new file mode 100644 index 00000000..757cc73d --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/DebugHelpers.cpp @@ -0,0 +1,391 @@ +#include "stdafx.h" +#include "DebugHelpers.h" +#include "Common.h" +#include + +#ifdef WIN32 +#include +#endif + +bool IsDebuggerAttached() +{ + bool isDebuggerPresent = false; // Note: on linux change edit the value in debgger to indicate precense +#ifdef WIN32 + if (IsDebuggerPresent()) + return true; +#endif + return isDebuggerPresent; +} + +void WaitForDebugger() +{ + while (!IsDebuggerAttached()) + QThread::msleep(500); +} + + +#if defined(_DEBUG) || defined(_TRACE) + +#ifdef WIN32 +bool g_assert_active = IsDebuggerPresent(); +#else +bool g_assert_active = true; +#endif + +void Assert(bool test) +{ + if(!test && g_assert_active) + _CrtDbgBreak(); +} + +void CTracer::operator()(const QString &sLine) const +{ + qDebug() << sLine; +} + +void CTracer::operator()(const wchar_t *sLine, ...) const +{ + const size_t bufferSize = 10241; + wchar_t bufferline[bufferSize]; + + va_list argptr; + va_start(argptr, sLine); +#ifndef WIN32 + if (vswprintf_l(bufferline, bufferSize, sLine, argptr) == -1) +#else + if (vswprintf(bufferline, bufferSize, sLine, argptr) == -1) +#endif + bufferline[bufferSize - 1] = L'\0'; + va_end(argptr); + + QString sBufferLine = QString::fromWCharArray(bufferline); + qDebug() << sBufferLine; +} + +void CTracer::operator()(const char *sLine, ...) const +{ + const size_t bufferSize = 10241; + char bufferline[bufferSize]; + + va_list argptr; + va_start(argptr, sLine); + if (vsprintf(bufferline, sLine, argptr) == -1) + bufferline[bufferSize - 1] = L'\0'; + va_end(argptr); + + QString sBufferLine = bufferline; + qDebug() << sBufferLine; +} + + +//////////////////////////////////////////////////////////////////////////////////////////// +// Tracers +//////////////////////////////////////////////////////////////////////////////////////////// + +CMemTracer memTracer; + +void CMemTracer::DumpTrace() +{ + QMutexLocker Locker(&m_Locker); + for(map::iterator I = m_MemoryTrace.begin(); I != m_MemoryTrace.end(); I++) + { + + map::iterator J = m_MemoryTrace2.find(I->first); + if(J != m_MemoryTrace2.end()) + TRACE(L"MEMORY TRACE: Object '%S' has %d (%d) instances.",I->first.c_str(),I->second, J->second); + else + TRACE(L"MEMORY TRACE: Object '%S' has %d instances.",I->first.c_str(),I->second); + } +} + +void CMemTracer::TraceAlloc(string Name) +{ + QMutexLocker Locker(&m_Locker); + m_MemoryTrace[Name] += 1; +} + +void CMemTracer::TraceFree(string Name) +{ + QMutexLocker Locker(&m_Locker); + m_MemoryTrace[Name] -= 1; + + //if(m_MemoryTrace2[Name] > 0) + if(m_MemoryTrace2.find(Name) != m_MemoryTrace2.end()) + m_MemoryTrace2[Name] -= 1; +} + +void CMemTracer::TracePre(string Name) +{ + QMutexLocker Locker(&m_Locker); + m_MemoryTrace2[Name] += 1; +} + +///////////////////////////////////////////////////////////////////////////////////// +CCpuTracer cpuTracer; + +void CCpuTracer::DumpTrace() +{ + QMutexLocker Locker(&m_Locker); + for(map::iterator I = m_CpuUsageTrace.begin(); I != m_CpuUsageTrace.end(); I++) + TRACE(L"CPU TRACE: Prozedure '%S' needed %f seconds.",I->first.c_str(),(double)I->second.Total/1000000.0); +} + +void CCpuTracer::ResetTrace() +{ + QMutexLocker Locker(&m_Locker); + m_CpuUsageTrace.clear(); +} + +void CCpuTracer::TraceStart(string Name) +{ + QMutexLocker Locker(&m_Locker); + m_CpuUsageTrace[Name].Counting = GetCurCycle(); +} + +void CCpuTracer::TraceStop(string Name) +{ + QMutexLocker Locker(&m_Locker); + m_CpuUsageTrace[Name].Total += GetCurCycle() - m_CpuUsageTrace[Name].Counting; +} + + +///////////////////////////////////////////////////////////////////////////////////// +CLockTracer lockTracer; + +void CLockTracer::DumpTrace() +{ + QMutexLocker Locker(&m_Locker); + for(map::iterator I = m_LockTrace.begin(); I != m_LockTrace.end(); I++) + TRACE(L"LOCK TRACE: Lock '%S' has %d Locks for %f seconds.",I->first.c_str(),I->second.LockCount, (double)(GetCurCycle()/1000 - I->second.LockTime) / 1000); +} + +void CLockTracer::TraceLock(string Name, int op) +{ + QMutexLocker Locker(&m_Locker); + SLocks &Locks = m_LockTrace[Name]; + if(Locks.LockCount == 0) + Locks.LockTime = GetCurCycle()/1000; + Locks.LockCount += op; + if(Locks.LockCount == 0) + Locks.LockTime = 0; +} +#endif + + +///////////////////////////////////////////////////////////////////////////////////////////// +// + +#ifdef WIN32 +#include + +typedef BOOL (__stdcall *tMDWD)( + 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 + ); + + +static tMDWD s_pMDWD; +static HMODULE s_hDbgHelpMod; +static MINIDUMP_TYPE s_dumpTyp = MiniDumpNormal; // MiniDumpWithDataSegs or MiniDumpWithFullMemory +static wchar_t s_szMiniDumpName[64]; + +static LONG __stdcall MyCrashHandlerExceptionFilter(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 + bool bSuccess = false; + + wchar_t szMiniDumpFileName[128]; + wsprintf(szMiniDumpFileName, L"%s %s.dmp", s_szMiniDumpName, QDateTime::currentDateTime().toString("dd.MM.yyyy hh-mm-ss,zzz").replace(QRegExp("[:*?<>|\"\\/]"), "_").toStdWString().c_str()); + + wchar_t szMiniDumpPath[MAX_PATH] = { 0 }; + + HANDLE hFile = CreateFile(szMiniDumpFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + if (hFile != INVALID_HANDLE_VALUE) + GetCurrentDirectory(MAX_PATH, szMiniDumpPath); + else + { + GetTempPath(MAX_PATH, szMiniDumpPath); + + wchar_t szMiniDumpFilePath[MAX_PATH] = { 0 }; + wsprintf(szMiniDumpFilePath, L"%s\\%s.dmp", szMiniDumpPath, szMiniDumpFileName); + hFile = CreateFile(szMiniDumpFilePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); + } + + if (hFile != INVALID_HANDLE_VALUE) + { + MINIDUMP_EXCEPTION_INFORMATION stMDEI; + stMDEI.ThreadId = GetCurrentThreadId(); + stMDEI.ExceptionPointers = pEx; + stMDEI.ClientPointers = TRUE; + // try to create an miniDump: + if (s_pMDWD(GetCurrentProcess(), GetCurrentProcessId(), hFile, s_dumpTyp, &stMDEI, NULL, NULL)) + { + bSuccess = true; + } + CloseHandle(hFile); + } + + wchar_t szMiniDumpMessage[256]; + if (!bSuccess) + wsprintf(szMiniDumpMessage, L"%s crashed!\r\nCrashdump creation failed.", s_szMiniDumpName); + else + wsprintf(szMiniDumpMessage, L"%s crashed!\r\nCrashdump saved to \"%s\".\r\nPlease report the crash and attach the file \"%s\".", s_szMiniDumpName, szMiniDumpPath, szMiniDumpFileName); + MessageBox(NULL, szMiniDumpMessage, s_szMiniDumpName, MB_OK | MB_ICONERROR); + + // or return one of the following: + // - EXCEPTION_CONTINUE_SEARCH + // - EXCEPTION_CONTINUE_EXECUTION + // - EXCEPTION_EXECUTE_HANDLER + return EXCEPTION_CONTINUE_SEARCH; // this will trigger the "normal" OS error-dialog +} + +void InitMiniDumpWriter(const wchar_t* Name) +{ + if (s_hDbgHelpMod != NULL) + return; + + ASSERT(wcslen(Name) < ARRSIZE(s_szMiniDumpName)); + wcscpy(s_szMiniDumpName, Name); + + // Initialize the member, so we do not load the dll after the exception has occured + // which might be not possible anymore... + s_hDbgHelpMod = LoadLibrary(L"dbghelp.dll"); + if (s_hDbgHelpMod != NULL) + s_pMDWD = (tMDWD) GetProcAddress(s_hDbgHelpMod, "MiniDumpWriteDump"); + + // Register Unhandled Exception-Filter: + SetUnhandledExceptionFilter(MyCrashHandlerExceptionFilter); + + // Additional call "PreventSetUnhandledExceptionFilter"... + // See also: "SetUnhandledExceptionFilter" and VC8 (and later) + // http://blog.kalmbachnet.de/?postid=75 +} + + +quint64 GetCurCycle() +{ + quint64 freq, now; + QueryPerformanceFrequency((LARGE_INTEGER*)&freq); + QueryPerformanceCounter((LARGE_INTEGER*)&now); + quint64 dwNow = ((now * 1000000) / freq) & 0xffffffff; + return dwNow; // returns time since system start in us +} + + +#elif !defined(__APPLE__) +#include +#include +#include +#include +#include + +static wchar_t s_szMiniDumpName[64]; + +/* This structure mirrors the one found in /usr/include/asm/ucontext.h */ +typedef struct _sig_ucontext { + unsigned long uc_flags; + struct ucontext *uc_link; + stack_t uc_stack; + struct sigcontext uc_mcontext; + sigset_t uc_sigmask; +} sig_ucontext_t; + +void crit_err_hdlr(int sig_num, siginfo_t * info, void * ucontext) +{ + void * array[50]; + void * caller_address; + char ** messages; + int size, i; + sig_ucontext_t * uc; + + uc = (sig_ucontext_t *)ucontext; + + /* Get the address at the time the signal was raised */ +#if defined(__i386__) // gcc specific + caller_address = (void *) uc->uc_mcontext.eip; // EIP: x86 specific +#elif defined(__x86_64__) // gcc specific + caller_address = (void *) uc->uc_mcontext.rip; // RIP: x86_64 specific +#else +#error Unsupported architecture. // TO-DO: Add support for other arch. +#endif + + fprintf(stderr, "signal %d (%s), address is %p from %p\n", + sig_num, strsignal(sig_num), info->si_addr, + (void *)caller_address); + + size = backtrace(array, 50); + + /* overwrite sigaction with caller's address */ + array[1] = caller_address; + + messages = backtrace_symbols(array, size); + + char szMiniDumpFileName[128]; + sprintf(szMiniDumpFileName, "%S_%s.log", s_szMiniDumpName, QDateTime::currentDateTime().toString("dd.MM.yyyy_hh-mm-ss,zzz").replace(QRegExp("[:*?<>|\"\\/]"), "_").toStdString().c_str()); + + FILE* file = fopen(szMiniDumpFileName, "wb"); + + /* skip first stack frame (points here) */ + for (i = 1; i < size && messages != NULL; ++i) + { + fprintf(stderr, "[bt]: (%d) %s\n", i, messages[i]); + fprintf(file, "[bt]: (%d) %s\n", i, messages[i]); + } + + fclose(file); + + free(messages); + + exit(EXIT_FAILURE); +} + +void InstallSigAction(int sig) +{ + struct sigaction sigact; + sigact.sa_sigaction = crit_err_hdlr; + sigact.sa_flags = SA_RESTART | SA_SIGINFO; + + if (sigaction(sig, &sigact, (struct sigaction *)NULL) != 0) + { + fprintf(stderr, "error setting signal handler for %d (%s)\n", sig, strsignal(sig)); + exit(EXIT_FAILURE); + } +} + +void InitMiniDumpWriter(const wchar_t* Name) +{ + ASSERT(wcslen(Name) < ARRSIZE(s_szMiniDumpName)); + wcscpy(s_szMiniDumpName, Name); + + InstallSigAction(SIGABRT); + InstallSigAction(SIGSEGV); +} + +quint64 GetCurCycle() +{ + return GetCurTick()*1000; // ToDo +} + +#else +void InitMiniDumpWriter(const wchar_t* Name) +{ + +} +#endif diff --git a/SandboxiePlus/MiscHelpers/Common/DebugHelpers.h b/SandboxiePlus/MiscHelpers/Common/DebugHelpers.h new file mode 100644 index 00000000..c301199c --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/DebugHelpers.h @@ -0,0 +1,173 @@ +#pragma once + +//#define _TRACE +#include "../mischelpers_global.h" + +MISCHELPERS_EXPORT bool IsDebuggerAttached(); + +MISCHELPERS_EXPORT void WaitForDebugger(); + +#if defined(_DEBUG) || defined(_TRACE) + + +MISCHELPERS_EXPORT void Assert(bool test); + + #define TRACE CTracer() + #define ASSERT(x) Assert(x) + #define VERIFY(f) ASSERT(f) + #define DEBUG_ONLY(f) (f) + +MISCHELPERS_EXPORT extern bool g_assert_active; + +class MISCHELPERS_EXPORT CTracer +{ +public: + void operator()(const QString &sLine) const; + void operator()(const wchar_t *sLine, ...) const; + void operator()(const char *sLine, ...) const; +}; + +//////////////////////////////////////////////////////////////////////////////////////////// +// Tracers +//////////////////////////////////////////////////////////////////////////////////////////// + +class MISCHELPERS_EXPORT CMemTracer +{ +public: + //CMemTracer(); + //~CMemTracer(); + + void DumpTrace(); + + void TraceAlloc(string Name); + void TraceFree(string Name); + void TracePre(string Name); + + +private: + QMutex m_Locker; + map m_MemoryTrace; + map m_MemoryTrace2; +}; + +MISCHELPERS_EXPORT extern CMemTracer memTracer; + + #define TRACE_ALLOC(x) memTracer.TraceAlloc(x); + #define TRACE_FREE(x) memTracer.TraceFree(x); + #define TRACE_PRE_FREE(x) memTracer.TracePre(x); + #define TRACE_MEMORY memTracer.DumpTrace(); + +class MISCHELPERS_EXPORT CCpuTracer +{ +public: + //CCpuTracer(); + //~CCpuTracer(); + + void DumpTrace(); + void ResetTrace(); + + void TraceStart(string Name); + void TraceStop(string Name); + +private: + struct SCycles + { + SCycles() + { + Counting = 0; + Total = 0; + } + quint64 Counting; + quint64 Total; + }; + QMutex m_Locker; + map m_CpuUsageTrace; +}; + +MISCHELPERS_EXPORT extern CCpuTracer cpuTracer; + + #define TRACE_RESET cpuTracer.ResetTrace(); + #define TRACE_START(x) cpuTracer.TraceStart(x); + #define TRACE_STOP(x) cpuTracer.TraceStop(x); + #define TRACE_CPU cpuTracer.DumpTrace(); + + +class MISCHELPERS_EXPORT CLockTracer +{ +public: + //CLockTracer(); + //~CLockTracer(); + + void DumpTrace(); + + void TraceLock(string Name, int op); + +private: + QMutex m_Locker; + struct SLocks + { + SLocks() + { + LockTime = 0; + LockCount = 0; + } + time_t LockTime; + int LockCount; + }; + map m_LockTrace; +}; + +MISCHELPERS_EXPORT extern CLockTracer lockTracer; + + #define TRACE_LOCK(x) lockTracer.TraceLock(x,1); + #define TRACE_UNLOCK(x)lockTracer.TraceLock(x,-1); + #define TRACE_LOCKING lockTracer.DumpTrace(); + +class MISCHELPERS_EXPORT CLockerTrace +{ +public: + CLockerTrace(string Name) + { + m_Name = Name; + lockTracer.TraceLock(m_Name,1); + } + ~CLockerTrace() + { + lockTracer.TraceLock(m_Name,-1); + } + +private: + string m_Name; +}; + + #define TRACE_LOCKER(x)CLockerTrace LockTrace(x) + + + +#else + +//#define X_ASSERT(x) if(!(x)) throw "Assertion failed in: " __FUNCTION__ "; File:" __FILE__ "; Line:" STR(__LINE__) ";"; + + #define TRACE + #define ASSERT(x) + #define VERIFY(f) (f) + #define DEBUG_ONLY(f) + + #define TRACE_ALLOC(x) + #define TRACE_FREE(x) + #define TRACE_MEMORY + + #define TRACE_RESET + #define TRACE_START(x) + #define TRACE_STOP(x) + #define TRACE_CPUUSAGE + #define TRACE_CPU + + #define TRACE_LOCK(x) + #define TRACE_UNLOCK(x) + #define TRACE_LOCKING + #define TRACE_LOCKER(x) +#endif + +MISCHELPERS_EXPORT void InitMiniDumpWriter(const wchar_t* Name); +MISCHELPERS_EXPORT quint64 GetCurCycle(); \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/ExitDialog.h b/SandboxiePlus/MiscHelpers/Common/ExitDialog.h new file mode 100644 index 00000000..85c0973d --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/ExitDialog.h @@ -0,0 +1,66 @@ +#pragma once + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CExitDialog : public QDialog +{ +public: + CExitDialog(const QString& Prompt, QWidget* parent = 0) + : QDialog(parent) + { + m_pMainLayout = new QGridLayout(this); + + QLabel* pLabel = new QLabel(Prompt); + m_pMainLayout->addWidget(pLabel, 0, 0, 1, 1); + + m_pButtonBox = new QDialogButtonBox(); + m_pButtonBox->setOrientation(Qt::Horizontal); + m_pButtonBox->setStandardButtons(QDialogButtonBox::Yes|QDialogButtonBox::No); + m_pMainLayout->addWidget(m_pButtonBox, 2, 0, 1, 1); + + connect(m_pButtonBox,SIGNAL(accepted()),this,SLOT(accept())); + connect(m_pButtonBox,SIGNAL(rejected()),this,SLOT(reject())); + + m_TimerId = startTimer(1000); + m_CountDown = 15; + } + ~CExitDialog() + { + killTimer(m_TimerId); + } + +protected: + void timerEvent(QTimerEvent *e) + { + if (e->timerId() != m_TimerId) + { + QDialog::timerEvent(e); + return; + } + + if(m_CountDown != 0) + { + m_CountDown--; + m_pButtonBox->button(QDialogButtonBox::Yes)->setText(tr("Yes (%1)").arg(m_CountDown)); + if(m_CountDown == 0) + accept(); + } + } + + void reject() + { + hide(); + } + + void closeEvent(QCloseEvent *e) + { + hide(); + e->ignore(); + } + + int m_TimerId; + int m_CountDown; + + QGridLayout* m_pMainLayout; + QDialogButtonBox * m_pButtonBox; +}; diff --git a/SandboxiePlus/MiscHelpers/Common/Finder.cpp b/SandboxiePlus/MiscHelpers/Common/Finder.cpp new file mode 100644 index 00000000..bb9c97e7 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/Finder.cpp @@ -0,0 +1,125 @@ +#include "stdafx.h" +#include "Finder.h" + +QWidget* CFinder::AddFinder(QWidget* pList, QObject* pFilterTarget, bool HighLightOption, CFinder** ppFinder) +{ + QWidget* pWidget = new QWidget(); + QVBoxLayout* pLayout = new QVBoxLayout(); + pLayout->setMargin(0); + pWidget->setLayout(pLayout); + + pLayout->addWidget(pList); + CFinder* pFinder = new CFinder(pFilterTarget, pWidget, HighLightOption); + pLayout->addWidget(pFinder); + + if (ppFinder) + *ppFinder = pFinder; + return pWidget; +} + +CFinder::CFinder(QObject* pFilterTarget, QWidget *parent, bool HighLightOption) +:QWidget(parent) +{ + m_pSearchLayout = new QHBoxLayout(); + m_pSearchLayout->setMargin(3); + m_pSearchLayout->setSpacing(3); + m_pSearchLayout->setAlignment(Qt::AlignLeft); + + m_pSearch = new QLineEdit(); + m_pSearch->setMinimumWidth(150); + m_pSearch->setMaximumWidth(350); + m_pSearchLayout->addWidget(m_pSearch); + QObject::connect(m_pSearch, SIGNAL(textChanged(QString)), this, SLOT(OnUpdate())); + //QObject::connect(m_pSearch, SIGNAL(returnPressed()), this, SLOT(_q_next())); + + m_pCaseSensitive = new QCheckBox(tr("Case Sensitive")); + m_pSearchLayout->addWidget(m_pCaseSensitive); + connect(m_pCaseSensitive, SIGNAL(stateChanged(int)), this, SLOT(OnUpdate())); + + m_pRegExp = new QCheckBox(tr("RegExp")); + m_pSearchLayout->addWidget(m_pRegExp); + connect(m_pRegExp, SIGNAL(stateChanged(int)), this, SLOT(OnUpdate())); + + m_pColumn = new QComboBox(); + m_pSearchLayout->addWidget(m_pColumn); + connect(m_pColumn, SIGNAL(currentIndexChanged(int)), this, SLOT(OnUpdate())); + m_pColumn->setVisible(false); + + if (HighLightOption) + { + m_pHighLight = new QCheckBox(tr("Highlight")); + m_pSearchLayout->addWidget(m_pHighLight); + connect(m_pHighLight, SIGNAL(stateChanged(int)), this, SLOT(OnUpdate())); + } + else + m_pHighLight = NULL; + + QToolButton* pClose = new QToolButton(this); + pClose->setIcon(QIcon(":/close.png")); + pClose->setAutoRaise(true); + pClose->setText(tr("Close")); + m_pSearchLayout->addWidget(pClose); + QObject::connect(pClose, SIGNAL(clicked()), this, SLOT(Close())); + + QWidget* pSpacer = new QWidget(); + pSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_pSearchLayout->addWidget(pSpacer); + + setLayout(m_pSearchLayout); + + setMaximumHeight(30); + + hide(); + + if (parent) + { + QAction* pFind = new QAction(tr("&Find ..."), parent); + pFind->setShortcut(QKeySequence::Find); + pFind->setShortcutContext(Qt::WidgetWithChildrenShortcut); + parent->addAction(pFind); + QObject::connect(pFind, SIGNAL(triggered()), this, SLOT(Open())); + } + + m_pSortProxy = qobject_cast(pFilterTarget); + if (pFilterTarget) + QObject::connect(this, SIGNAL(SetFilter(const QRegExp&, bool, int)), pFilterTarget, SLOT(SetFilter(const QRegExp&, bool, int))); +} + +CFinder::~CFinder() +{ +} + + +void CFinder::Open() +{ + if (m_pSortProxy && m_pColumn->count() == 0) + { + m_pColumn->addItem(tr("All columns"), -1); + for (int i = 0; i < m_pSortProxy->columnCount(); i++) + m_pColumn->addItem(m_pSortProxy->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(), i); + m_pColumn->setVisible(true); + } + + show(); + m_pSearch->setFocus(Qt::OtherFocusReason); + m_pSearch->selectAll(); + OnUpdate(); +} + +QRegExp CFinder::GetRegExp() const +{ + if (!isVisible()) + return QRegExp(); + return QRegExp(m_pSearch->text(), m_pCaseSensitive->isChecked() ? Qt::CaseSensitive : Qt::CaseInsensitive, m_pRegExp->isChecked() ? QRegExp::RegExp : QRegExp::FixedString); +} + +void CFinder::OnUpdate() +{ + emit SetFilter(GetRegExp(), GetHighLight(), GetColumn()); +} + +void CFinder::Close() +{ + emit SetFilter(QRegExp()); + hide(); +} \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/Finder.h b/SandboxiePlus/MiscHelpers/Common/Finder.h new file mode 100644 index 00000000..b4fc799b --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/Finder.h @@ -0,0 +1,38 @@ +#pragma once + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CFinder: public QWidget +{ + Q_OBJECT + +public: + CFinder(QObject* pFilterTarget, QWidget *parent = NULL, bool HighLightOption = true); + ~CFinder(); + + static QWidget* AddFinder(QWidget* pList, QObject* pFilterTarget, bool HighLightOption = true, CFinder** ppFinder = NULL); + + QRegExp GetRegExp() const; + bool GetHighLight() const { return m_pHighLight ? m_pHighLight->isChecked() : false; } + int GetColumn() const { return m_pColumn->currentData().toInt(); } + +signals: + void SetFilter(const QRegExp& Exp, bool bHighLight = false, int Column = -1); + +public slots: + void Open(); + void OnUpdate(); + void Close(); + +private: + + QHBoxLayout* m_pSearchLayout; + + QLineEdit* m_pSearch; + QCheckBox* m_pCaseSensitive; + QCheckBox* m_pRegExp; + QComboBox* m_pColumn; + QCheckBox* m_pHighLight; + + QSortFilterProxyModel* m_pSortProxy; +}; \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/FlexError.h b/SandboxiePlus/MiscHelpers/Common/FlexError.h new file mode 100644 index 00000000..08ed25a6 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/FlexError.h @@ -0,0 +1,87 @@ +#pragma once + +#define ERROR_UNDEFINED (1) +#define ERROR_CONFIRM (2) +#define ERROR_INTERNAL (3) +#define ERROR_PARAMS (4) + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CFlexError +{ +public: + CFlexError() + { + m = NULL; + } + CFlexError(const QString& Error, long Status = ERROR_UNDEFINED) : CFlexError() + { + Attach(MkError(Error, Status)); + } + CFlexError(long Status) : CFlexError(QObject::tr("Error"), Status) + { + } + CFlexError(const CFlexError& other) : CFlexError() + { + if(other.m != NULL) + Attach((SFlexError*)other.m); + } + ~CFlexError() + { + Detach(); + } + + CFlexError& operator=(const CFlexError& Array) + { + Attach(Array.m); + return *this; + } + + __inline bool IsError() const { return m != NULL; } + __inline long GetStatus() const { return m ? m->Status : 0; } + __inline QString GetText() const { return m ? m->Error: ""; } + + operator bool() const {return !IsError();} + +private: + struct SFlexError + { + QString Error; + long Status; + + mutable atomic aRefCnt; + } *m; + + SFlexError* MkError(const QString& Error, long Status) + { + SFlexError* p = new SFlexError(); + p->Error = Error; + p->Status = Status; + return p; + } + + void Attach(SFlexError* p) + { + Detach(); + + if (p != NULL) + { + p->aRefCnt.fetch_add(1); + m = p; + } + } + + void Detach() + { + if (m != NULL) + { + if (m->aRefCnt.fetch_sub(1) == 1) + delete m; + m = NULL; + } + } +}; + +typedef CFlexError STATUS; +#define OK CFlexError() +#define ERR CFlexError \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/FlowLayout.cpp b/SandboxiePlus/MiscHelpers/Common/FlowLayout.cpp new file mode 100644 index 00000000..62115132 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/FlowLayout.cpp @@ -0,0 +1,223 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "stdafx.h" +#include "FlowLayout.h" + +//! [1] +QFlowLayout::QFlowLayout(QWidget *parent, int margin, int hSpacing, int vSpacing) + : QLayout(parent), m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} + +QFlowLayout::QFlowLayout(int margin, int hSpacing, int vSpacing) + : m_hSpace(hSpacing), m_vSpace(vSpacing) +{ + setContentsMargins(margin, margin, margin, margin); +} +//! [1] + +//! [2] +QFlowLayout::~QFlowLayout() +{ + QLayoutItem *item; + while ((item = takeAt(0))) + delete item; +} +//! [2] + +//! [3] +void QFlowLayout::addItem(QLayoutItem *item) +{ + itemList.append(item); +} +//! [3] + +//! [4] +int QFlowLayout::horizontalSpacing() const +{ + if (m_hSpace >= 0) { + return m_hSpace; + } else { + return smartSpacing(QStyle::PM_LayoutHorizontalSpacing); + } +} + +int QFlowLayout::verticalSpacing() const +{ + if (m_vSpace >= 0) { + return m_vSpace; + } else { + return smartSpacing(QStyle::PM_LayoutVerticalSpacing); + } +} +//! [4] + +//! [5] +int QFlowLayout::count() const +{ + return itemList.size(); +} + +QLayoutItem *QFlowLayout::itemAt(int index) const +{ + return itemList.value(index); +} + +QLayoutItem *QFlowLayout::takeAt(int index) +{ + if (index >= 0 && index < itemList.size()) + return itemList.takeAt(index); + else + return 0; +} +//! [5] + +//! [6] +Qt::Orientations QFlowLayout::expandingDirections() const +{ + return 0; +} +//! [6] + +//! [7] +bool QFlowLayout::hasHeightForWidth() const +{ + return true; +} + +int QFlowLayout::heightForWidth(int width) const +{ + int height = doLayout(QRect(0, 0, width, 0), true); + return height; +} +//! [7] + +//! [8] +void QFlowLayout::setGeometry(const QRect &rect) +{ + QLayout::setGeometry(rect); + doLayout(rect, false); +} + +QSize QFlowLayout::sizeHint() const +{ + return minimumSize(); +} + +QSize QFlowLayout::minimumSize() const +{ + QSize size; + QLayoutItem *item; + foreach (item, itemList) + size = size.expandedTo(item->minimumSize()); + + size += QSize(2*margin(), 2*margin()); + return size; +} +//! [8] + +//! [9] +int QFlowLayout::doLayout(const QRect &rect, bool testOnly) const +{ + int left, top, right, bottom; + getContentsMargins(&left, &top, &right, &bottom); + QRect effectiveRect = rect.adjusted(+left, +top, -right, -bottom); + int x = effectiveRect.x(); + int y = effectiveRect.y(); + int lineHeight = 0; +//! [9] + +//! [10] + QLayoutItem *item; + foreach (item, itemList) { + QWidget *wid = item->widget(); + int spaceX = horizontalSpacing(); + if (spaceX == -1) + spaceX = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Horizontal); + int spaceY = verticalSpacing(); + if (spaceY == -1) + spaceY = wid->style()->layoutSpacing( + QSizePolicy::PushButton, QSizePolicy::PushButton, Qt::Vertical); +//! [10] +//! [11] + int nextX = x + item->sizeHint().width() + spaceX; + if (nextX - spaceX > effectiveRect.right() && lineHeight > 0) { + x = effectiveRect.x(); + y = y + lineHeight + spaceY; + nextX = x + item->sizeHint().width() + spaceX; + lineHeight = 0; + } + + if (!testOnly) + item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); + + x = nextX; + lineHeight = qMax(lineHeight, item->sizeHint().height()); + } + return y + lineHeight - rect.y() + bottom; +} +//! [11] +//! [12] +int QFlowLayout::smartSpacing(QStyle::PixelMetric pm) const +{ + QObject *parent = this->parent(); + if (!parent) { + return -1; + } else if (parent->isWidgetType()) { + QWidget *pw = static_cast(parent); + return pw->style()->pixelMetric(pm, 0, pw); + } else { + return static_cast(parent)->spacing(); + } +} +//! [12] diff --git a/SandboxiePlus/MiscHelpers/Common/FlowLayout.h b/SandboxiePlus/MiscHelpers/Common/FlowLayout.h new file mode 100644 index 00000000..2bdc3433 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/FlowLayout.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the examples of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QFlowLayout_H +#define QFlowLayout_H + +#include +#include +#include +//! [0] +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT QFlowLayout : public QLayout +{ +public: + explicit QFlowLayout(QWidget *parent, int margin = -1, int hSpacing = -1, int vSpacing = -1); + explicit QFlowLayout(int margin = -1, int hSpacing = -1, int vSpacing = -1); + ~QFlowLayout(); + + void addItem(QLayoutItem *item) override; + int horizontalSpacing() const; + int verticalSpacing() const; + Qt::Orientations expandingDirections() const override; + bool hasHeightForWidth() const override; + int heightForWidth(int) const override; + int count() const override; + QLayoutItem *itemAt(int index) const override; + QSize minimumSize() const override; + void setGeometry(const QRect &rect) override; + QSize sizeHint() const override; + QLayoutItem *takeAt(int index) override; + +private: + int doLayout(const QRect &rect, bool testOnly) const; + int smartSpacing(QStyle::PixelMetric pm) const; + + QList itemList; + int m_hSpace; + int m_vSpace; +}; +//! [0] + +#endif // QFlowLayout_H diff --git a/SandboxiePlus/MiscHelpers/Common/HistoryGraph.h b/SandboxiePlus/MiscHelpers/Common/HistoryGraph.h new file mode 100644 index 00000000..a1b70012 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/HistoryGraph.h @@ -0,0 +1,161 @@ +#pragma once + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CHistoryGraph: public QObject +{ +public: + CHistoryGraph(bool bSimpleMode = false, QColor BkG = Qt::white, QObject* parent = NULL) : QObject(parent) { + m_SimpleMode = bSimpleMode; + m_BkG = BkG; + } + ~CHistoryGraph() {} + + void AddValue(int Id, QColor Color) + { + m_Values.insert(Id, { 0.0, Color }); + } + + void SetValue(int Id, float Value) + { + m_Values[Id].Value = Value; + } + + void Update(int CellHeight, int CellWidth) + { + Update(m_Graph, m_BkG, m_Values, CellHeight, CellWidth, m_SimpleMode); + } + + QImage GetImage() const { return m_Graph; } + + struct SValue + { + float Value; + QColor Color; + }; + + static void Update(QImage& m_Graph, const QColor& m_BkG, const QMap& m_Values, int CellHeight, int CellWidth, bool m_SimpleMode = true) + { + // init / resize + if(m_Graph.height() != CellWidth /*|| m_Graph.width() != curHeight*/) + { + QImage Graph = QImage(CellHeight, CellWidth, QImage::Format_RGB32); + QPainter qp(&Graph); + qp.fillRect(-1, -1, CellHeight+1, CellWidth+1, m_BkG); + if (!m_Graph.isNull()) + qp.drawImage(0, Graph.height() - m_Graph.height(), m_Graph); + m_Graph = Graph; + } + + // shift + uchar *dest = m_Graph.bits(); + int pos = m_Graph.sizeInBytes() - m_Graph.bytesPerLine(); + memmove(dest, dest + m_Graph.bytesPerLine(), pos); + QPainter qp(&m_Graph); + + // draw new data points + int top = 0; + +#if 0 + foreach(const SValue& Value, m_Values) + { + int x = (float)(m_Graph.width()) * Value.Value; + if (x > top) + top = x; + + qp.setPen(Value.Color); + qp.drawLine(0, m_Graph.height() - 1, x, m_Graph.height()); + } + + qp.setPen(Qt::white); + qp.drawLine(top, m_Graph.height() - 1, m_Graph.width()-1, m_Graph.height()); +#else + dest += pos; + memset(dest, 0, m_Graph.bytesPerLine()); // fill line black + ASSERT(m_Graph.depth() == 32); + int max = m_Graph.width(); + //ASSERT(max * 4 == m_Graph.bytesPerLine()); + + foreach(const SValue& Value, m_Values) + { + int x = (float)(max) * Value.Value; + if (x > max) + x = max; + if (x > top) + top = x; + x *= 4; + + int r, g, b; + Value.Color.getRgb(&r, &g, &b); + + + for (int i = 0; i < x; i += 4) + { + if (m_SimpleMode || *(quint32*)(dest + i) == 0) + { + dest[i ] = b; + dest[i + 1] = g; + dest[i + 2] = r; + } + else + { +#define DIV 4/5 + int cb = (int)dest[i ]*DIV + b*DIV; + int cg = (int)dest[i + 1]*DIV + g*DIV; + int cr = (int)dest[i + 2]*DIV + r*DIV; + + dest[i ] = qMin(cb, 255); + dest[i + 1] = qMin(cg, 255); + dest[i + 2] = qMin(cr, 255); + } + } + } + + // fill whats left of the line + /*top *= 4; + if (top < m_Graph.bytesPerLine()) // fill rest white + memset(dest + top, 0xFF, m_Graph.bytesPerLine() - top);*/ + { + int r, g, b; + m_BkG.getRgb(&r, &g, &b); + + top *= 4; + max *= 4; + for (int i = top; i < max; i += 4) + { + dest[i] = b; + dest[i + 1] = g; + dest[i + 2] = r; + } + } +#endif + } + +protected: + QImage m_Graph; + QColor m_BkG; + QMap m_Values; + bool m_SimpleMode; +}; + +class CHistoryWidget: public QWidget +{ +public: + CHistoryWidget(CHistoryGraph* pHistoryGraph, QWidget* parent = NULL) : QWidget(parent) { m_pHistoryGraph = pHistoryGraph; } + ~CHistoryWidget() {} + +protected: + void paintEvent(QPaintEvent* e) + { + if (m_pHistoryGraph) + { + QImage HistoryGraph = m_pHistoryGraph->GetImage(); + QPainter qp(this); + qp.translate(width() - HistoryGraph.height() - 1, height()); + qp.rotate(270); + qp.drawImage(0, 0, HistoryGraph); + } + } + + QPointer m_pHistoryGraph; +}; \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/IconExtreactor.cpp b/SandboxiePlus/MiscHelpers/Common/IconExtreactor.cpp new file mode 100644 index 00000000..fa12d489 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/IconExtreactor.cpp @@ -0,0 +1,307 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtWinExtras module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:BSD$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** BSD License Usage +** Alternatively, you may use this file under the terms of the BSD license +** as follows: +** +** "Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions are +** met: +** * Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** * Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in +** the documentation and/or other materials provided with the +** distribution. +** * Neither the name of The Qt Company Ltd nor the names of its +** contributors may be used to endorse or promote products derived +** from this software without specific prior written permission. +** +** +** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "stdafx.h" +#include "IconExtreactor.h" + +#include + +//#include + +#include +#include +#include +#include +#include + + +/* This example demonstrates the Windows-specific image conversion + * functions. */ + +/*struct PixmapEntry { + QString name; + QPixmap pixmap; +}; + +typedef QVector PixmapEntryList;*/ + +/*std::wostream &operator<<(std::wostream &str, const QString &s) +{ +#ifdef Q_OS_WIN + str << reinterpret_cast(s.utf16()); +#else + str << s.toStdWString(); +#endif + return str; +}*/ + +QString formatSize(const QSize &size) +{ + return QString::number(size.width()) + u'x' + QString::number(size.height()); +} + +// Extract icons contained in executable or DLL using the Win32 API ExtractIconEx() +PixmapEntryList extractIcons(const QString &sourceFile, bool large) +{ + const QString nativeName = QDir::toNativeSeparators(sourceFile); + const auto *sourceFileC = reinterpret_cast(nativeName.utf16()); + const UINT iconCount = ExtractIconEx(sourceFileC, -1, nullptr, nullptr, 0); + if (!iconCount) { + //std::wcerr << sourceFile << " does not appear to contain icons.\n"; + return PixmapEntryList(); + } + + QScopedArrayPointer icons(new HICON[iconCount]); + const UINT extractedIconCount = large ? + ExtractIconEx(sourceFileC, 0, icons.data(), nullptr, iconCount) : + ExtractIconEx(sourceFileC, 0, nullptr, icons.data(), iconCount); + if (!extractedIconCount) { + qErrnoWarning("Failed to extract icons from %s", qPrintable(sourceFile)); + return PixmapEntryList(); + } + + PixmapEntryList result; + result.reserve(int(extractedIconCount)); + + //std::wcout << sourceFile << " contains " << extractedIconCount << " icon(s).\n"; + + for (UINT i = 0; i < extractedIconCount; ++i) { + PixmapEntry entry; + entry.pixmap = QtWin::fromHICON(icons[i]); + if (entry.pixmap.isNull()) { + //std::wcerr << "Error converting icons.\n"; + return PixmapEntryList(); + } + entry.name = QString::fromLatin1("%1_%2x%3").arg(i, 3, 10, QLatin1Char('0')) + .arg(entry.pixmap.width()).arg(entry.pixmap.height()); + result.append(entry); + } + return result; +} + +// Helper for extracting large/jumbo icons available from Windows Vista onwards +// via SHGetImageList(). +QPixmap pixmapFromShellImageList(int iImageList, const SHFILEINFO &info) +{ + QPixmap result; + // For MinGW: + const IID iID_IImageList = { 0x46eb5926, 0x582e, 0x4017, {0x9f, 0xdf, 0xe8, 0x99, 0x8d, 0xaa, 0x9, 0x50} }; + + IImageList *imageList = nullptr; + if (FAILED(SHGetImageList(iImageList, iID_IImageList, reinterpret_cast(&imageList)))) + return result; + + HICON hIcon = nullptr; + if (SUCCEEDED(imageList->GetIcon(info.iIcon, ILD_TRANSPARENT, &hIcon))) { + result = QtWin::fromHICON(hIcon); + DestroyIcon(hIcon); + } + return result; +} + +// Extract icons that would be displayed by the Explorer (shell) +PixmapEntryList extractShellIcons(const QString &sourceFile, bool addOverlays) +{ + enum { // Shell image list ids + sHIL_EXTRALARGE = 0x2, // 48x48 or user-defined + sHIL_JUMBO = 0x4 // 256x256 (Vista or later) + }; + + struct FlagEntry { + const char *name; + unsigned flags; + }; + + const FlagEntry modeEntries[] = + { + {"", 0}, + {"open", SHGFI_OPENICON}, + {"sel", SHGFI_SELECTED}, + }; + const FlagEntry standardSizeEntries[] = + { + {"s", SHGFI_SMALLICON}, + {"l", SHGFI_LARGEICON}, + {"sh", SHGFI_SHELLICONSIZE}, + }; + + const QString nativeName = QDir::toNativeSeparators(sourceFile); + const auto *sourceFileC = reinterpret_cast(nativeName.utf16()); + + SHFILEINFO info; + unsigned int baseFlags = SHGFI_ICON | SHGFI_SYSICONINDEX | SHGFI_ICONLOCATION; + if (addOverlays) + baseFlags |= SHGFI_ADDOVERLAYS | SHGFI_OVERLAYINDEX; + if (!QFileInfo(sourceFile).isDir()) + baseFlags |= SHGFI_USEFILEATTRIBUTES; + + PixmapEntryList result; + for (auto modeEntry : modeEntries) { + const unsigned modeFlags = baseFlags | modeEntry.flags; + QString modePrefix = QLatin1String("_shell_"); + if (modeEntry.name[0]) + modePrefix += QLatin1String(modeEntry.name) + u'_'; + for (auto standardSizeEntry : standardSizeEntries) { + const unsigned flags = modeFlags | standardSizeEntry.flags; + const QString prefix = modePrefix + QLatin1String(standardSizeEntry.name) + + u'_'; + ZeroMemory(&info, sizeof(SHFILEINFO)); + const HRESULT hr = SHGetFileInfo(sourceFileC, 0, &info, sizeof(SHFILEINFO), flags); + if (FAILED(hr)) { + _com_error error(hr); + //std::wcerr << "SHGetFileInfo() failed for \"" << nativeName << "\", " + // << std::hex << std::showbase << flags << std::dec << std::noshowbase + // << " (" << prefix << "): " << error.ErrorMessage() << '\n'; + continue; + } + + if (info.hIcon) { + PixmapEntry entry; + entry.pixmap = QtWin::fromHICON(info.hIcon); + DestroyIcon(info.hIcon); + if (entry.pixmap.isNull()) { + //std::wcerr << "Error converting icons.\n"; + return PixmapEntryList(); + } + entry.name = prefix + formatSize(entry.pixmap.size()); + + const int iconIndex = info.iIcon & 0xFFFFFF; + const int overlayIconIndex = info.iIcon >> 24; + + //std::wcout << "Obtained icon #" << iconIndex; + //if (addOverlays) + // std::wcout << " (overlay #" << overlayIconIndex << ')'; + //if (info.szDisplayName[0]) + // std::wcout << " from " << QString::fromWCharArray(info.szDisplayName); + //std::wcout << " (" << entry.pixmap.width() << 'x' + // << entry.pixmap.height() << ") for " << std::hex << std::showbase << flags + // << std::dec << std::noshowbase << '\n'; + + result.append(entry); + } + } // for standardSizeEntryCount + // Windows Vista onwards: extract large/jumbo icons + if (info.hIcon) { + const QPixmap extraLarge = pixmapFromShellImageList(sHIL_EXTRALARGE, info); + if (!extraLarge.isNull()) { + PixmapEntry entry; + entry.pixmap = extraLarge; + entry.name = modePrefix + QLatin1String("xl_") + formatSize(extraLarge.size()); + result.append(entry); + } + const QPixmap jumbo = pixmapFromShellImageList(sHIL_JUMBO, info); + if (!jumbo.isNull()) { + PixmapEntry entry; + entry.pixmap = jumbo; + entry.name = modePrefix + QLatin1String("jumbo_") + formatSize(extraLarge.size()); + result.append(entry); + } + } + } // for modes + return result; +} + +/* +const char description[] = +"\nExtracts Windows icons from executables, DLL or icon files and writes them\n" +"out as numbered .png-files.\n" +"When passing the --shell option, the icons displayed by Explorer are extracted.\n"; + + +#include +#include + +int main(int argc, char *argv[]) +{ + QGuiApplication app(argc, argv); + QCoreApplication::setApplicationName(QStringLiteral("Icon Extractor")); + QCoreApplication::setOrganizationName(QStringLiteral("QtProject")); + QCoreApplication::setApplicationVersion(QLatin1String(QT_VERSION_STR)); + QCommandLineParser parser; + parser.setSingleDashWordOptionMode(QCommandLineParser::ParseAsCompactedShortOptions); + parser.setApplicationDescription(QLatin1String(description)); + parser.addHelpOption(); + parser.addVersionOption(); + const QCommandLineOption largeIconOption(QStringLiteral("large"), QStringLiteral("Extract large icons")); + parser.addOption(largeIconOption); + const QCommandLineOption shellIconOption(QStringLiteral("shell"), QStringLiteral("Extract shell icons using SHGetFileInfo()")); + parser.addOption(shellIconOption); + const QCommandLineOption shellOverlayOption(QStringLiteral("overlay"), QStringLiteral("Extract shell overlay icons")); + parser.addOption(shellOverlayOption); + parser.addPositionalArgument(QStringLiteral("file"), QStringLiteral("The file to open.")); + parser.addPositionalArgument(QStringLiteral("image_file_folder"), QStringLiteral("The folder to store the images.")); + parser.process(app); + const QStringList &positionalArguments = parser.positionalArguments(); + if (positionalArguments.isEmpty()) + parser.showHelp(0); + + QString imageFileRoot = positionalArguments.size() > 1 ? positionalArguments.at(1) : QDir::currentPath(); + const QFileInfo imageFileRootInfo(imageFileRoot); + if (!imageFileRootInfo.isDir()) { + std::wcerr << imageFileRoot << " is not a directory.\n"; + return 1; + } + const QString &sourceFile = positionalArguments.constFirst(); + imageFileRoot = imageFileRootInfo.absoluteFilePath() + u'/' + QFileInfo(sourceFile).baseName(); + + const PixmapEntryList pixmaps = parser.isSet(shellIconOption) + ? extractShellIcons(sourceFile, parser.isSet(shellOverlayOption)) + : extractIcons(sourceFile, parser.isSet(largeIconOption)); + + for (const auto &entry : pixmaps) { + const QString fileName = imageFileRoot + entry.name + QLatin1String(".png"); + if (!entry.pixmap.save(fileName)) { + std::wcerr << "Error writing image file " << fileName << ".\n"; + return 1; + } + std::wcout << "Wrote " << QDir::toNativeSeparators(fileName) << ".\n"; + } + return 0; +}*/ \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/IconExtreactor.h b/SandboxiePlus/MiscHelpers/Common/IconExtreactor.h new file mode 100644 index 00000000..0edda02f --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/IconExtreactor.h @@ -0,0 +1,13 @@ +#pragma once + +#include "../mischelpers_global.h" + +struct PixmapEntry { + QString name; + QPixmap pixmap; +}; + +typedef QVector PixmapEntryList; + +MISCHELPERS_EXPORT PixmapEntryList extractIcons(const QString &sourceFile, bool large); +MISCHELPERS_EXPORT PixmapEntryList extractShellIcons(const QString &sourceFile, bool addOverlays); \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/ItemChooser.cpp b/SandboxiePlus/MiscHelpers/Common/ItemChooser.cpp new file mode 100644 index 00000000..4f0072e9 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/ItemChooser.cpp @@ -0,0 +1,191 @@ +#include "stdafx.h" +#include "ItemChooser.h" + +CItemChooser::CItemChooser(const QString& Prompt, QWidget *parent) + : QDialog(parent) +{ + this->setWindowTitle(tr("Item Chooser")); + + m_pMainLayout = new QVBoxLayout(); + this->setLayout(m_pMainLayout); + + m_pLabel = new QLabel(Prompt.isNull() ? tr("Select items that will be used.") : Prompt, this); + m_pLabel->setWordWrap(true); + m_pMainLayout->addWidget(m_pLabel); + + m_pCenterLayout = new QHBoxLayout(); + m_pMainLayout->addLayout(m_pCenterLayout); + + m_pListAll = new QListWidget(this); + m_pCenterLayout->addWidget(m_pListAll); + m_pListAll->setSelectionMode(QAbstractItemView::ExtendedSelection); + connect(m_pListAll, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(OnAdd())); + + m_pMidleLayout = new QVBoxLayout(); + m_pCenterLayout->addLayout(m_pMidleLayout); + + m_pMidleLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + m_pBtnAdd = new QPushButton(tr("Add >"), this); + connect(m_pBtnAdd, SIGNAL(pressed()), this, SLOT(OnAdd())); + m_pMidleLayout->addWidget(m_pBtnAdd); + + m_pBtnRemove = new QPushButton(tr("< Remove"), this); + connect(m_pBtnRemove, SIGNAL(pressed()), this, SLOT(OnRemove())); + m_pMidleLayout->addWidget(m_pBtnRemove); + + m_pMidleLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + m_pListChoosen = new QListWidget(this); + m_pCenterLayout->addWidget(m_pListChoosen); + m_pListChoosen->setSelectionMode(QAbstractItemView::ExtendedSelection); + connect(m_pListChoosen, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(OnRemove())); + + m_pRightLayout = new QVBoxLayout(); + m_pCenterLayout->addLayout(m_pRightLayout); + + m_pBtnUp = new QPushButton(tr("Move Up"), this); + connect(m_pBtnUp, SIGNAL(pressed()), this, SLOT(OnUp())); + m_pRightLayout->addWidget(m_pBtnUp); + + m_pBtnDown = new QPushButton(tr("Move Down"), this); + connect(m_pBtnDown, SIGNAL(pressed()), this, SLOT(OnDown())); + m_pRightLayout->addWidget(m_pBtnDown); + + m_pRightLayout->addItem(new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding)); + + m_InsertIndex = m_pMainLayout->count(); + + m_pButtonBox = new QDialogButtonBox(this); + m_pMainLayout->addWidget(m_pButtonBox); + m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + connect(m_pButtonBox, SIGNAL(accepted()), this, SLOT(accept())); + connect(m_pButtonBox, SIGNAL(rejected()), this, SLOT(reject())); +} + +void CItemChooser::closeEvent(QCloseEvent *e) +{ + this->deleteLater(); +} + +void CItemChooser::SetPrompt(const QString& Text) +{ + m_pLabel->setText(Text); +} + +void CItemChooser::AddWidgets(QList Widgets) +{ + foreach(QWidget* pWidget, Widgets) + AddWidget(pWidget); +} + +void CItemChooser::AddWidget(QWidget* pWidget) +{ + m_pMainLayout->insertWidget(m_InsertIndex++, pWidget); +} + +void CItemChooser::AddItem(const QString& Label, const QVariant& Data) +{ + QListWidgetItem* pItem = new QListWidgetItem(Label); + pItem->setData(Qt::UserRole, Data); + m_pListAll->addItem(pItem); +} + +void CItemChooser::OnAdd() +{ + foreach(QListWidgetItem* pItem, m_pListAll->selectedItems()) + AddItem(pItem); +} + +void CItemChooser::OnRemove() +{ + foreach(QListWidgetItem* pItem, m_pListChoosen->selectedItems()) + RemoveItem(pItem); +} + +void CItemChooser::AddItem(QListWidgetItem* pItem) +{ + pItem->setFlags(pItem->flags() & ~Qt::ItemIsSelectable & ~Qt::ItemIsEnabled); + if (m_pListChoosen->findItems(pItem->text(), Qt::MatchExactly).count() > 0) + return; // already added + + QListWidgetItem* pChoosenItem = new QListWidgetItem(pItem->text()); + pChoosenItem->setData(Qt::UserRole, pItem->data(Qt::UserRole)); + m_pListChoosen->addItem(pChoosenItem); +} + +void CItemChooser::RemoveItem(QListWidgetItem* pChoosenItem) +{ + foreach(QListWidgetItem* pItem, m_pListAll->findItems(pChoosenItem->text(), Qt::MatchExactly)) + pItem->setFlags(pItem->flags() | Qt::ItemIsSelectable | Qt::ItemIsEnabled); + delete pChoosenItem; +} + +void CItemChooser::OnUp() +{ + for (int i = 0; i < m_pListChoosen->count(); i++) + { + QListWidgetItem* pItem = m_pListChoosen->item(i); + if (!pItem->isSelected()) + continue; + if (!MoveItem(pItem, -1)) + break; + } +} + +void CItemChooser::OnDown() +{ + for (int i = m_pListChoosen->count()-1; i >= 0 ; i--) + { + QListWidgetItem* pItem = m_pListChoosen->item(i); + if (!pItem->isSelected()) + continue; + if (!MoveItem(pItem, 1)) + break; + } +} + +bool CItemChooser::MoveItem(QListWidgetItem* pItem, int Pos) +{ + int currIndex = m_pListChoosen->row(pItem); + + QListWidgetItem *pPrev = m_pListChoosen->item(m_pListChoosen->row(pItem) + Pos); + if (!pPrev) + return false; + int prevIndex = m_pListChoosen->row(pPrev); + + QListWidgetItem *pTemp = m_pListChoosen->takeItem(prevIndex); + m_pListChoosen->insertItem(prevIndex, pItem); + m_pListChoosen->insertItem(currIndex, pTemp); + return true; +} + +QVariantList CItemChooser::GetChoosenItems() +{ + QVariantList ChoosenItems; + for(int i=0; i < m_pListChoosen->count(); i++) + ChoosenItems.append(m_pListChoosen->item(i)->data(Qt::UserRole)); + return ChoosenItems; +} + +void CItemChooser::ChooseItems(const QVariantList& ChoosenItems) +{ + foreach(const QVariant& Data, ChoosenItems) + ChooseItem(Data); +} + +void CItemChooser::ChooseItem(const QVariant& Data) +{ + QListWidgetItem* pItem = NULL; + for (int i = 0; i < m_pListAll->count(); i++) + { + if (m_pListAll->item(i)->data(Qt::UserRole) == Data) + { + pItem = m_pListAll->item(i); + break; + } + } + + if (pItem) + AddItem(pItem); +} \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/ItemChooser.h b/SandboxiePlus/MiscHelpers/Common/ItemChooser.h new file mode 100644 index 00000000..5379dbaf --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/ItemChooser.h @@ -0,0 +1,61 @@ +#pragma once + +#include +#include + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CItemChooser : public QDialog +{ + Q_OBJECT + +public: + CItemChooser(const QString& Prompt = QString(), QWidget *parent = Q_NULLPTR); + + void SetPrompt(const QString& Text); + void AddWidgets(QList Widgets); + void AddWidget(QWidget* pWidget); + + void AddItem(const QString& Label, const QVariant& Data); + void ChooseItems(const QVariantList& ChoosenItems); + void ChooseItem(const QVariant& Data); + + QVariantList GetChoosenItems(); + +private slots: + void OnAdd(); + void OnRemove(); + void OnUp(); + void OnDown(); + + +protected: + void closeEvent(QCloseEvent *e); + + void AddItem(QListWidgetItem* pItem); + void RemoveItem(QListWidgetItem* pChoosenItem); + bool MoveItem(QListWidgetItem* pItem, int Pos); + +private: + + QVBoxLayout* m_pMainLayout; + QLabel* m_pLabel; + + QHBoxLayout* m_pCenterLayout; + + QListWidget* m_pListAll; + + QVBoxLayout* m_pMidleLayout; + QPushButton* m_pBtnAdd; + QPushButton* m_pBtnRemove; + + QListWidget* m_pListChoosen; + + QVBoxLayout* m_pRightLayout; + QPushButton* m_pBtnUp; + QPushButton* m_pBtnDown; + + QDialogButtonBox* m_pButtonBox; + + int m_InsertIndex; +}; diff --git a/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.cpp b/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.cpp new file mode 100644 index 00000000..e34f9c68 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.cpp @@ -0,0 +1,224 @@ +#include "stdafx.h" +#include "KeyValueInputDialog.h" + +#include +#include +#include +#include +#include + + +class CKeyValueInputDialogPrivate +{ +public: + CKeyValueInputDialogPrivate(QDialog *q) + : clickedButton(0) + { + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + + pixmapLabel = new QLabel(q); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); + pixmapLabel->setSizePolicy(sizePolicy); + pixmapLabel->setVisible(false); + + QSpacerItem *pixmapSpacer = + new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + + messageLabel = new QLabel(q); + messageLabel->setMinimumSize(QSize(300, 0)); + messageLabel->setWordWrap(true); + messageLabel->setOpenExternalLinks(true); + messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); + + QSpacerItem *buttonSpacer = + new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); + + keyLabel = new QLabel(q); + keyLabel->setText("Key:"); + + keyEdit = new QLineEdit(q); + + valueLabel = new QLabel(q); + valueLabel->setText("Value:"); + + valueEdit = new QTextEdit(q); + + + buttonBox = new QDialogButtonBox(q); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(pixmapLabel); + verticalLayout->addItem(pixmapSpacer); + + QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); + horizontalLayout_2->addLayout(verticalLayout); + horizontalLayout_2->addWidget(messageLabel); + + QFormLayout *horizontalLayout = new QFormLayout(); + horizontalLayout->addRow(keyLabel, keyEdit); + horizontalLayout->addRow(valueLabel, valueEdit); + + QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); + verticalLayout_2->addLayout(horizontalLayout_2); + verticalLayout_2->addLayout(horizontalLayout); + verticalLayout_2->addItem(buttonSpacer); + verticalLayout_2->addWidget(buttonBox); + } + + QLabel *pixmapLabel; + QLabel *messageLabel; + QLabel *keyLabel; + QLineEdit* keyEdit; + QLabel *valueLabel; + QTextEdit* valueEdit; + QDialogButtonBox *buttonBox; + QAbstractButton *clickedButton; +}; + +CKeyValueInputDialog::CKeyValueInputDialog(QWidget *parent) : + QDialog(parent), + d(new CKeyValueInputDialogPrivate(this)) +{ + setModal(true); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept())); + connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject())); + connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)), + SLOT(slotClicked(QAbstractButton*))); +} + +CKeyValueInputDialog::~CKeyValueInputDialog() +{ + delete d; +} + +void CKeyValueInputDialog::slotClicked(QAbstractButton *b) +{ + d->clickedButton = b; +} + +QAbstractButton *CKeyValueInputDialog::clickedButton() const +{ + return d->clickedButton; +} + +QDialogButtonBox::StandardButton CKeyValueInputDialog::clickedStandardButton() const +{ + if (d->clickedButton) + return d->buttonBox->standardButton(d->clickedButton); + return QDialogButtonBox::NoButton; +} + +QString CKeyValueInputDialog::text() const +{ + return d->messageLabel->text(); +} + +void CKeyValueInputDialog::setText(const QString &t) +{ + d->messageLabel->setText(t); +} + +QString CKeyValueInputDialog::keyLabel() const +{ + return d->keyLabel->text(); +} + +void CKeyValueInputDialog::setKeyLabel(const QString &t) +{ + return d->keyLabel->setText(t); +} + +QString CKeyValueInputDialog::key() const +{ + return d->keyEdit->text(); +} + +void CKeyValueInputDialog::setKey(const QString &t) +{ + return d->keyEdit->setText(t); +} + +bool CKeyValueInputDialog::keyReadOnly() const +{ + return d->keyEdit->isReadOnly(); +} + +void CKeyValueInputDialog::setKeyReadOnly(bool r) +{ + return d->keyEdit->setReadOnly(r); +} + +QString CKeyValueInputDialog::valueLabel() const +{ + return d->valueLabel->text(); +} + +void CKeyValueInputDialog::setValueLabel(const QString &t) +{ + return d->valueLabel->setText(t); +} + +QString CKeyValueInputDialog::value() const +{ + return d->valueEdit->toPlainText(); +} + +void CKeyValueInputDialog::setValue(const QString &t) +{ + return d->valueEdit->setText(t); +} + +QPixmap CKeyValueInputDialog::iconPixmap() const +{ + if (const QPixmap *p = d->pixmapLabel->pixmap()) + return QPixmap(*p); + return QPixmap(); +} + +void CKeyValueInputDialog::setIconPixmap(const QPixmap &p) +{ + d->pixmapLabel->setPixmap(p); + d->pixmapLabel->setVisible(!p.isNull()); +} + +QDialogButtonBox::StandardButtons CKeyValueInputDialog::standardButtons() const +{ + return d->buttonBox->standardButtons(); +} + +void CKeyValueInputDialog::setStandardButtons(QDialogButtonBox::StandardButtons s) +{ + d->buttonBox->setStandardButtons(s); +} + +QPushButton *CKeyValueInputDialog::button(QDialogButtonBox::StandardButton b) const +{ + return d->buttonBox->button(b); +} + +QPushButton *CKeyValueInputDialog::addButton(const QString &text, QDialogButtonBox::ButtonRole role) +{ + return d->buttonBox->addButton(text, role); +} + +QDialogButtonBox::StandardButton CKeyValueInputDialog::defaultButton() const +{ + foreach (QAbstractButton *b, d->buttonBox->buttons()) + if (QPushButton *pb = qobject_cast(b)) + if (pb->isDefault()) + return d->buttonBox->standardButton(pb); + return QDialogButtonBox::NoButton; +} + +void CKeyValueInputDialog::setDefaultButton(QDialogButtonBox::StandardButton s) +{ + if (QPushButton *b = d->buttonBox->button(s)) { + b->setDefault(true); + b->setFocus(); + } +} diff --git a/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.h b/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.h new file mode 100644 index 00000000..b7d5b90e --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/KeyValueInputDialog.h @@ -0,0 +1,67 @@ +#ifndef KEYVALUEINPUTDIALOG_H +#define KEYVALUEINPUTDIALOG_H + +#include +#include + +class CKeyValueInputDialogPrivate; + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CKeyValueInputDialog : public QDialog +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) + Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) + Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) + +public: + explicit CKeyValueInputDialog(QWidget *parent = NULL); + virtual ~CKeyValueInputDialog(); + + QString text() const; + void setText(const QString &); + + QString keyLabel() const; + void setKeyLabel(const QString &); + + QString key() const; + void setKey(const QString &); + + bool keyReadOnly() const; + void setKeyReadOnly(bool); + + QString valueLabel() const; + void setValueLabel(const QString &); + + QString value() const; + void setValue(const QString &); + + + QDialogButtonBox::StandardButtons standardButtons() const; + void setStandardButtons(QDialogButtonBox::StandardButtons s); + QPushButton *button(QDialogButtonBox::StandardButton b) const; + QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); + + QDialogButtonBox::StandardButton defaultButton() const; + void setDefaultButton(QDialogButtonBox::StandardButton s); + + // See static QMessageBox::standardPixmap() + QPixmap iconPixmap() const; + void setIconPixmap (const QPixmap &p); + + // Query the result + QAbstractButton *clickedButton() const; + QDialogButtonBox::StandardButton clickedStandardButton() const; + +private slots: + void slotClicked(QAbstractButton *b); + +private: + CKeyValueInputDialogPrivate *d; +}; + + + +#endif // HCOMBODIALOG_H diff --git a/SandboxiePlus/MiscHelpers/Common/ListItemModel.cpp b/SandboxiePlus/MiscHelpers/Common/ListItemModel.cpp new file mode 100644 index 00000000..fbec6a81 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/ListItemModel.cpp @@ -0,0 +1,305 @@ +#include "stdafx.h" +#include "ListItemModel.h" + +#define FIRST_COLUMN 0 + +bool CListItemModel::m_DarkMode = false; + +CListItemModel::CListItemModel(QObject *parent) +: QAbstractItemModelEx(parent) +{ + m_bUseIcons = false; +} + +CListItemModel::~CListItemModel() +{ + foreach(SListNode* pNode, m_List) + delete pNode; +} + +int CListItemModel::GetRow(SListNode* pNode) const +{ + int Index = m_RevList.value(pNode, -1); + ASSERT(Index != -1); + ASSERT(m_List[Index] == pNode); + return Index; +} + +void CSimpleListModel::Sync(QList List) +{ + QList New; + QHash Old = m_Map; + + foreach (const QVariantMap& Cur, List) + { + QVariant ID = Cur["ID"]; + + int Row = -1; + SListNode* pNode = static_cast(Old[ID]); + if(!pNode) + { + pNode = static_cast(MkNode(ID)); + pNode->Values.resize(columnCount()); + pNode->IsBold = Cur["IsBold"].toBool(); + pNode->Icon = Cur["Icon"]; + New.append(pNode); + } + else + { + Old[ID] = NULL; + Row = GetRow(pNode); + } + + int Col = 0; + bool State = false; + bool Changed = false; + + QVariantMap Values = Cur["Values"].toMap(); + for(int section = FIRST_COLUMN; section < columnCount(); section++) + { + if (!m_Columns.contains(section)) + continue; // ignore columns which are hidden + + QVariant Value = Values[QString::number(section)]; + + SListNode::SValue& ColValue = pNode->Values[section]; + + if (ColValue.Raw != Value) + { + Changed = true; + ColValue.Raw = Value; + + //ColValue.Formated = + } + + if(State != Changed) + { + if(State && Row != -1) + emit dataChanged(createIndex(Row, Col), createIndex(Row, section-1)); + State = Changed; + Col = section; + } + Changed = false; + } + if(State && Row != -1) + emit dataChanged(createIndex(Row, Col, pNode), createIndex(Row, columnCount()-1, pNode)); + } + + CListItemModel::Sync(New, Old); +} + +void CListItemModel::Sync(QList& New, QHash& Old) +{ + int Removed = 0; + + int Begin = -1; + int End = -1; + for(int i = m_List.count()-1; i >= -1; i--) + { + QVariant ID = i >= 0 ? m_List[i]->ID : QVariant(); + if(!ID.isNull() && (Old.value(ID) != NULL)) // remove it + { + m_Map.remove(ID); + if(End == -1) + End = i; + } + else if(End != -1) // keep it and remove whatis to be removed at once + { + Begin = i + 1; + + beginRemoveRows(QModelIndex(), Begin, End); + for (int j = End; j >= Begin; j--) + { + Removed++; + + SListNode* pNode = m_List.takeAt(j); + m_RevList.remove(pNode); + delete pNode; + } + endRemoveRows(); + + End = -1; + Begin = -1; + } + } + + // if something was removed we need to update the row cache + if (Removed > 0) + { + ASSERT(m_RevList.size() == m_List.size()); + for (int i = 0; i < m_List.count(); i++) + m_RevList[m_List[i]] = i; + } + + Begin = m_List.count(); + for(QList::iterator I = New.begin(); I != New.end(); I++) + { + SListNode* pNode = *I; + m_Map.insert(pNode->ID, pNode); + + int Index = m_List.size(); + m_List.append(pNode); + m_RevList.insert(pNode, Index); + } + End = m_List.count(); + if(Begin < End) + { + beginInsertRows(QModelIndex(), Begin, End-1); + endInsertRows(); + } +} + +QModelIndex CListItemModel::FindIndex(const QVariant& ID) +{ + if(SListNode* pNode = m_Map.value(ID)) + { + int row = m_List.indexOf(pNode); + ASSERT(row != -1); + return createIndex(row, FIRST_COLUMN, pNode); + } + return QModelIndex(); +} + +void CListItemModel::Clear() +{ + //beginResetModel(); + if (rowCount() == 0) + return; + beginRemoveRows(QModelIndex(), 0, rowCount()-1); + foreach(SListNode* pNode, m_List) + delete pNode; + m_List.clear(); + m_RevList.clear(); + m_Map.clear(); + endRemoveRows(); + //endResetModel(); +} + +QVariant CListItemModel::data(const QModelIndex &index, int role) const +{ + return Data(index, role, index.column()); +} + +QVariant CListItemModel::Data(const QModelIndex &index, int role, int section) const +{ + if (!index.isValid()) + return QVariant(); + + //if(role == Qt::SizeHintRole ) + // return QSize(64,16); // for fixing height + + SListNode* pNode = static_cast(index.internalPointer()); + + switch(role) + { + case Qt::DisplayRole: + { + SListNode::SValue& Value = pNode->Values[section]; + return Value.Formated.isValid() ? Value.Formated : Value.Raw; + } + case Qt::EditRole: // sort role + { + return pNode->Values[section].Raw; + } + case Qt::ToolTipRole: + { + QString ToolTip; + emit ToolTipCallback(pNode->ID, ToolTip); + if(!ToolTip.isNull()) + return ToolTip; + break; + } + case Qt::DecorationRole: + { + if (m_bUseIcons && section == FIRST_COLUMN) + return pNode->Icon.isValid() ? pNode->Icon : GetDefaultIcon(); + break; + } + case Qt::FontRole: + { + if (section == FIRST_COLUMN && pNode->IsBold) + { + QFont fnt; + fnt.setBold(true); + return fnt; + } + break; + } + case Qt::BackgroundRole: + { + if(!m_DarkMode) + return pNode->Color.isValid() ? pNode->Color : QVariant(); + break; + } + case Qt::ForegroundRole: + { + if(m_DarkMode) + return pNode->Color.isValid() ? pNode->Color : QVariant(); + else if (pNode->IsGray) + { + QColor Color = Qt::darkGray; + return QBrush(Color); + } + break; + } + + case Qt::UserRole: + { + switch(section) + { + case FIRST_COLUMN: return pNode->ID; + } + break; + } + } + return QVariant(); +} + +Qt::ItemFlags CListItemModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +QModelIndex CListItemModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + if (parent.isValid()) + return QModelIndex(); + if(m_List.count() > row) + return createIndex(row, column, m_List[row]); + return QModelIndex(); +} + +QModelIndex CListItemModel::parent(const QModelIndex &index) const +{ + return QModelIndex(); +} + +int CListItemModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) + return 0; + + if (parent.isValid()) + return 0; + return m_List.count(); +} + +int CSimpleListModel::columnCount(const QModelIndex &parent) const +{ + return m_Headers.count(); +} + +QVariant CSimpleListModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + if (section < m_Headers.size()) + return m_Headers.at(section); + } + return QVariant(); +} \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/ListItemModel.h b/SandboxiePlus/MiscHelpers/Common/ListItemModel.h new file mode 100644 index 00000000..13cfc7d6 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/ListItemModel.h @@ -0,0 +1,98 @@ +#pragma once +#include "TreeViewEx.h" + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CListItemModel : public QAbstractItemModelEx +{ + Q_OBJECT + +public: + CListItemModel(QObject *parent = 0); + virtual ~CListItemModel(); + + void SetUseIcons(bool bUseIcons) { m_bUseIcons = bUseIcons; } + static void SetDarkMode(bool bDark) { m_DarkMode = bDark;} + + QModelIndex FindIndex(const QVariant& ID); + + QVariant Data(const QModelIndex &index, int role, int section) const; + + // derived functions + virtual QVariant data(const QModelIndex &index, int role) const; + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &index) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const = 0; + +public slots: + void Clear(); + +signals: + void ToolTipCallback(const QVariant& ID, QString& ToolTip) const; + +protected: + struct SListNode + { + SListNode(const QVariant& Id) + { + ID = Id; + + IsBold = false; + IsGray = false; + } + virtual ~SListNode() + { + } + + QVariant ID; + + QVariant Icon; + bool IsBold; + bool IsGray; + QColor Color; + struct SValue + { + QVariant Raw; + QVariant Formated; + }; + QVector Values; + }; + + virtual SListNode* MkNode(const QVariant& Id) = 0; // { return new SListNode(Id); } + + void Sync(QList& New, QHash& Old); + + virtual QVariant GetDefaultIcon() const { return QVariant(); } + + int GetRow(SListNode* pNode) const; + + QList m_List; + QHash m_RevList; + QHash m_Map; + bool m_bUseIcons; + + static bool m_DarkMode; +}; + +class MISCHELPERS_EXPORT CSimpleListModel : public CListItemModel +{ + Q_OBJECT + +public: + CSimpleListModel(QObject *parent = 0) : CListItemModel(parent) {} + + void Sync(QList List); + + void setHeaderLabels(const QStringList& Columns) { m_Headers = Columns; } + + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + +protected: + virtual SListNode* MkNode(const QVariant& Id) { return new SListNode(Id); } + + QStringList m_Headers; +}; \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.cpp b/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.cpp new file mode 100644 index 00000000..c37f9cbd --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.cpp @@ -0,0 +1,169 @@ +#include "stdafx.h" +#include "MultiLineInputDialog.h" + +#include +#include +#include +#include +#include + + +class CMultiLineInputDialogPrivate +{ +public: + CMultiLineInputDialogPrivate(QDialog *q) + : clickedButton(0) + { + QSizePolicy sizePolicy(QSizePolicy::Minimum, QSizePolicy::Preferred); + + pixmapLabel = new QLabel(q); + sizePolicy.setHorizontalStretch(0); + sizePolicy.setVerticalStretch(0); + sizePolicy.setHeightForWidth(pixmapLabel->sizePolicy().hasHeightForWidth()); + pixmapLabel->setSizePolicy(sizePolicy); + pixmapLabel->setVisible(false); + + QSpacerItem *pixmapSpacer = + new QSpacerItem(0, 5, QSizePolicy::Minimum, QSizePolicy::MinimumExpanding); + + messageLabel = new QLabel(q); + messageLabel->setMinimumSize(QSize(300, 0)); + messageLabel->setWordWrap(true); + messageLabel->setOpenExternalLinks(true); + messageLabel->setTextInteractionFlags(Qt::LinksAccessibleByKeyboard|Qt::LinksAccessibleByMouse); + + QSpacerItem *buttonSpacer = + new QSpacerItem(0, 1, QSizePolicy::Minimum, QSizePolicy::Minimum); + + edit = new QPlainTextEdit(q); + + + buttonBox = new QDialogButtonBox(q); + buttonBox->setOrientation(Qt::Horizontal); + buttonBox->setStandardButtons(QDialogButtonBox::Cancel|QDialogButtonBox::Ok); + + QVBoxLayout *verticalLayout = new QVBoxLayout(); + verticalLayout->addWidget(pixmapLabel); + verticalLayout->addItem(pixmapSpacer); + + QHBoxLayout *horizontalLayout_2 = new QHBoxLayout(); + horizontalLayout_2->addLayout(verticalLayout); + horizontalLayout_2->addWidget(messageLabel); + + QVBoxLayout *verticalLayout_2 = new QVBoxLayout(q); + verticalLayout_2->addLayout(horizontalLayout_2); + verticalLayout_2->addWidget(edit); + verticalLayout_2->addItem(buttonSpacer); + verticalLayout_2->addWidget(buttonBox); + } + + QLabel *pixmapLabel; + QLabel *messageLabel; + QPlainTextEdit* edit; + QDialogButtonBox *buttonBox; + QAbstractButton *clickedButton; +}; + +CMultiLineInputDialog::CMultiLineInputDialog(QWidget *parent) : + QDialog(parent), + d(new CMultiLineInputDialogPrivate(this)) +{ + setModal(true); + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + connect(d->buttonBox, SIGNAL(accepted()), SLOT(accept())); + connect(d->buttonBox, SIGNAL(rejected()), SLOT(reject())); + connect(d->buttonBox, SIGNAL(clicked(QAbstractButton*)), + SLOT(slotClicked(QAbstractButton*))); +} + +CMultiLineInputDialog::~CMultiLineInputDialog() +{ + delete d; +} + +void CMultiLineInputDialog::slotClicked(QAbstractButton *b) +{ + d->clickedButton = b; +} + +QAbstractButton *CMultiLineInputDialog::clickedButton() const +{ + return d->clickedButton; +} + +QDialogButtonBox::StandardButton CMultiLineInputDialog::clickedStandardButton() const +{ + if (d->clickedButton) + return d->buttonBox->standardButton(d->clickedButton); + return QDialogButtonBox::NoButton; +} + +QString CMultiLineInputDialog::text() const +{ + return d->messageLabel->text(); +} + +void CMultiLineInputDialog::setText(const QString &t) +{ + d->messageLabel->setText(t); +} + +QString CMultiLineInputDialog::value() const +{ + return d->edit->toPlainText(); +} + +void CMultiLineInputDialog::setValue(const QString &t) +{ + d->edit->setPlainText(t); +} + +QPixmap CMultiLineInputDialog::iconPixmap() const +{ + if (const QPixmap *p = d->pixmapLabel->pixmap()) + return QPixmap(*p); + return QPixmap(); +} + +void CMultiLineInputDialog::setIconPixmap(const QPixmap &p) +{ + d->pixmapLabel->setPixmap(p); + d->pixmapLabel->setVisible(!p.isNull()); +} + +QDialogButtonBox::StandardButtons CMultiLineInputDialog::standardButtons() const +{ + return d->buttonBox->standardButtons(); +} + +void CMultiLineInputDialog::setStandardButtons(QDialogButtonBox::StandardButtons s) +{ + d->buttonBox->setStandardButtons(s); +} + +QPushButton *CMultiLineInputDialog::button(QDialogButtonBox::StandardButton b) const +{ + return d->buttonBox->button(b); +} + +QPushButton *CMultiLineInputDialog::addButton(const QString &text, QDialogButtonBox::ButtonRole role) +{ + return d->buttonBox->addButton(text, role); +} + +QDialogButtonBox::StandardButton CMultiLineInputDialog::defaultButton() const +{ + foreach (QAbstractButton *b, d->buttonBox->buttons()) + if (QPushButton *pb = qobject_cast(b)) + if (pb->isDefault()) + return d->buttonBox->standardButton(pb); + return QDialogButtonBox::NoButton; +} + +void CMultiLineInputDialog::setDefaultButton(QDialogButtonBox::StandardButton s) +{ + if (QPushButton *b = d->buttonBox->button(s)) { + b->setDefault(true); + b->setFocus(); + } +} diff --git a/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.h b/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.h new file mode 100644 index 00000000..6a343d16 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/MultiLineInputDialog.h @@ -0,0 +1,55 @@ +#ifndef MULTILINEINPUTDIALOG_H +#define MULTILINEINPUTDIALOG_H + +#include +#include + +class CMultiLineInputDialogPrivate; + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CMultiLineInputDialog : public QDialog +{ + Q_OBJECT + Q_PROPERTY(QString text READ text WRITE setText) + Q_PROPERTY(QPixmap iconPixmap READ iconPixmap WRITE setIconPixmap) + Q_PROPERTY(QDialogButtonBox::StandardButtons buttons READ standardButtons WRITE setStandardButtons) + Q_PROPERTY(QDialogButtonBox::StandardButton defaultButton READ defaultButton WRITE setDefaultButton) + +public: + explicit CMultiLineInputDialog(QWidget *parent = NULL); + virtual ~CMultiLineInputDialog(); + + QString text() const; + void setText(const QString &); + + QString value() const; + void setValue(const QString &); + + + QDialogButtonBox::StandardButtons standardButtons() const; + void setStandardButtons(QDialogButtonBox::StandardButtons s); + QPushButton *button(QDialogButtonBox::StandardButton b) const; + QPushButton *addButton(const QString &text, QDialogButtonBox::ButtonRole role); + + QDialogButtonBox::StandardButton defaultButton() const; + void setDefaultButton(QDialogButtonBox::StandardButton s); + + // See static QMessageBox::standardPixmap() + QPixmap iconPixmap() const; + void setIconPixmap (const QPixmap &p); + + // Query the result + QAbstractButton *clickedButton() const; + QDialogButtonBox::StandardButton clickedStandardButton() const; + +private slots: + void slotClicked(QAbstractButton *b); + +private: + CMultiLineInputDialogPrivate *d; +}; + + + +#endif // HMULTILINEINPUTDIALOG_H diff --git a/SandboxiePlus/MiscHelpers/Common/PanelView.cpp b/SandboxiePlus/MiscHelpers/Common/PanelView.cpp new file mode 100644 index 00000000..6ead11a2 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/PanelView.cpp @@ -0,0 +1,220 @@ +#include "stdafx.h" +#include "PanelView.h" + +bool CPanelView::m_SimpleFormat = false; +int CPanelView::m_MaxCellWidth = 0; +QString CPanelView::m_CellSeparator = "\t"; + +CPanelView::CPanelView(QWidget *parent) + :QWidget(parent) +{ + //m_CopyAll = false; + + m_pMenu = new QMenu(); +} + +CPanelView::~CPanelView() +{ +} + +void CPanelView::AddPanelItemsToMenu(bool bAddSeparator) +{ + if(bAddSeparator) + m_pMenu->addSeparator(); + m_pCopyCell = m_pMenu->addAction(tr("Copy Cell"), this, SLOT(OnCopyCell())); + m_pCopyRow = m_pMenu->addAction(tr("Copy Row"), this, SLOT(OnCopyRow())); + m_pCopyRow->setShortcut(QKeySequence::Copy); + m_pCopyRow->setShortcutContext(Qt::WidgetWithChildrenShortcut); + this->addAction(m_pCopyRow); + m_pCopyPanel = m_pMenu->addAction(tr("Copy Panel"), this, SLOT(OnCopyPanel())); +} + +void CPanelView::OnMenu(const QPoint& Point) +{ + QModelIndex Index = GetView()->currentIndex(); + + m_pCopyCell->setEnabled(Index.isValid()); + m_pCopyRow->setEnabled(Index.isValid()); + + m_pMenu->popup(QCursor::pos()); +} + +void CPanelView::OnCopyCell() +{ + QAbstractItemModel* pModel = GetModel(); + QTreeView * pView = GetView(); + + QModelIndex Index = pView->currentIndex(); + QModelIndex ModelIndex = MapToSource(Index); + int Column = ModelIndex.column(); + + QList Rows; + foreach(const QModelIndex& Index, pView->selectionModel()->selectedIndexes()) + { + if (Index.column() != Column) + continue; + QModelIndex CurIndex = pModel->index(Index.row(), Column, Index.parent()); + QString Cell = pModel->data(CurIndex, Qt::DisplayRole).toString(); + Rows.append(QStringList() << Cell); + } + FormatAndCopy(Rows, false); +} + +void CPanelView::OnCopyRow() +{ + QAbstractItemModel* pModel = GetModel(); + QTreeView * pView = GetView(); + + int Column = 0; // find first not hidden column + for (int i = 0; i < pModel->columnCount(); i++) + { + if (!pView->isColumnHidden(i) || m_ForcedColumns.contains(i)) + { + Column = i; + break; + } + } + + QList Rows; + foreach(const QModelIndex& Index, pView->selectionModel()->selectedIndexes()) + { + if (Index.column() != Column) + continue; + + QModelIndex ModelIndex = MapToSource(Index); + Rows.append(CopyRow(ModelIndex)); + } + FormatAndCopy(Rows); +} + +QStringList CPanelView::CopyHeader() +{ + QAbstractItemModel* pModel = GetModel(); + QTreeView * pView = GetView(); + + QStringList Headder; + for (int i = 0; i < pModel->columnCount(); i++) + { + if (/*!m_CopyAll &&*/ pView->isColumnHidden(i) && !m_ForcedColumns.contains(i)) + continue; + QString Cell = pModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(); + if (!m_SimpleFormat) + Cell = "|" + Cell + "|"; + Headder.append(Cell); + } + return Headder; +} + +QStringList CPanelView::CopyRow(const QModelIndex& ModelIndex, int Level) +{ + QAbstractItemModel* pModel = GetModel(); + QTreeView * pView = GetView(); + + QStringList Cells; + for (int i = 0; i < pModel->columnCount(); i++) + { + if (/*!m_CopyAll &&*/ pView->isColumnHidden(i) && !m_ForcedColumns.contains(i)) + continue; + QModelIndex CellIndex = pModel->index(ModelIndex.row(), i, ModelIndex.parent()); + QString Cell = pModel->data(CellIndex, Qt::DisplayRole).toString(); + if (Level && i == 0) + Cell.prepend(QString(Level, '_') + " "); + Cells.append(Cell); + } + return Cells; +} + +void CPanelView::RecursiveCopyPanel(const QModelIndex& ModelIndex, QList& Rows, int Level) +{ + QAbstractItemModel* pModel = GetModel(); + + Rows.append(CopyRow(ModelIndex, Level)); + + for (int i = 0; i < pModel->rowCount(ModelIndex); i++) + { + QModelIndex SubIndex = pModel->index(i, 0, ModelIndex); + RecursiveCopyPanel(SubIndex, Rows, Level + 1); + } +} + +void CPanelView::OnCopyPanel() +{ + QAbstractItemModel* pModel = GetModel(); + + QList Rows; + for (int i = 0; i < pModel->rowCount(); ++i) + { + QModelIndex ModelIndex = pModel->index(i, 0); + RecursiveCopyPanel(ModelIndex, Rows); + } + FormatAndCopy(Rows); +} + +void CPanelView::FormatAndCopy(QList Rows, bool Headder) +{ + int RowCount = Rows.length(); + + if (Headder) + { + Rows.prepend(QStringList()); + Rows.prepend(CopyHeader()); + Rows.prepend(QStringList()); + } + + QStringList TextRows; + if (m_SimpleFormat || !Headder) + { + foreach(const QStringList& Row, Rows) + TextRows.append(Row.join(m_CellSeparator)); + } + else if(Rows.size() > (Headder ? 3 : 0)) + { + int Columns = Rows[Headder ? 3 : 0].count(); + QVector ColumnWidths(Columns, 0); + + foreach(const QStringList& Row, Rows) + { + for (int i = 0; i < Min(Row.count(), Columns); i++) + { + int CellWidth = Row[i].length(); + if (ColumnWidths[i] < CellWidth) + ColumnWidths[i] = CellWidth; + } + } + + foreach(const QStringList& Row, Rows) + { + if (m_MaxCellWidth != 0 && RowCount > 1) + { + for (int Pos = 0;;Pos += m_MaxCellWidth) + { + bool More = false; + + QString RowText; + for (int i = 0; i < Min(Row.count(), Columns); i++) + { + if (Row[i].length() > Pos) + RowText.append(Row[i].mid(Pos, m_MaxCellWidth).leftJustified(Min(m_MaxCellWidth, ColumnWidths[i]) + 3)); + else + RowText.append(QString(Min(m_MaxCellWidth, ColumnWidths[i]) + 3, ' ')); + + if (Row[i].length() > Pos + m_MaxCellWidth) + More = true; + } + TextRows.append(RowText); + + if (!More) + break; + } + } + else + { + QString RowText; + for (int i = 0; i < Min(Row.count(), Columns); i++) + RowText.append(Row[i].leftJustified(ColumnWidths[i] + 3)); + TextRows.append(RowText); + } + } + } + QApplication::clipboard()->setText(TextRows.join("\n")); +} \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/PanelView.h b/SandboxiePlus/MiscHelpers/Common/PanelView.h new file mode 100644 index 00000000..2d1338f4 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/PanelView.h @@ -0,0 +1,170 @@ +#pragma once + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CPanelView : public QWidget +{ + Q_OBJECT +public: + CPanelView(QWidget *parent = 0); + virtual ~CPanelView(); + + static void SetSimpleFormat(bool bSimple) { m_SimpleFormat = bSimple; } + static void SetMaxCellWidth(int iMaxWidth) { m_MaxCellWidth = iMaxWidth; } + static void SetCellSeparator(const QString& Sep) { m_CellSeparator = Sep; } + +protected slots: + virtual void OnMenu(const QPoint& Point); + + virtual void OnCopyCell(); + virtual void OnCopyRow(); + virtual void OnCopyPanel(); + + + virtual QTreeView* GetView() = 0; + virtual QAbstractItemModel* GetModel() = 0; + virtual QModelIndex MapToSource(const QModelIndex& Model) { return Model; } + static QModelIndexList MapToSource(QModelIndexList Indexes, QSortFilterProxyModel* pProxy) { + for (int i = 0; i < Indexes.count(); i++) + Indexes[i] = pProxy->mapToSource(Indexes[i]); + return Indexes; + } + + virtual void AddPanelItemsToMenu(bool bAddSeparator = true); + + virtual void ForceColumn(int column, bool bSet = true) { if (bSet) m_ForcedColumns.insert(column); else m_ForcedColumns.remove(column); } + + virtual QStringList CopyHeader(); + virtual QStringList CopyRow(const QModelIndex& ModelIndex, int Level = 0); + virtual void RecursiveCopyPanel(const QModelIndex& ModelIndex, QList& Rows, int Level = 0); + +protected: + void FormatAndCopy(QList Rows, bool Headder = true); + + QMenu* m_pMenu; + + QAction* m_pCopyCell; + QAction* m_pCopyRow; + QAction* m_pCopyPanel; + + //bool m_CopyAll; + QSet m_ForcedColumns; + static bool m_SimpleFormat; + static int m_MaxCellWidth; + static QString m_CellSeparator; +}; + +template +class CPanelWidget : public CPanelView +{ +public: + CPanelWidget(QWidget *parent = 0) : CPanelView(parent) + { + m_pMainLayout = new QVBoxLayout(); + m_pMainLayout->setMargin(0); + this->setLayout(m_pMainLayout); + + m_pTreeList = new T(); + m_pTreeList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_pTreeList, SIGNAL(customContextMenuRequested( const QPoint& )), this, SLOT(OnMenu(const QPoint &))); + m_pMainLayout->addWidget(m_pTreeList); + m_pTreeList->setMinimumHeight(50); + AddPanelItemsToMenu(); + + m_pLastAction = m_pMenu->actions()[0]; + } + + virtual QMenu* GetMenu() { return m_pMenu; } + virtual void AddAction(QAction* pAction) { m_pMenu->insertAction(m_pLastAction, pAction); } + + virtual T* GetTree() { return m_pTreeList; } + virtual QTreeView* GetView() { return m_pTreeList; } + virtual QAbstractItemModel* GetModel() { return m_pTreeList->model(); } + +protected: + QVBoxLayout* m_pMainLayout; + + T* m_pTreeList; + + QAction* m_pLastAction; +}; + +#include "TreeWidgetEx.h" +#include "Finder.h" + +class MISCHELPERS_EXPORT CPanelWidgetEx : public CPanelWidget +{ + Q_OBJECT + +public: + CPanelWidgetEx(QWidget *parent = 0) : CPanelWidget(parent) + { + m_pFinder = new CFinder(NULL, this, false); + m_pMainLayout->addWidget(m_pFinder); + QObject::connect(m_pFinder, SIGNAL(SetFilter(const QRegExp&, bool, int)), this, SLOT(SetFilter(const QRegExp&, bool, int))); + } + + static void ApplyFilter(QTreeWidgetEx* pTree, QTreeWidgetItem* pItem, const QRegExp& Exp/*, bool bHighLight = false, int Col = -1*/) + { + for (int j = 0; j < pTree->columnCount(); j++) + pItem->setBackground(j, !Exp.isEmpty() && pItem->text(j).contains(Exp) ? Qt::yellow : Qt::white); + + for (int i = 0; i < pItem->childCount(); i++) + { + ApplyFilter(pTree, pItem->child(i), Exp/*, bHighLight, Col*/); + } + } + + static void ApplyFilter(QTreeWidgetEx* pTree, const QRegExp& Exp/*, bool bHighLight = false, int Col = -1*/) + { + for (int i = 0; i < pTree->topLevelItemCount(); i++) + ApplyFilter(pTree, pTree->topLevelItem(i), Exp/*, bHighLight, Col*/); + } + +private slots: + void SetFilter(const QRegExp& Exp, bool bHighLight = false, int Col = -1) // -1 = any + { + ApplyFilter(m_pTreeList, Exp); + } + +private: + + CFinder* m_pFinder; +}; + +#include "TreeViewEx.h" + +template +class CPanelViewImpl: public CPanelWidget +{ +public: + CPanelViewImpl(T* pModel, QWidget *parent = 0) : CPanelWidget(parent) + { + m_pModel = pModel; + + m_pSortProxy = new CSortFilterProxyModel(false, this); + m_pSortProxy->setSortRole(Qt::EditRole); + m_pSortProxy->setSourceModel(m_pModel); + m_pSortProxy->setDynamicSortFilter(true); + + m_pTreeList->setModel(m_pSortProxy); + + m_pTreeList->setSelectionMode(QAbstractItemView::ExtendedSelection); +#ifdef WIN32 + QStyle* pStyle = QStyleFactory::create("windows"); + m_pTreeList->setStyle(pStyle); +#endif + m_pTreeList->setSortingEnabled(true); + + m_pTreeList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_pTreeList, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(OnMenu(const QPoint &))); + + m_pTreeList->setColumnReset(1); + //connect(m_pTreeList, SIGNAL(ResetColumns()), m_pTreeList, SLOT(OnResetColumns())); + //connect(m_pBoxTree, SIGNAL(ColumnChanged(int, bool)), this, SLOT(OnColumnsChanged())); + } + +protected: + T* m_pModel; + QSortFilterProxyModel* m_pSortProxy; +}; \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/ProgressDialog.h b/SandboxiePlus/MiscHelpers/Common/ProgressDialog.h new file mode 100644 index 00000000..24a251b3 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/ProgressDialog.h @@ -0,0 +1,117 @@ +#pragma once + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CProgressDialog : public QMainWindow +{ + Q_OBJECT + +public: + CProgressDialog(const QString& Prompt, QWidget* parent = 0) + : QMainWindow(parent) + { + setWindowFlags(Qt::Tool); + + m_pMainWidget = new QWidget(); + m_pMainLayout = new QGridLayout(this); + m_pMainWidget->setLayout(m_pMainLayout); + this->setCentralWidget(m_pMainWidget); + + m_pMessageLabel = new QLabel(Prompt); + m_pMessageLabel->setMinimumWidth(300); + m_pMessageLabel->setWordWrap(true); + m_pMainLayout->addWidget(m_pMessageLabel, 0, 0, 1, 1); + + m_pProgressBar = new QProgressBar(); + m_pProgressBar->setTextVisible(false); + m_pProgressBar->setMaximum(0); + m_pProgressBar->setMinimum(0); + m_pMainLayout->addWidget(m_pProgressBar, 1, 0, 1, 1); + + m_pButtonBox = new QDialogButtonBox(); + m_pButtonBox->setOrientation(Qt::Horizontal); + m_pButtonBox->setStandardButtons(QDialogButtonBox::Cancel); + m_pMainLayout->addWidget(m_pButtonBox, 2, 0, 1, 1); + + setFixedSize(sizeHint()); + + connect(m_pButtonBox,SIGNAL(rejected()),this,SIGNAL(Cancel())); + + m_TimerId = startTimer(1000); + m_CountDown = 0; + } + ~CProgressDialog() + { + killTimer(m_TimerId); + } + +signals: + void Cancel(); + +public slots: + void OnProgressMessage(const QString& Message, int Progress = -1) + { + if(!Message.isEmpty()) + m_pMessageLabel->setText(Message); + + if (Progress == -1) + { + if (m_pProgressBar->maximum() != 0) + m_pProgressBar->setMinimum(0); + } + else + { + if (m_pProgressBar->maximum() != 100) + m_pProgressBar->setMinimum(100); + + m_pProgressBar->setValue(Progress); + } + } + + void OnStatusMessage(const QString& Message, int Code = 0) + { + //if(Code == 0) + m_pMessageLabel->setText(Message); + //else // note: parent can't be set as this window may auto close + // QMessageBox::warning(NULL, this->windowTitle(), Message); + } + + void OnFinished() + { + m_pButtonBox->button(QDialogButtonBox::Cancel)->setEnabled(false); + m_CountDown = 3; + } + +protected: + void timerEvent(QTimerEvent *e) + { + if (e->timerId() != m_TimerId) + { + QMainWindow::timerEvent(e); + return; + } + + if(m_CountDown != 0) + { + m_CountDown--; + m_pButtonBox->button(QDialogButtonBox::Cancel)->setText(tr("Close (%1)").arg(m_CountDown)); + if (m_CountDown == 0) + close(); + } + } + + void closeEvent(QCloseEvent *e) + { + emit Cancel(); + this->deleteLater(); + } + + int m_TimerId; + int m_CountDown; + + QWidget* m_pMainWidget; + QGridLayout* m_pMainLayout; + QLabel* m_pMessageLabel; + QProgressBar* m_pProgressBar; + QDialogButtonBox* m_pButtonBox; +}; diff --git a/SandboxiePlus/MiscHelpers/Common/Settings.cpp b/SandboxiePlus/MiscHelpers/Common/Settings.cpp new file mode 100644 index 00000000..a1eb4960 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/Settings.cpp @@ -0,0 +1,113 @@ +#include "stdafx.h" +#include "Settings.h" +//#include "qzlib.h" +#include "Common.h" + +bool TestWriteRight(const QString& Path) +{ + QFile TestFile(Path + "/~test-" + GetRand64Str() + ".tmp"); + if(!TestFile.open(QFile::WriteOnly)) + return false; + TestFile.close(); + return TestFile.remove(); +} + +CSettings::CSettings(const QString& AppName, QMap DefaultValues, QObject* qObject) : QObject(qObject) +{ + m_ConfigDir = QCoreApplication::applicationDirPath(); + if (!(m_bPortable = QFile::exists(m_ConfigDir + "/" + AppName + ".ini"))) + { + QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); + if (dirs.isEmpty()) + m_ConfigDir = QDir::homePath() + "/." + AppName; + else + m_ConfigDir = dirs.first() + "/" + AppName; + QDir().mkpath(m_ConfigDir); + } + + m_pConf = new QSettings(m_ConfigDir + "/" + AppName + ".ini", QSettings::IniFormat, this); + + m_pConf->sync(); + + m_DefaultValues = DefaultValues; + foreach (const QString& Key, m_DefaultValues.uniqueKeys()) + { + const SSetting& Setting = m_DefaultValues[Key]; + if(!m_pConf->contains(Key) || !Setting.Check(m_pConf->value(Key))) + { + if(Setting.IsBlob()) + m_pConf->setValue(Key, Setting.Value.toByteArray().toBase64().replace("+","-").replace("/","_").replace("=","")); + else + m_pConf->setValue(Key, Setting.Value); + } + } +} + +CSettings::~CSettings() +{ + m_pConf->sync(); +} + +bool CSettings::SetValue(const QString &key, const QVariant &value) +{ + QMutexLocker Locker(&m_Mutex); + + if (!m_DefaultValues.isEmpty()) + { + ASSERT(m_pConf->contains(key)); +#ifndef _DEBUG + if (!m_DefaultValues[key].Check(value)) + return false; +#endif + } + + m_pConf->setValue(key, value); + + m_ValueCache.clear(); + return true; +} + +QVariant CSettings::GetValue(const QString &key, const QVariant& preset) +{ + QMutexLocker Locker(&m_Mutex); + + ASSERT(m_DefaultValues.isEmpty() || m_pConf->contains(key)); + + return m_pConf->value(key, preset); +} + +void CSettings::SetBlob(const QString& key, const QByteArray& value) +{ + QString str; + //QByteArray data = Pack(value); + //if(data.size() < value.size()) + // str = ":PackedArray:" + data.toBase64().replace("+","-").replace("/","_").replace("=",""); + //else + str = ":ByteArray:" + value.toBase64().replace("+","-").replace("/","_").replace("=",""); + SetValue(key, str); +} + +QByteArray CSettings::GetBlob(const QString& key) +{ + QByteArray value; + QByteArray str = GetValue(key).toByteArray(); + if(str.left(11) == ":ByteArray:") + value = QByteArray::fromBase64(str.mid(11).replace("-","+").replace("_","/")); + //else if(str.left(13) == ":PackedArray:") + // value = Unpack(QByteArray::fromBase64(str.mid(13).replace("-","+").replace("_","/"))); + return value; +} + +const QStringList CSettings::ListKeys(const QString& Root) +{ + QMutexLocker Locker(&m_Mutex); + QStringList Keys; + foreach(const QString& Key, m_pConf->allKeys()) + { + QStringList Path = Key.split("/"); + ASSERT(Path.count() == 2); + if(Path[0] == Root) + Keys.append(Path[1]); + } + return Keys; +} diff --git a/SandboxiePlus/MiscHelpers/Common/Settings.h b/SandboxiePlus/MiscHelpers/Common/Settings.h new file mode 100644 index 00000000..72e2bc29 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/Settings.h @@ -0,0 +1,150 @@ +#pragma once + +#include +#include +#include +#include + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CSettings: public QObject +{ + Q_OBJECT + +public: + struct SStrRef + { + SStrRef(const char* pRef) + : Ref(pRef) {} + + bool operator < (const SStrRef& Other) const {return strcmp(Ref, Other.Ref) < 0;} + bool operator == (const SStrRef& Other) const {return strcmp(Ref, Other.Ref) == 0;} + + operator QString() const {return QString(Ref);} + + const char* Ref; + }; + + union SCacheVal + { + bool _Bool; + qint32 _Int; + quint32 _UInt; + quint64 _UInt64; + }; + + struct SSetting + { + SSetting(){ + MinValue = 0; + MaxValue = 0; + } + + SSetting(const QVariant& value){ + Value = value; + MinValue = 0; + MaxValue = 0; + } + + SSetting(qint64 value, qint64 minvalue = LLONG_MIN, qint64 maxvalue = LLONG_MAX) { + Value = value; + MinValue = minvalue; + MaxValue = maxvalue; + } + + SSetting(const QString& value, const QStringList& values) { + Value = value; + MinValue = 0; + MaxValue = 0; + Values = values; + } + + bool Check(const QVariant& value) const + { + if(MinValue != MaxValue) + { + if(!value.canConvert(QVariant::LongLong)) + return false; + qint64 Test = value.toLongLong(); + if(Test < MinValue || MinValue > MaxValue) + return false; + } + else if(!Values.isEmpty()) + { + if(!value.canConvert(QVariant::String)) + return false; + if(!Values.contains(value.toString())) + return false; + } + return true; + } + + virtual bool IsBlob() const {return false;} + + QVariant Value; + qint64 MinValue; + qint64 MaxValue; + QStringList Values; + }; + + struct SBlobSetting: SSetting + { + SBlobSetting(const QByteArray& value){ + Value = value; + MinValue = 0; + MaxValue = 0; + } + virtual bool IsBlob() const {return true;} + }; + + CSettings(const QString& AppName, QMap DefaultValues = QMap(), QObject* qObject = NULL); + virtual ~CSettings(); + + bool SetValue(const QString& key, const QVariant& value); + QVariant GetValue(const QString& key, const QVariant& preset = QVariant()); + + void SetBlob(const QString& key, const QByteArray& value); + QByteArray GetBlob(const QString& key); + +#define IMPL_CFG_CACHE_GET(x,y,z) \ + x Get##y(const SStrRef& key, x def = 0) \ + { \ + QMutexLocker Locker(&m_Mutex); \ + QMap::Iterator I = m_ValueCache.find(key); \ + if(I != m_ValueCache.end()) \ + return I.value()._##y; \ + Locker.unlock(); \ + x val = GetValue(key, def).to##z(); \ + Locker.relock(); \ + SCacheVal entry; \ + entry._##y = val; \ + m_ValueCache.insert(key, entry); \ + return val; \ + } + IMPL_CFG_CACHE_GET(bool, Bool, Bool); + IMPL_CFG_CACHE_GET(qint32, Int, Int); + IMPL_CFG_CACHE_GET(quint32, UInt, UInt); + IMPL_CFG_CACHE_GET(quint64, UInt64, ULongLong); +#undef IMPL_CFG_CACHE_GET + + const QString GetString(const QString& key, const QVariant& preset = QVariant()) {return GetValue(key, preset).toString();} + const QStringList GetStringList(const QString& key, const QVariant& preset = QVariant()) {return GetValue(key, preset).toStringList();} + + const QStringList ListSettings() {QMutexLocker Locker(&m_Mutex); return m_pConf->allKeys();} + const QStringList ListGroupes() {QMutexLocker Locker(&m_Mutex); return m_pConf->childGroups();} + const QStringList ListKeys(const QString& Root); + + const QString GetConfigDir() {QMutexLocker Locker(&m_Mutex); return m_ConfigDir;} + const bool IsPortable() {QMutexLocker Locker(&m_Mutex); return m_bPortable;} + +protected: + QMutex m_Mutex; + QMap m_DefaultValues; + + QMapm_ValueCache; + + QString m_ConfigDir; + bool m_bPortable; + + QSettings* m_pConf; +}; diff --git a/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.cpp b/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.cpp new file mode 100644 index 00000000..79a5a43d --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.cpp @@ -0,0 +1,70 @@ +#include "stdafx.h" +#include "SettingsWidgets.h" +#include "CheckableMessageBox.h" + +/////////////////////////////////////////////////// +// CPathEdit + +CPathEdit::CPathEdit(bool bDirs, QWidget *parent) + : CTxtEdit(parent) +{ + m_bDirs = bDirs; + + QHBoxLayout* pLayout = new QHBoxLayout(this); + pLayout->setMargin(0); + m_pEdit = new QLineEdit(this); + connect(m_pEdit, SIGNAL(textChanged(const QString &)), this, SIGNAL(textChanged(const QString &))); + pLayout->addWidget(m_pEdit); + QPushButton* pButton = new QPushButton("..."); + pButton->setMaximumWidth(25); + connect(pButton, SIGNAL(pressed()), this, SLOT(Browse())); + pLayout->addWidget(pButton); +} + +void CPathEdit::Browse() +{ + QString FilePath = m_bDirs + ? QFileDialog::getExistingDirectory(this, tr("Select Directory")) + : QFileDialog::getOpenFileName(0, tr("Browse"), "", QString("Any File (*.*)")); + if(!FilePath.isEmpty()) + SetText(FilePath); +} + +/////////////////////////////////////////////////// +// CProxyEdit + +CProxyEdit::CProxyEdit(QWidget *parent) + : CTxtEdit(parent) +{ + QHBoxLayout* pLayout = new QHBoxLayout(this); + pLayout->setMargin(0); + + m_pType = new QComboBox(); + m_pType->addItem(QString("No")); + m_pType->addItem(QString("http")); + m_pType->addItem(QString("socks5")); + connect(m_pType, SIGNAL(activated(int)), this, SLOT(OnType(int))); + pLayout->addWidget(m_pType); + + m_pEdit = new QLineEdit(this); + connect(m_pEdit, SIGNAL(textChanged(const QString &)), this, SIGNAL(textChanged(const QString &))); + pLayout->addWidget(m_pEdit); +} + +void CProxyEdit::SetText(const QString& Text) +{ + QUrl Url(Text); + m_pType->setCurrentText(Url.scheme()); + m_pEdit->setText(Text); +} + +void CProxyEdit::OnType(int Index) +{ + if(Index == 0) + m_pEdit->setEnabled(false); + else + { + m_pEdit->setEnabled(true); + m_pEdit->setText(m_pType->currentText() + "://"); + } +} diff --git a/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.h b/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.h new file mode 100644 index 00000000..a014050f --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/SettingsWidgets.h @@ -0,0 +1,421 @@ +#pragma once + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CMultiLineEdit: public QPlainTextEdit +{ + Q_OBJECT +public: + CMultiLineEdit(QWidget *parent = 0) + :QPlainTextEdit(parent) {} + + void SetLines(const QStringList& Lines){ + setPlainText(Lines.join("\r\n")); + } + QStringList GetLines(){ + return toPlainText().split(QRegExp("\r?\n")); + } +}; + +/////////////////////////////////////////////////// +// + +class MISCHELPERS_EXPORT QNumEdit: public QLineEdit +{ + Q_OBJECT + + Q_PROPERTY(int Value READ GetValue WRITE SetValue) + +public: + QNumEdit(QWidget *parent = 0) : QLineEdit(parent) {} + + virtual void SetValue(qint64 Value) = 0; + virtual qint64 GetValue() = 0; +}; + + /////////////////////////////////////////////////// +// + +class MISCHELPERS_EXPORT CKbpsEdit: public QNumEdit +{ + Q_OBJECT + +public: + CKbpsEdit(qint64 Factor = 1024, QWidget *parent = 0) + :QNumEdit(parent) , m_Factor(Factor) {} + + void SetValue(qint64 Value){ + if(Value <= 0) + setText(tr("Unlimited")); + else + setText(QString::number(Value/m_Factor)); + } + qint64 GetValue(){ + int Value = text().toInt(); + if(Value > 0) + return Value*m_Factor; + else + return -1; // Unlimited; + } + +protected: + qint64 m_Factor; +}; + +/////////////////////////////////////////////////// +// + +class MISCHELPERS_EXPORT CFactorEdit: public QNumEdit +{ + Q_OBJECT + +public: + CFactorEdit(double Factor = 100.0, int Prec = 2, bool bEmpty = false, QWidget *parent = 0) + :QNumEdit(parent), m_Factor(Factor), m_Empty(bEmpty), m_Prec(Prec) {} + + void SetValue(qint64 Value){ + if(!Value && m_Empty) + setText(""); + else + setText(QString::number(Value / m_Factor, 'f', m_Prec)); + } + qint64 GetValue(){ + return text().toDouble() * m_Factor; + } + +protected: + double m_Factor; + bool m_Empty; + int m_Prec; +}; + +/////////////////////////////////////////////////// +// + +class MISCHELPERS_EXPORT CSpinBoxEx: public QSpinBox +{ + Q_OBJECT +public: + CSpinBoxEx(quint64 Unit, QWidget *parent = 0) + :QSpinBox(parent) { + m_Unit = Unit; + setSingleStep(Unit); + setMaximum(INT_MAX); + } + +protected: + virtual int valueFromText(const QString &text) const{ + return text.toInt() * m_Unit; + } + virtual QString textFromValue(int val) const{ + return QString::number(val / m_Unit); + } + + quint64 m_Unit; +}; + +/////////////////////////////////////////////////// +// + +class MISCHELPERS_EXPORT CTxtEdit: public QWidget +{ + Q_OBJECT + + Q_PROPERTY(QString Data READ GetText WRITE SetText) + +public: + CTxtEdit(QWidget *parent = 0) : QWidget(parent) {} + + virtual void SetText(const QString& Text) = 0; + virtual QString GetText() = 0; +}; + +/////////////////////////////////////////////////// +// CPathEdit + +class MISCHELPERS_EXPORT CPathEdit: public CTxtEdit +{ + Q_OBJECT +public: + CPathEdit(bool bDirs = false, QWidget *parent = 0); + + QLineEdit* GetEdit() {return m_pEdit;} + + void SetText(const QString& Text) {m_pEdit->setText(Text);} + QString GetText() {return m_pEdit->text();} + +signals: + void textChanged(const QString& text); + +private slots: + void Browse(); +protected: + QLineEdit* m_pEdit; + bool m_bDirs; +}; + +/////////////////////////////////////////////////// +// CProxyEdit + +class MISCHELPERS_EXPORT CProxyEdit: public CTxtEdit +{ + Q_OBJECT + +public: + CProxyEdit(QWidget *parent = 0); + + void SetText(const QString& Text); + QString GetText() {return m_pEdit->text();} + +signals: + void textChanged(const QString& text); + +private slots: + void OnType(int Index); + +protected: + QComboBox* m_pType; + QLineEdit* m_pEdit; +}; + +/////////////////////////////////////////////////// +// + +class MISCHELPERS_EXPORT QComboBoxEx: public QComboBox +{ + Q_OBJECT + + Q_PROPERTY(QVariant Data READ GetData) + +public: + QComboBoxEx(QWidget* pParent = 0) : QComboBox(pParent) {} + + QVariant GetData() {return itemData(currentIndex());} +}; + +/////////////////////////////////////////////////// +// + +class MISCHELPERS_EXPORT QCheckBoxEx: public QCheckBox +{ + Q_OBJECT +public: + QCheckBoxEx(const QString& Text, bool bSwap = true) : QCheckBox(Text) + { + m_bSwap = bSwap; + setTristate(); + } + + void SetState(int State) + { + if(m_bSwap) + { + switch(State) + { + case 0: State = Qt::PartiallyChecked; break; + case 1: State = Qt::Checked; break; + case 2: State = Qt::Unchecked; break; + } + } + setCheckState((Qt::CheckState)State); + } + + int GetState() + { + int State = checkState(); + if(m_bSwap) + { + switch(State) + { + case Qt::PartiallyChecked: State = 0; break; + case Qt::Checked: State = 1; break; + case Qt::Unchecked: State = 2; break; + } + } + return State; + } + +protected: + bool m_bSwap; +}; + +/////////////////////////////////////////////////// +// + +class MISCHELPERS_EXPORT QSecretCheckBox: public QCheckBoxEx +{ + Q_OBJECT +public: + QSecretCheckBox(const QString& Text) : QCheckBoxEx(Text, false) + { + setTristate(false); + } + +protected: + void mouseDoubleClickEvent (QMouseEvent * e) + { + if(QApplication::keyboardModifiers() & Qt::ControlModifier) + setTristate(); + } +}; + +/////////////////////////////////////////////////// +// QSpinBoxEx + +class MISCHELPERS_EXPORT QSpinBoxEx: public QSpinBox +{ +public: + QSpinBoxEx(QWidget* parent, const QString& Default, const QString& Suffix = "", bool exponential = false) : QSpinBox(parent) + { + m_default = 1; + + setRange(0, INT_MAX); + setSuffix(Suffix); + setSpecialValueText(Default + Suffix); + m_exponential = exponential; + } + + void stepBy(int steps) + { + if(m_exponential) + { + int Value = value(); + if(!Value) + { + Value = m_default; + if(Value <= 0) + Value = 1; + } + + if(steps == 1) + setValue(Value*2); + else if(steps == -1) + setValue(Value/2); + else + QSpinBox::stepBy(steps); + } + else + QSpinBox::stepBy(steps); + } + + void setDefault(int value) {m_default = value;} + +protected: + int m_default; + bool m_exponential; +}; + + +/////////////////////////////////////////////////// +// CMenuAction & CActionWidget + +class MISCHELPERS_EXPORT QMenu_: public QMenu { public: void initStyleOption_(QStyleOptionMenuItem *option, const QAction *action) const {QMenu::initStyleOption(option, action);} }; + + +class MISCHELPERS_EXPORT CActionWidget: public QWidget +{ +public: + CActionWidget(QWidget* parent = 0) : QWidget(parent) {} + + void paintEvent(QPaintEvent* e) + { + QPainter p(this); + + QMenu_ Menu; + QAction* action = new QAction(this); + + QStyleOptionMenuItem opt; + Menu.initStyleOption_(&opt, action); + opt.menuRect = opt.rect = QRect(0, 0, 22, 22); // with this icons and text are drown properly + style()->drawControl(QStyle::CE_MenuItem, &opt, &p, this); + } +}; + +class MISCHELPERS_EXPORT CMenuAction: public QWidgetAction +{ +public: + CMenuAction (QWidget* pControll, const QString& Title, QString IconFile = "") : QWidgetAction (pControll->parent()) + { + QWidget* pWidget = new CActionWidget(pControll->parentWidget()); + QHBoxLayout* pLayout = new QHBoxLayout(); + pLayout->setMargin(0); + + if(!IconFile.isEmpty()) + { + QImage Image(IconFile); + QLabel* pIcon = new QLabel(); + pIcon->setToolTip(Title); + pIcon->setPixmap(QPixmap::fromImage(Image).scaled(16,16, Qt::IgnoreAspectRatio, Qt::SmoothTransformation)); + pLayout->addWidget(pIcon); + + pLayout->insertSpacing(0, 3); + pLayout->insertSpacing(2, 6); + } + else + { + pLayout->addWidget(new QLabel(Title)); + + pLayout->insertSpacing(0, 32); + } + + pLayout->addWidget(pControll); + + pWidget->setLayout(pLayout); + setDefaultWidget(pWidget); + } +}; + +/////////////////////////////////////////////////// +// QCheckComboBox + +class MISCHELPERS_EXPORT QCheckComboBox : public QComboBox +{ + Q_OBJECT +public: + QCheckComboBox() + { + view()->viewport()->installEventFilter(this); + } + + virtual bool eventFilter(QObject *watched, QEvent *e) + { + switch (e->type()) + { + case QEvent::MouseButtonPress: + break; + case QEvent::MouseButtonRelease: + QMouseEvent *m = static_cast(e); + + // is check box rect pressed + if (m->localPos().x() < this->height()) + { + QStandardItemModel *model_ = qobject_cast(model()); + Qt::CheckState state = model_->data(view()->currentIndex(), Qt::CheckStateRole).value(); + + if (state != Qt::Checked) + state = Qt::Checked; + else + state = Qt::Unchecked; + + model_->setData(view()->currentIndex(), state, Qt::CheckStateRole); + + emit checkStateChanged(view()->currentIndex().row(), state); + return true; + } + + /*if (isVisible() && view()->rect().contains(m->pos()) && view()->currentIndex().isValid() + && (view()->currentIndex().flags() & Qt::ItemIsEnabled) + && (view()->currentIndex().flags() & Qt::ItemIsSelectable)) { + this->hidePopup(); + this->setCurrentIndex(view()->currentIndex().row()); + return true; + }*/ + + break; + } + return false; + } + +signals: + void checkStateChanged(int Index, Qt::CheckState state); +}; \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.cpp b/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.cpp new file mode 100644 index 00000000..2121fc6e --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.cpp @@ -0,0 +1,65 @@ +#include "stdafx.h" +#include "SmartGridWidget.h" +#include + +CSmartGridWidget::CSmartGridWidget(QWidget* parent) + : QWidget(parent) +{ + m_pMainLayout = new QGridLayout(); + this->setLayout(m_pMainLayout); + + m_pMainLayout->setMargin(1); + m_pMainLayout->setSpacing(2); + + m_bReArangePending = false; +} + +void CSmartGridWidget::SetBackground(const QColor& BackColor) +{ + QPalette pal = palette(); + pal.setColor(QPalette::Background, BackColor); + this->setAutoFillBackground(true); + this->setPalette(pal); +} + +void CSmartGridWidget::AddWidget(QWidget* pWidget) +{ + m_Widgets.append(pWidget); + if(!pWidget->parent()) + pWidget->setParent(this); + + if (!m_bReArangePending) + { + m_bReArangePending = true; + QTimer::singleShot(0, this, SLOT(ReArange())); + } +} + +void CSmartGridWidget::ReArange() +{ + m_bReArangePending = false; + + int count = 0; + while (count < m_Widgets.size()) + { + if (m_Widgets[count] == NULL) + m_Widgets.removeAt(count); + else + count++; + } + + float columns = ceil(sqrt((float)count)); + float rows = columns > 0 ? ceil(count / columns) : 0; + + for (int row = 0; row < rows; row++) + { + for (int column = 0; column < columns; column++) + { + int index = row * columns + column; + if (index >= count) + break; + + m_pMainLayout->addWidget(m_Widgets.at(index), row, column); + } + } +} \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.h b/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.h new file mode 100644 index 00000000..2c328d1b --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/SmartGridWidget.h @@ -0,0 +1,30 @@ +#pragma once +#include + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CSmartGridWidget : public QWidget +{ + Q_OBJECT + +public: + CSmartGridWidget(QWidget* parent = NULL); + virtual ~CSmartGridWidget() {} + + virtual void SetBackground(const QColor& BackColor); + + virtual void AddWidget(QWidget* pWidget); + + virtual int GetCount() { return m_Widgets.count(); } + virtual QWidget* GetWidget(int Index) { return m_Widgets.at(Index); } + +public slots: + virtual void ReArange(); + +protected: + QGridLayout* m_pMainLayout; + + QList>m_Widgets; + + bool m_bReArangePending; +}; diff --git a/SandboxiePlus/MiscHelpers/Common/SortFilterProxyModel.h b/SandboxiePlus/MiscHelpers/Common/SortFilterProxyModel.h new file mode 100644 index 00000000..bd22d00b --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/SortFilterProxyModel.h @@ -0,0 +1,82 @@ +#pragma once + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CSortFilterProxyModel: public QSortFilterProxyModel +{ + Q_OBJECT + +public: + CSortFilterProxyModel(bool bAlternate, QObject* parrent = 0) : QSortFilterProxyModel(parrent) + { + m_bAlternate = bAlternate; + m_bHighLight = false; + } + + bool filterAcceptsRow(int source_row, const QModelIndex & source_parent) const + { + if (m_bHighLight) + return true; + + // allow the item to pass if any of the child items pass + if(!filterRegExp().isEmpty()) + { + // get source-model index for current row + QModelIndex source_index = sourceModel()->index(source_row, 0, source_parent); + if(source_index.isValid()) + { + // if any of children matches the filter, then current index matches the filter as well + int nb = sourceModel()->rowCount(source_index); + for(int i = 0; i < nb; i++) + { + if(filterAcceptsRow(i, source_index)) + return true; + } + // check current index itself + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); + } + } + + // default behavioure + return QSortFilterProxyModel::filterAcceptsRow(source_row, source_parent); + } + + QVariant data(const QModelIndex &index, int role) const + { + QVariant Data = QSortFilterProxyModel::data(index, role); + if (role == Qt::BackgroundRole) + { + if (m_bHighLight) + { + if (!filterRegExp().isEmpty()) + { + QString Key = QSortFilterProxyModel::data(index, filterRole()).toString(); + if (Key.contains(filterRegExp())) + return QColor(Qt::yellow); + } + return QColor(Qt::white); + } + + if (m_bAlternate && !Data.isValid()) + { + if (0 == index.row() % 2) + return QColor(226, 237, 253); + else + return QColor(Qt::white); + } + } + return Data; + } + +public slots: + void SetFilter(const QRegExp& Exp, bool bHighLight = false, int Col = -1) // -1 = any + { + m_bHighLight = bHighLight; + setFilterKeyColumn(Col); + setFilterRegExp(Exp); + } + +protected: + bool m_bAlternate; + bool m_bHighLight; +}; \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/SplitTreeView.cpp b/SandboxiePlus/MiscHelpers/Common/SplitTreeView.cpp new file mode 100644 index 00000000..8ac8efc3 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/SplitTreeView.cpp @@ -0,0 +1,369 @@ +#include "stdafx.h" +#include "SplitTreeView.h" + +CSplitTreeView::CSplitTreeView(QAbstractItemModel* pModel, QWidget *parent) : QWidget(parent) +{ + m_pModel = pModel; + + m_pMainLayout = new QHBoxLayout(); + m_pMainLayout->setMargin(0); + this->setLayout(m_pMainLayout); + + + m_pSplitter = new QSplitter(); + m_pSplitter->setOrientation(Qt::Horizontal); + m_pMainLayout->addWidget(m_pSplitter); + +#ifdef WIN32 + QStyle* pStyle = QStyleFactory::create("windows"); +#endif + + m_LockSellection = 0; + + // Tree + m_pTree = new QTreeView(); + + m_pOneModel = new COneColumnModel(); + m_pOneModel->setSourceModel(m_pModel); + m_pTree->setModel(m_pOneModel); + //m_pTree->setModel(m_pSortProxy); + + m_pTree->setSelectionMode(QAbstractItemView::ExtendedSelection); +#ifdef WIN32 + m_pTree->setStyle(pStyle); +#endif + //m_pTree->setSortingEnabled(true); + m_pTree->setSortingEnabled(false); + m_pTree->setUniformRowHeights(true); + m_pTree->header()->setSortIndicatorShown(true); + m_pTree->header()->setSectionsClickable(true); + connect(m_pTree->header(), SIGNAL(sectionClicked(int)), this, SLOT(OnTreeCustomSortByColumn(int))); + + m_pTree->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_pTree, SIGNAL(customContextMenuRequested( const QPoint& )), this, SIGNAL(MenuRequested(const QPoint &))); + + m_pSplitter->addWidget(m_pTree); + //m_pSplitter->setCollapsible(0, false); + m_pSplitter->setStretchFactor(0, 0); + // + + + // List + // Note: It would be convinient to use QTreeViewEx here but qt does not scale well when there are too many columns + // hence we will add and remove columns at the model level directly. + // This way we can get out operational CPU usage to be quite comparable with TaskInfo na other advanced task managers + // Plus there are to many columns to cram them into one simple context menu :-) + //m_pList = new QTreeViewEx(); + m_pList = new QTreeView(); + + m_pList->setModel(m_pModel); + + m_pList->setSelectionMode(QAbstractItemView::ExtendedSelection); +#ifdef WIN32 + m_pList->setStyle(pStyle); +#endif + m_pList->setSortingEnabled(true); + m_pList->setUniformRowHeights(true); + connect(m_pList->header(), SIGNAL(sectionClicked(int)), this, SLOT(OnListCustomSortByColumn(int))); + + m_pList->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_pList, SIGNAL(customContextMenuRequested( const QPoint& )), this, SIGNAL(MenuRequested(const QPoint &))); + + m_pSplitter->addWidget(m_pList); + m_pSplitter->setCollapsible(1, false); + m_pSplitter->setStretchFactor(1, 1); + // + + connect(m_pSplitter, SIGNAL(splitterMoved(int,int)), this, SLOT(OnSplitterMoved(int,int))); + + // Link selections + //m_pTree->setSelectionModel(m_pList->selectionModel()); // this works only when booth views share the same data model + + connect(m_pTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnTreeSelectionChanged(QItemSelection,QItemSelection))); + connect(m_pList->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this, SLOT(OnListSelectionChanged(QItemSelection,QItemSelection))); + + connect(m_pTree->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnTreeCurrentChanged(QModelIndex,QModelIndex))); + connect(m_pList->selectionModel(), SIGNAL(currentChanged(QModelIndex,QModelIndex)), this, SLOT(OnListCurrentChanged(QModelIndex,QModelIndex))); + + m_bTreeEnabled = true; + m_pList->setColumnHidden(0, true); + + // link expansion + connect(m_pTree, SIGNAL(expanded(const QModelIndex)), this, SLOT(OnExpandTree(const QModelIndex))); + connect(m_pTree, SIGNAL(collapsed(const QModelIndex)), this, SLOT(OnCollapseTree(const QModelIndex))); + //connect(m_pList, SIGNAL(expanded(const QModelIndex)), this, SLOT(expand(const QModelIndex))); + //connect(m_pList, SIGNAL(collapsed(const QModelIndex)), this, SLOT(collapse(const QModelIndex))); + + // link scrollbars + m_pTree->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff); + m_pTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + m_pList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOn); + connect(m_pList->verticalScrollBar(), SIGNAL(valueChanged(int)), m_pTree->verticalScrollBar(), SLOT(setValue(int))); + connect(m_pTree->verticalScrollBar(), SIGNAL(valueChanged(int)), m_pList->verticalScrollBar(), SLOT(setValue(int))); + + connect(m_pTree, SIGNAL(clicked(const QModelIndex&)), this, SLOT(OnClickedTree(const QModelIndex&))); + connect(m_pList, SIGNAL(clicked(const QModelIndex&)), this, SIGNAL(clicked(const QModelIndex&))); + + connect(m_pTree, SIGNAL(doubleClicked(const QModelIndex&)), this, SLOT(OnDoubleClickedTree(const QModelIndex&))); + connect(m_pList, SIGNAL(doubleClicked(const QModelIndex&)), this, SIGNAL(doubleClicked(const QModelIndex&))); + + //QTimer::singleShot(0, this, [this]() { + // emit TreeEnabled(m_bTreeEnabled); + //}); +} + +CSplitTreeView::~CSplitTreeView() +{ + +} + +int CSplitTreeView::GetTreeWidth() const +{ + QList sizes = m_pSplitter->sizes(); + return sizes[0]; +} + +void CSplitTreeView::SetTreeWidth(int Width) +{ + QList sizes = m_pSplitter->sizes(); + int total = sizes[0] + sizes[1]; + sizes[0] = Width; + sizes[1] = total - Width; +} + +void CSplitTreeView::OnSplitterMoved(int pos, int index) +{ + if (index != 1) + return; + + //if ((pos > 0) == m_bTreeEnabled) + // return; + //m_bTreeEnabled = (pos > 0); + + m_pList->setColumnHidden(0, pos > 0); + + //emit TreeEnabled(m_bTreeEnabled); +} + +void CSplitTreeView::OnExpandTree(const QModelIndex& index) +{ + m_pList->expand(m_pOneModel->mapToSource(index)); +} + +void CSplitTreeView::OnCollapseTree(const QModelIndex& index) +{ + m_pList->collapse(m_pOneModel->mapToSource(index)); +} + +void CSplitTreeView::OnClickedTree(const QModelIndex& Index) +{ + emit clicked(m_pOneModel->mapToSource(Index)); +} + +void CSplitTreeView::OnDoubleClickedTree(const QModelIndex& Index) +{ + emit doubleClicked(m_pOneModel->mapToSource(Index)); +} + +void CSplitTreeView::expand(const QModelIndex &index) +{ + m_pTree->expand(m_pOneModel->mapFromSource(index)); +} + +void CSplitTreeView::collapse(const QModelIndex &index) +{ + m_pTree->collapse(m_pOneModel->mapFromSource(index)); +} + +void CSplitTreeView::OnTreeSelectionChanged(const QItemSelection& Selected, const QItemSelection& Deselected) +{ + if (m_LockSellection) + return; + m_LockSellection = 1; + QItemSelection SelectedItems; + foreach(const QModelIndex& Index, m_pTree->selectionModel()->selectedIndexes()) + { + QModelIndex ModelIndex = m_pOneModel->mapToSource(Index); + + QModelIndex ModelL = m_pModel->index(ModelIndex.row(), 0, ModelIndex.parent()); + QModelIndex ModelR = m_pModel->index(ModelIndex.row(), m_pModel->columnCount()-1, ModelIndex.parent()); + + SelectedItems.append(QItemSelectionRange(ModelL, ModelR)); + } + m_pList->selectionModel()->select(SelectedItems, QItemSelectionModel::ClearAndSelect); + /* + foreach(const QModelIndex& Index, Selected.indexes()) + { + QModelIndex ModelIndex = m_pOneModel->mapToSource(Index); + + QModelIndex ModelL = m_pModel->index(ModelIndex.row(), 0, ModelIndex.parent()); + QModelIndex ModelR = m_pModel->index(ModelIndex.row(), m_pModel->columnCount()-1, ModelIndex.parent()); + + m_pList->selectionModel()->select(QItemSelection(ModelL, ModelR), QItemSelectionModel::Select); + } + foreach(const QModelIndex& Index, Deselected.indexes()) + { + QModelIndex ModelIndex = m_pOneModel->mapToSource(Index); + + QModelIndex ModelL = m_pModel->index(ModelIndex.row(), 0, ModelIndex.parent()); + QModelIndex ModelR = m_pModel->index(ModelIndex.row(), m_pModel->columnCount()-1, ModelIndex.parent()); + + m_pList->selectionModel()->select(QItemSelection(ModelL, ModelR), QItemSelectionModel::Deselect); + }*/ + m_LockSellection = 0; +} + +void CSplitTreeView::OnListSelectionChanged(const QItemSelection& Selected, const QItemSelection& Deselected) +{ + if (m_LockSellection != 2) + emit selectionChanged(Selected, Deselected); + + if (m_LockSellection) + return; + m_LockSellection = 1; + QItemSelection SelectedItems; + foreach(const QModelIndex& Index, m_pList->selectionModel()->selectedIndexes()) + { + QModelIndex ModelIndex = m_pOneModel->mapFromSource(Index); + if (ModelIndex.column() != 0) + continue; + + QModelIndex Model = m_pOneModel->index(ModelIndex.row(), 0, ModelIndex.parent()); + + SelectedItems.append(QItemSelectionRange(Model)); + } + m_pTree->selectionModel()->select(SelectedItems, QItemSelectionModel::ClearAndSelect); + /*foreach(const QModelIndex& Index, Selected.indexes()) + { + QModelIndex ModelIndex = m_pOneModel->mapFromSource(Index); + if (ModelIndex.column() != 0) + continue; + m_pTree->selectionModel()->select(ModelIndex, QItemSelectionModel::Select); + } + foreach(const QModelIndex& Index, Deselected.indexes()) + { + QModelIndex ModelIndex = m_pOneModel->mapFromSource(Index); + if (ModelIndex.column() != 0) + continue; + m_pTree->selectionModel()->select(ModelIndex, QItemSelectionModel::Deselect); + }*/ + m_LockSellection = 0; +} + +void CSplitTreeView::OnTreeCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (m_LockSellection) + return; + m_LockSellection = 2; + int hPos = m_pList->horizontalScrollBar()->value(); // fix horizontalScrollBar position reset on selection + m_pList->selectionModel()->setCurrentIndex(m_pOneModel->mapToSource(current), QItemSelectionModel::SelectCurrent); + m_pList->horizontalScrollBar()->setValue(hPos); + m_LockSellection = 0; + emit currentChanged(m_pOneModel->mapToSource(current), m_pOneModel->mapToSource(previous)); +} + +void CSplitTreeView::OnListCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous) +{ + if (m_LockSellection) + return; + m_LockSellection = 2; + m_pTree->selectionModel()->setCurrentIndex(m_pOneModel->mapFromSource(current), QItemSelectionModel::SelectCurrent); + m_LockSellection = 0; + emit currentChanged(current, previous); +} + +QModelIndexList CSplitTreeView::selectedRows() const +{ + int Column = 0; + for (int i = 0; i < m_pModel->columnCount(); i++) + { + if (!m_pList->isColumnHidden(i)) + { + Column = i; + break; + } + } + + QModelIndexList IndexList; + foreach(const QModelIndex& Index, m_pList->selectionModel()->selectedIndexes()) + { + if (Index.column() == Column) + IndexList.append(Index); + } + return IndexList; +} + +void CSplitTreeView::SetTree(bool bSet) +{ + m_bTreeEnabled = bSet; + emit TreeEnabled(m_bTreeEnabled); +} + +void CSplitTreeView::OnTreeCustomSortByColumn(int column) +{ + Qt::SortOrder order = m_pTree->header()->sortIndicatorOrder(); + if (order == Qt::AscendingOrder) + SetTree(!m_bTreeEnabled); + m_pList->sortByColumn(column, order); + m_pTree->header()->setSortIndicatorShown(true); +} + +void CSplitTreeView::OnListCustomSortByColumn(int column) +{ + Qt::SortOrder order = m_pList->header()->sortIndicatorOrder(); + if (column == 0) + SetTree(!m_bTreeEnabled); + else if (m_bTreeEnabled) + SetTree(false); + m_pTree->header()->setSortIndicatorShown(false); +} + +static const qint32 CSplitTreeViewMagic = 'STVs'; + +QByteArray CSplitTreeView::saveState() const +{ + int version = 1; + QByteArray data; + QDataStream stream(&data, QIODevice::WriteOnly); + + stream << qint32(CSplitTreeViewMagic); + stream << qint32(version); + stream << m_pList->header()->saveState(); + stream << m_pSplitter->saveState(); + stream << m_bTreeEnabled; + + return data; +} + +bool CSplitTreeView::restoreState(const QByteArray &state) +{ + int version = 1; + QByteArray sd = state; + QDataStream stream(&sd, QIODevice::ReadOnly); + + qint32 marker; + stream >> marker; + qint32 v; + stream >> v; + if (marker != CSplitTreeViewMagic || v > version) + return false; + + QByteArray header; + stream >> header; + m_pList->header()->restoreState(header); + + QByteArray splitter; + stream >> splitter; + m_pSplitter->restoreState(splitter); + + stream >> m_bTreeEnabled; + emit TreeEnabled(m_bTreeEnabled); + + m_pTree->header()->setSortIndicatorShown(m_pList->header()->sortIndicatorSection() == 0); + + QList sizes = m_pSplitter->sizes(); + for(int i=0; i < sizes.count(); i++) + OnSplitterMoved(sizes[i], i); + + return true; +} diff --git a/SandboxiePlus/MiscHelpers/Common/SplitTreeView.h b/SandboxiePlus/MiscHelpers/Common/SplitTreeView.h new file mode 100644 index 00000000..b2a16c67 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/SplitTreeView.h @@ -0,0 +1,116 @@ +#pragma once +#include + +#include "TreeViewEx.h" + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT COneColumnModel : public QIdentityProxyModel +{ + Q_OBJECT +public: + COneColumnModel( QObject* parrent = 0) : QIdentityProxyModel(parrent) {} + + int columnCount(const QModelIndex &parent = QModelIndex()) const { return 1; } +}; + +class MISCHELPERS_EXPORT CSplitTreeView : public QWidget +{ + Q_OBJECT +public: + CSplitTreeView(QAbstractItemModel* pModel, QWidget *parent = 0); + virtual ~CSplitTreeView(); + + QTreeView* GetView() { return m_pList; } + QTreeView* GetTree() { return m_pTree; } + + void SetTree(bool bSet); + bool IsTree() const { return m_bTreeEnabled; } + + void SetTreeWidth(int Width); + int GetTreeWidth() const; + + QModelIndex currentIndex() const { return m_pList->currentIndex(); } + QModelIndexList selectedRows() const; + + QByteArray saveState() const; + bool restoreState(const QByteArray &state); + + template + void StartUpdatingWidgets(T& OldMap, T& Map) + { + for(typename T::iterator I = Map.begin(); I != Map.end();) + { + if(I.value().first == NULL) + I = Map.erase(I); + else + { + OldMap.insert(I.key(), I.value()); + I++; + } + } + } + + template + void EndUpdatingWidgets(T& OldMap, T& Map) + { + for(typename T::iterator I = OldMap.begin(); I != OldMap.end(); I++) + { + Map.remove(I.key()); + if(I.value().second.isValid()) + m_pList->setIndexWidget(I.value().second, NULL); + } + } + +signals: + void TreeEnabled(bool bEnabled); + + void MenuRequested(const QPoint &); + + void clicked(const QModelIndex& Index); + void doubleClicked(const QModelIndex& Index); + void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); + void selectionChanged(const QItemSelection& selected, const QItemSelection& seselected); + +public slots: + void hideColumn(int column) { m_pList->hideColumn(column); } + void showColumn(int column) { m_pList->showColumn(column); } + void expand(const QModelIndex &index); + void collapse(const QModelIndex &index); + void resizeColumnToContents(int column) { m_pList->resizeColumnToContents(column); } + void sortByColumn(int column) { m_pList->sortByColumn(column); } + void expandAll() { m_pTree->expandAll(); } + void collapseAll() { m_pTree->collapseAll(); } + void expandToDepth(int depth) { m_pTree->expandToDepth(depth); } + +private slots: + void OnSplitterMoved(int pos, int index); + + void OnClickedTree(const QModelIndex& Index); + void OnDoubleClickedTree(const QModelIndex& Index); + + void OnExpandTree(const QModelIndex& index); + void OnCollapseTree(const QModelIndex& index); + + void OnTreeSelectionChanged(const QItemSelection& Selected, const QItemSelection& Deselected); + void OnListSelectionChanged(const QItemSelection& Selected, const QItemSelection& Deselected); + + void OnTreeCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous); + void OnListCurrentChanged(const QModelIndex ¤t, const QModelIndex &previous); + + void OnTreeCustomSortByColumn(int column); + void OnListCustomSortByColumn(int column); + +private: + + QHBoxLayout* m_pMainLayout; + + bool m_bTreeEnabled; + QSplitter* m_pSplitter; + QTreeView* m_pTree; + QTreeView* m_pList; + QAbstractItemModel* m_pModel; + COneColumnModel* m_pOneModel; + + int m_LockSellection; +}; diff --git a/SandboxiePlus/MiscHelpers/Common/TabPanel.cpp b/SandboxiePlus/MiscHelpers/Common/TabPanel.cpp new file mode 100644 index 00000000..2f280104 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/TabPanel.cpp @@ -0,0 +1,76 @@ +#include "stdafx.h" +#include "TabPanel.h" + +CTabPanel::CTabPanel(QWidget* parent) + : QWidget(parent) +{ + m_pMainLayout = new QVBoxLayout(); + m_pMainLayout->setMargin(0); + this->setLayout(m_pMainLayout); + +#ifdef USE_QEXTWIDGETS + m_pTabs = new QTabWidgetEx(); + m_pTabs->setMultiRow(true); +#else + m_pTabs = new QTabWidget(); +#endif + m_pMainLayout->addWidget(m_pTabs); +} + +CTabPanel::~CTabPanel() +{ +} + +void CTabPanel::ShowTab(int Index, bool bShow) +{ + if (Index >= m_AllTabs.size()) + return; + + m_AllTabs[Index].bVisible = bShow; + + int ActiveTab = 0; + QStringList VisibleTabs; + + SaveTabs(ActiveTab, VisibleTabs); + + RebuildTabs(ActiveTab, VisibleTabs); +} + +void CTabPanel::AddTab(QWidget* pWidget, const QString& Name) +{ + STab Tab{Name, pWidget, true}; + m_AllTabs.append(Tab); + m_pTabs->addTab(Tab.pWidget, Tab.Name); +} + +void CTabPanel::SaveTabs(int& ActiveTab, QStringList& VisibleTabs) +{ + ActiveTab = 0; + VisibleTabs.clear(); + for(int i=0; i < m_AllTabs.size(); i++) + { + STab& Tab = m_AllTabs[i]; + + VisibleTabs.append(QString::number(Tab.bVisible)); + if (m_pTabs->currentWidget() == Tab.pWidget) + ActiveTab = i; + } +} + +void CTabPanel::RebuildTabs(const int ActiveTab, const QStringList& VisibleTabs) +{ + m_pTabs->clear(); + for(int i=0; i < m_AllTabs.size(); i++) + { + STab& Tab = m_AllTabs[i]; + + if (VisibleTabs.size() <= i || VisibleTabs[i].toInt() != 0) + { + Tab.bVisible = true; + + m_pTabs->addTab(Tab.pWidget, Tab.Name); + if (i == ActiveTab) + m_pTabs->setCurrentWidget(Tab.pWidget); + } + } +} diff --git a/SandboxiePlus/MiscHelpers/Common/TabPanel.h b/SandboxiePlus/MiscHelpers/Common/TabPanel.h new file mode 100644 index 00000000..eb0d85ba --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/TabPanel.h @@ -0,0 +1,44 @@ +#pragma once +#include +#include "Settings.h" + +#ifdef USE_QEXTWIDGETS +#include "../../qextwidgets/qtabwidgetex.h" +#endif + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CTabPanel : public QWidget +{ + Q_OBJECT +public: + CTabPanel(QWidget* parent = 0); + virtual ~CTabPanel(); + + virtual int GetTabCount() { return m_AllTabs.size(); } + virtual QString GetTabLabel(int Index) { if (Index >= m_AllTabs.size()) return ""; return m_AllTabs[Index].Name; } + virtual void ShowTab(int Index, bool bShow); + virtual bool IsTabVisible(int Index) { if (Index >= m_AllTabs.size()) return ""; return m_AllTabs[Index].bVisible; } + +protected: + virtual void InitializeTabs() = 0; + virtual void AddTab(QWidget* pWidget, const QString& Name); + virtual void RebuildTabs(const int ActiveTab, const QStringList& VisibleTabs); + virtual void SaveTabs(int& ActiveTab, QStringList& VisibleTabs); + + QVBoxLayout* m_pMainLayout; + +#ifdef USE_QEXTWIDGETS + QTabWidgetEx* m_pTabs; +#else + QTabWidget* m_pTabs; +#endif + struct STab + { + QString Name; + QWidget* pWidget; + bool bVisible; + }; + QVector m_AllTabs; +}; + diff --git a/SandboxiePlus/MiscHelpers/Common/TreeItemModel.cpp b/SandboxiePlus/MiscHelpers/Common/TreeItemModel.cpp new file mode 100644 index 00000000..cda3334d --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/TreeItemModel.cpp @@ -0,0 +1,502 @@ +#include "stdafx.h" +#include "TreeItemModel.h" + +#define FIRST_COLUMN 0 + +bool CTreeItemModel::m_DarkMode = false; + +CTreeItemModel::CTreeItemModel(QObject *parent) +: QAbstractItemModelEx(parent) +{ + m_bTree = true; + m_bUseIcons = false; + m_Root = NULL; +} + +CTreeItemModel::~CTreeItemModel() +{ + delete m_Root; +} + +CSimpleTreeModel::CSimpleTreeModel(QObject *parent) + : CTreeItemModel(parent) +{ + m_Root = MkNode(QVariant()); +} + +QList CSimpleTreeModel::MakePath(const QVariantMap& Cur, const QMap& List) +{ + QVariant ParentID = Cur["ParentID"]; + QVariantMap Parent = List.value(ParentID); + + QList Path; + if (!Parent.isEmpty()) + { + Path = MakePath(Parent, List); + Path.append(ParentID); + } + return Path; +} + +bool CSimpleTreeModel::TestPath(const QList& Path, const QVariantMap& Cur, const QMap& List, int Index) +{ + QVariant ParentID = Cur["ParentID"]; + QVariantMap Parent = List.value(ParentID); + + if (!Parent.isEmpty()) + { + if(Index >= Path.size() || Path[Path.size() - Index - 1] != ParentID) + return false; + + return TestPath(Path, Parent, List, Index + 1); + } + + return Path.size() == Index; +} + +void CSimpleTreeModel::Sync(const QMap& List) +{ + QMap, QList > New; + QHash Old = m_Map; + + foreach (const QVariantMap& Cur, List) + { + QVariant ID = Cur["ID"]; + + QModelIndex Index; + + STreeNode* pNode = static_cast(Old.value(ID)); + if(!pNode || (m_bTree ? !TestPath(pNode->Path, Cur, List) : !pNode->Path.isEmpty())) + { + pNode = static_cast(MkNode(ID)); + pNode->Values.resize(columnCount()); + if(m_bTree) + pNode->Path = MakePath(Cur, List); + pNode->IsBold = Cur["IsBold"].toBool(); + pNode->Icon = Cur["Icon"]; + New[pNode->Path].append(pNode); + } + else + { + Old[ID] = NULL; + Index = Find(m_Root, pNode); + } + + //if(Index.isValid()) // this is to slow, be more precise + // emit dataChanged(createIndex(Index.row(), 0, pNode), createIndex(Index.row(), columnCount()-1, pNode)); + + int Col = 0; + bool State = false; + bool Changed = false; + + QVariantMap Values = Cur["Values"].toMap(); + for(int section = FIRST_COLUMN; section < columnCount(); section++) + { + if (!m_Columns.contains(section)) + continue; // ignore columns which are hidden + + QVariant Value = Values[QString::number(section)]; + + STreeNode::SValue& ColValue = pNode->Values[section]; + + if (ColValue.Raw != Value) + { + Changed = true; + ColValue.Raw = Value; + + //ColValue.Formated = + } + + if(State != Changed) + { + if(State && Index.isValid()) + emit dataChanged(createIndex(Index.row(), Col, pNode), createIndex(Index.row(), section-1, pNode)); + State = Changed; + Col = section; + } + Changed = false; + } + if(State && Index.isValid()) + emit dataChanged(createIndex(Index.row(), Col, pNode), createIndex(Index.row(), columnCount()-1, pNode)); + + } + + CTreeItemModel::Sync(New, Old); +} + +void CTreeItemModel::Sync(QMap, QList >& New, QHash& Old) +{ + Purge(m_Root, QModelIndex(), Old); + + if(!New.isEmpty()) + { + emit layoutAboutToBeChanged(); + + //foreach(const QString& Path, New.uniqueKeys()) + for(QMap, QList >::const_iterator I = New.begin(); I != New.end(); I++) + Fill(m_Root, QModelIndex(), I.key(), 0, I.value(), I.key()); + + emit layoutChanged(); + } + + emit Updated(); +} + +/*void CTreeItemModel::CountItems() +{ + CountItems(m_Root); +} + +int CTreeItemModel::CountItems(STreeNode* pRoot) +{ + if(pRoot->Children.isEmpty()) + return 1; + + int Counter = 0; + foreach(STreeNode* pChild, pRoot->Children) + Counter += CountItems(pChild); + //pRoot->AllChildren = Counter; + return Counter; +}*/ + +void CTreeItemModel::Purge(STreeNode* pParent, const QModelIndex &parent, QHash &Old) +{ + int Removed = 0; + + int Begin = -1; + int End = -1; + for(int i = pParent->Children.count()-1; i >= -1; i--) + { + STreeNode* pNode = i >= 0 ? pNode = pParent->Children[i] : NULL; + if(pNode) + Purge(pNode, index(i, 0, parent), Old); + + bool bRemove = false; + if(pNode && (pNode->ID.isNull() || (bRemove = Old.value(pNode->ID) != NULL)) && pNode->Children.isEmpty()) // remove it + { + //m_Map.remove(pNode->ID, pNode); + m_Map.remove(pNode->ID); + if(End == -1) + End = i; + } + else // keep it + { + if(bRemove) + { + ASSERT(!pNode->Children.isEmpty()); // we wanted to remove it but we have to keep it + //m_Map.remove(pNode->ID, pNode); + m_Map.remove(pNode->ID); + pNode->ID = QVariant(); + pNode->Icon.clear(); + } + + if(End != -1) // remove whats to be removed at once + { + Begin = i + 1; + + beginRemoveRows(parent, Begin, End); + //ASSERT(pParent->Children.count() > End); + for(int j = End; j >= Begin; j--) + { + pNode = pParent->Children.takeAt(j); + delete pNode; + Removed++; + } + endRemoveRows(); + + End = -1; + Begin = -1; + } + } + } + + if(Removed > 0) + { + pParent->Aux.clear(); + for (int i = pParent->Children.count() - 1; i >= 0; i--) + { + pParent->Children[i]->Row = i; + pParent->Aux.insert(pParent->Children[i]->ID, i); + } + } +} + +void CTreeItemModel::Fill(STreeNode* pParent, const QModelIndex &parent, const QList& Paths, int PathsIndex, const QList& New, const QList& Path) +{ + if(Paths.size() > PathsIndex) + { + QVariant CurPath = Paths.at(PathsIndex); + STreeNode* pNode; + int i = pParent->Aux.value(CurPath, -1); + if(i != -1) + pNode = pParent->Children[i]; + else + { + i = 0; + pNode = MkNode(QVariant()); + pNode->Parent = pParent; + pNode->Values.resize(columnCount()); + + //int Count = pParent->Children.count(); + //beginInsertRows(parent, Count, Count); + pParent->Aux.insert(pNode->ID, pParent->Children.size()); + pNode->Row = pParent->Children.size(); + pParent->Children.append(pNode); + //endInsertRows(); + } + Fill(pNode, index(i, 0, parent), Paths, PathsIndex + 1, New, Path); + } + else + { + for(QList::const_iterator I = New.begin(); I != New.end(); I++) + { + STreeNode* pNode = *I; + ASSERT(pNode); + //ASSERT(!m_Map.contains(pNode->ID)); + m_Map.insert(pNode->ID, pNode); + pNode->Parent = pParent; + + //int Count = pParent->Children.count(); + //beginInsertRows(parent, Count, Count); + pParent->Aux.insert(pNode->ID, pParent->Children.size()); + pNode->Row = pParent->Children.size(); + pParent->Children.append(pNode); + //endInsertRows(); + } + } +} + +QModelIndex CTreeItemModel::FindIndex(const QVariant& ID) +{ + if(STreeNode* pNode = m_Map.value(ID)) + return Find(m_Root, pNode); + return QModelIndex(); +} + +QModelIndex CTreeItemModel::Find(STreeNode* pParent, STreeNode* pNode) +{ + // ''find'' + ASSERT(pNode->Parent->Children[pNode->Row] == pNode); + return createIndex(pNode->Row, FIRST_COLUMN, pNode); + /* + int count = pParent->Children.count(); + for(int i=0; i < count; i++) + { + if (pParent->Children[i] == pNode) + { + ASSERT(i == pNode->Row); + return createIndex(i, FIRST_COLUMN, pNode); + } + + QModelIndex Index = Find(pParent->Children[i], pNode); + if(Index.isValid()) + return Index; + } + return QModelIndex(); + */ +} + +void CTreeItemModel::Clear() +{ + QHash Old = m_Map; + //beginResetModel(); + Purge(m_Root, QModelIndex(), Old); + //endResetModel(); + ASSERT(m_Map.isEmpty()); +} + +void CTreeItemModel::RemoveIndex(const QModelIndex &index) +{ + if (!index.isValid()) + return; + + STreeNode* pNode = static_cast(index.internalPointer()); + ASSERT(pNode); + + QHash Old; + Old[pNode->ID] = pNode; + + Purge(m_Root, QModelIndex(), Old); +} + +QVariant CTreeItemModel::data(const QModelIndex &index, int role) const +{ + return Data(index, role, index.column()); +} + +bool CTreeItemModel::setData(const QModelIndex &index, const QVariant &value, int role) +{ + if(index.column() == FIRST_COLUMN && role == Qt::CheckStateRole) + { + STreeNode* pNode = static_cast(index.internalPointer()); + ASSERT(pNode); + emit CheckChanged(pNode->ID, value.toInt() != Qt::Unchecked); + return true; + } + return false; +} + +QVariant CTreeItemModel::Data(const QModelIndex &index, int role, int section) const +{ + if (!index.isValid()) + return QVariant(); + + //if(role == Qt::SizeHintRole) + // return QSize(64,16); // for fixing height + + STreeNode* pNode = static_cast(index.internalPointer()); + ASSERT(pNode); + + if (pNode->Values.size() <= section) + return QVariant(); + + return NodeData(pNode, role, section); +} + +QVariant CTreeItemModel::NodeData(STreeNode* pNode, int role, int section) const +{ + switch(role) + { + case Qt::DisplayRole: + { + STreeNode::SValue& Value = pNode->Values[section]; + return Value.Formated.isValid() ? Value.Formated : Value.Raw; + } + case Qt::EditRole: // sort role + { + STreeNode::SValue& value = pNode->Values[section]; + return value.SortKey.isValid() ? value.SortKey : value.Raw; + } + case Qt::ToolTipRole: + { + QString ToolTip; + emit ToolTipCallback(pNode->ID, ToolTip); + if(!ToolTip.isNull()) + return ToolTip; + break; + } + case Qt::DecorationRole: + { + if (m_bUseIcons && section == FIRST_COLUMN) + return pNode->Icon.isValid() ? pNode->Icon : GetDefaultIcon(); + break; + } + case Qt::FontRole: + { + if (section == FIRST_COLUMN && pNode->IsBold) + { + QFont fnt; + fnt.setBold(true); + return fnt; + } + break; + } + case Qt::BackgroundRole: + { + if(!m_DarkMode) + return pNode->Color.isValid() ? pNode->Color : QVariant(); + break; + } + case Qt::ForegroundRole: + { + if(m_DarkMode) + return pNode->Color.isValid() ? pNode->Color : QVariant(); + else if (pNode->IsGray) + { + QColor Color = Qt::darkGray; + return QBrush(Color); + } + break; + } + case Qt::CheckStateRole: + { + /*if(section == eModule) + { + if(pNode->...) + return Qt::Unchecked; + else + return Qt::Checked; + }*/ + break; + } + case Qt::UserRole: + { + switch(section) + { + case FIRST_COLUMN: return pNode->ID; + } + break; + } + } + return QVariant(); +} + +Qt::ItemFlags CTreeItemModel::flags(const QModelIndex &index) const +{ + if (!index.isValid()) + return 0; + if(index.column() == 0) + return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; + return Qt::ItemIsEnabled | Qt::ItemIsSelectable; +} + +QModelIndex CTreeItemModel::index(int row, int column, const QModelIndex &parent) const +{ + if (!hasIndex(row, column, parent)) + return QModelIndex(); + + STreeNode* pParent; + if (!parent.isValid()) + pParent = m_Root; + else + pParent = static_cast(parent.internalPointer()); + + if(STreeNode* pNode = pParent->Children.count() > row ? pParent->Children[row] : NULL) + return createIndex(row, column, pNode); + return QModelIndex(); +} + +QModelIndex CTreeItemModel::parent(const QModelIndex &index) const +{ + if (!index.isValid()) + return QModelIndex(); + + STreeNode* pNode = static_cast(index.internalPointer()); + ASSERT(pNode->Parent); + STreeNode* pParent = pNode->Parent; + if (pParent == m_Root) + return QModelIndex(); + + int row = 0; + if(pParent->Parent) + row = pParent->Parent->Children.indexOf(pParent); + return createIndex(row, 0, pParent); +} + +int CTreeItemModel::rowCount(const QModelIndex &parent) const +{ + if (parent.column() > 0) + return 0; + + STreeNode* pNode; + if (!parent.isValid()) + pNode = m_Root; + else + pNode = static_cast(parent.internalPointer()); + return pNode->Children.count(); +} + +int CSimpleTreeModel::columnCount(const QModelIndex &parent) const +{ + return m_Headers.count(); +} + +QVariant CSimpleTreeModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + if (section < m_Headers.size()) + return m_Headers.at(section); + } + return QVariant(); +} \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/TreeItemModel.h b/SandboxiePlus/MiscHelpers/Common/TreeItemModel.h new file mode 100644 index 00000000..d77ce8e1 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/TreeItemModel.h @@ -0,0 +1,123 @@ +#pragma once +#include "TreeViewEx.h" + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CTreeItemModel : public QAbstractItemModelEx +{ + Q_OBJECT + +public: + CTreeItemModel(QObject *parent = 0); + virtual ~CTreeItemModel(); + + void SetTree(bool bTree) { m_bTree = bTree; } + bool IsTree() const { return m_bTree; } + void SetUseIcons(bool bUseIcons) { m_bUseIcons = bUseIcons; } + static void SetDarkMode(bool bDark) { m_DarkMode = bDark;} + + //void CountItems(); + QModelIndex FindIndex(const QVariant& ID); + void RemoveIndex(const QModelIndex &index); + + QVariant Data(const QModelIndex &index, int role, int section) const; + + // derived functions + virtual QVariant data(const QModelIndex &index, int role) const; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role); + virtual Qt::ItemFlags flags(const QModelIndex &index) const; + virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; + virtual QModelIndex parent(const QModelIndex &index) const; + virtual int rowCount(const QModelIndex &parent = QModelIndex()) const; + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const = 0; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const = 0; + +public slots: + void Clear(); + +signals: + void CheckChanged(const QVariant& ID, bool State); + void ToolTipCallback(const QVariant& ID, QString& ToolTip) const; + void Updated(); + +protected: + struct STreeNode + { + STreeNode(const QVariant& Id){ + ID = Id; + Parent = NULL; + Row = 0; + //AllChildren = 0; + + IsBold = false; + IsGray = false; + } + virtual ~STreeNode(){ + foreach(STreeNode* pNode, Children) + delete pNode; + } + + QVariant ID; + + STreeNode* Parent; + int Row; + QList Path; + QList Children; + //int AllChildren; + QMap Aux; + + QVariant Icon; + bool IsBold; + bool IsGray; + QColor Color; + struct SValue + { + QVariant Raw; + QVariant SortKey; + QVariant Formated; + }; + QVector Values; + }; + + virtual QVariant NodeData(STreeNode* pNode, int role, int section) const; + + virtual STreeNode* MkNode(const QVariant& Id) = 0; // { return new STreeNode(Id); } + + void Sync(QMap, QList >& New, QHash& Old); + void Purge(STreeNode* pParent, const QModelIndex &parent, QHash& Old); + void Fill(STreeNode* pParent, const QModelIndex &parent, const QList& Paths, int PathsIndex, const QList& New, const QList& Path); + QModelIndex Find(STreeNode* pParent, STreeNode* pNode); + //int CountItems(STreeNode* pRoot); + + virtual QVariant GetDefaultIcon() const { return QVariant(); } + + STreeNode* m_Root; + QHash m_Map; + bool m_bTree; + bool m_bUseIcons; + + static bool m_DarkMode; +}; + +class MISCHELPERS_EXPORT CSimpleTreeModel : public CTreeItemModel +{ + Q_OBJECT + +public: + CSimpleTreeModel(QObject *parent = 0); + + void Sync(const QMap& List); + + void setHeaderLabels(const QStringList& Columns) { m_Headers = Columns; } + + virtual int columnCount(const QModelIndex &parent = QModelIndex()) const; + virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + +protected: + virtual STreeNode* MkNode(const QVariant& Id) { return new STreeNode(Id); } + + QList MakePath(const QVariantMap& Cur, const QMap& List); + bool TestPath(const QList& Path, const QVariantMap& Cur, const QMap& List, int Index = 0); + + QStringList m_Headers; +}; \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/TreeViewEx.h b/SandboxiePlus/MiscHelpers/Common/TreeViewEx.h new file mode 100644 index 00000000..00de812d --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/TreeViewEx.h @@ -0,0 +1,346 @@ +#pragma once + +#include + +__inline uint qHash( const QVariant & var ) +{ + if ( !var.isValid() || var.isNull() ) + //return -1; + Q_ASSERT(0); + + switch ( var.type() ) + { + case QVariant::Int: + return qHash( var.toInt() ); + break; + case QVariant::UInt: + return qHash( var.toUInt() ); + break; + case QVariant::Bool: + return qHash( var.toUInt() ); + break; + case QVariant::Double: + return qHash( var.toUInt() ); + break; + case QVariant::LongLong: + return qHash( var.toLongLong() ); + break; + case QVariant::ULongLong: + return qHash( var.toULongLong() ); + break; + case QVariant::String: + return qHash( var.toString() ); + break; + case QVariant::Char: + return qHash( var.toChar() ); + break; + case QVariant::StringList: + return qHash( var.toString() ); + break; + case QVariant::ByteArray: + return qHash( var.toByteArray() ); + break; + case QVariant::Date: + case QVariant::Time: + case QVariant::DateTime: + case QVariant::Url: + case QVariant::Locale: + case QVariant::RegExp: + return qHash( var.toString() ); + break; + case QVariant::Map: + case QVariant::List: + case QVariant::BitArray: + case QVariant::Size: + case QVariant::SizeF: + case QVariant::Rect: + case QVariant::LineF: + case QVariant::Line: + case QVariant::RectF: + case QVariant::Point: + case QVariant::PointF: + // not supported yet + break; + case QVariant::UserType: + case QVariant::Invalid: + default: + return -1; + } + + // could not generate a hash for the given variant + return -1; +} + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT QAbstractItemModelEx : public QAbstractItemModel +{ + Q_OBJECT + +public: + QAbstractItemModelEx(QObject *parent = 0) : QAbstractItemModel(parent) {} + virtual ~QAbstractItemModelEx() {} + + bool IsColumnEnabled(int column) + { + return m_Columns.contains(column); + } + + void SetColumnEnabled(int column, bool set) + { + if (!set) + m_Columns.remove(column); + else + m_Columns.insert(column); + } + +protected: + + QSet m_Columns; +}; + +class MISCHELPERS_EXPORT QTreeViewEx: public QTreeView +{ + Q_OBJECT +public: + QTreeViewEx(QWidget *parent = 0) : QTreeView(parent) + { + setUniformRowHeights(true); + + m_ColumnReset = 1; + + header()->setContextMenuPolicy(Qt::CustomContextMenu); + connect(header(), SIGNAL(customContextMenuRequested( const QPoint& )), this, SLOT(OnMenuRequested(const QPoint &))); + + m_pMenu = new QMenu(this); + } + + void setColumnReset(int iMode) + { + m_ColumnReset = iMode; + } + + void setColumnFixed(int column, bool fixed) + { + if (fixed) + m_FixedColumns.insert(column); + else + m_FixedColumns.remove(column); + } + + bool isColumnFixed(int column) const + { + return m_FixedColumns.contains(column); + } + + QModelIndexList selectedRows() const + { + int Column = 0; + QAbstractItemModel* pModel = model(); + for (int i = 0; i < pModel->columnCount(); i++) + { + if (!isColumnHidden(i)) + { + Column = i; + break; + } + } + + QModelIndexList IndexList; + foreach(const QModelIndex& Index, selectedIndexes()) + { + if (Index.column() == Column) + IndexList.append(Index); + } + return IndexList; + } + + template + void StartUpdatingWidgets(T& OldMap, T& Map) + { + for(typename T::iterator I = Map.begin(); I != Map.end();) + { + if(I.value().first == NULL) + I = Map.erase(I); + else + { + OldMap.insert(I.key(), I.value()); + I++; + } + } + } + + template + void EndUpdatingWidgets(T& OldMap, T& Map) + { + for(typename T::iterator I = OldMap.begin(); I != OldMap.end(); I++) + { + Map.remove(I.key()); + if(I.value().second.isValid()) + setIndexWidget(I.value().second, NULL); + } + } + + bool restoreState(const QByteArray &state) + { + bool bRet = header()->restoreState(state); + + SyncColumnsWithModel(); + + return bRet; + } + + QByteArray saveState() const + { + return header()->saveState(); + } + + QAbstractItemModelEx* modelEx() const + { + QAbstractItemModelEx* pModel = qobject_cast(model()); + if (!pModel) + { + QSortFilterProxyModel* pProxyModel = qobject_cast(model()); + if(pProxyModel) + pModel = qobject_cast(pProxyModel->sourceModel()); + } + return pModel; + } + + void SetColumnHidden(int column, bool hide, bool fixed = false) + { + if (!fixed && isColumnFixed(column)) + return; // can not change fixed columns + + setColumnHidden(column, hide); + + if(QAbstractItemModelEx* pModel = modelEx()) + pModel->SetColumnEnabled(column, !hide); + + if (fixed) + setColumnFixed(column, true); + + emit ColumnChanged(column, !hide); + } + +signals: + void ColumnChanged(int column, bool visible); + void ResetColumns(); + +public slots: + void SyncColumnsWithModel() + { + if(QAbstractItemModelEx* pModel = modelEx()) + { + for (int i = 0; i < pModel->columnCount(); i++) + pModel->SetColumnEnabled(i, !isColumnHidden(i)); + } + } + + void OnResetColumns() + { + QAbstractItemModel* pModel = model(); + for (int i = 0; i < pModel->columnCount(); i++) + SetColumnHidden(i, false); + } + +private slots: + void OnMenuRequested(const QPoint &point) + { + QAbstractItemModel* pModel = model(); + + if(m_Columns.isEmpty()) + { + for(int i=0; i < pModel->columnCount(); i++) + { + QString Label = pModel->headerData(i, Qt::Horizontal, Qt::DisplayRole).toString(); + if(Label.isEmpty() || m_FixedColumns.contains(i)) + continue; + QAction* pAction = new QAction(Label, m_pMenu); + pAction->setCheckable(true); + pAction->setChecked(!isColumnHidden(i)); + connect(pAction, SIGNAL(triggered()), this, SLOT(OnMenu())); + m_pMenu->addAction(pAction); + m_Columns[pAction] = i; + } + + if (m_ColumnReset) + { + m_pMenu->addSeparator(); + QAction* pAction = m_pMenu->addAction(tr("Reset columns")); + if(m_ColumnReset == 1) + connect(pAction, SIGNAL(triggered()), this, SLOT(OnResetColumns())); + else + connect(pAction, SIGNAL(triggered()), this, SIGNAL(ResetColumns())); + } + } + + for(QMap::iterator I = m_Columns.begin(); I != m_Columns.end(); I++) + I.key()->setChecked(!isColumnHidden(I.value())); + + m_pMenu->popup(QCursor::pos()); + } + + void OnMenu() + { + QAction* pAction = (QAction*)sender(); + int Column = m_Columns.value(pAction, -1); + SetColumnHidden(Column, !pAction->isChecked()); + } + +protected: + QMenu* m_pMenu; + QMap m_Columns; + QSet m_FixedColumns; + int m_ColumnReset; +}; + +class MISCHELPERS_EXPORT QStyledItemDelegateMaxH : public QStyledItemDelegate +{ + Q_OBJECT +public: + QStyledItemDelegateMaxH(int MaxHeight, QObject *parent = 0) + : QStyledItemDelegate(parent) {m_MaxHeight = MaxHeight;} + + QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const + { + QSize size = QStyledItemDelegate::sizeHint(option, index); + size.setHeight(m_MaxHeight); + return size; + } + + int m_MaxHeight; +}; + +class MISCHELPERS_EXPORT CStyledGridItemDelegate : public QStyledItemDelegateMaxH +{ +public: + explicit CStyledGridItemDelegate(int MaxHeight, QObject * parent = 0) : CStyledGridItemDelegate(MaxHeight, false, parent) { } + explicit CStyledGridItemDelegate(int MaxHeight, QColor Color, QObject * parent = 0) : CStyledGridItemDelegate(MaxHeight, Color, false, parent) { } + explicit CStyledGridItemDelegate(int MaxHeight, bool Tree, QObject * parent = 0) : CStyledGridItemDelegate(MaxHeight, QColor(Qt::darkGray), false, parent) { } + explicit CStyledGridItemDelegate(int MaxHeight, QColor Color, bool Tree, QObject * parent = 0) : QStyledItemDelegateMaxH(MaxHeight, parent) { + m_Color = Color; + m_Tree = Tree; + m_Grid = true; + } + + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index ) const + { + QStyledItemDelegate::paint(painter, option, index); + + if (m_Grid) + { + painter->save(); + painter->setPen(m_Color); + //painter->drawRect(option.rect); + //painter->drawLine(option.rect.left(), option.rect.top(), option.rect.right(), option.rect.top()); + painter->drawLine(option.rect.right(), option.rect.top(), option.rect.right(), option.rect.bottom()); + painter->drawLine(option.rect.left() + (m_Tree && index.column() == 0 ? 24 : 0), option.rect.bottom(), option.rect.right(), option.rect.bottom()); + painter->restore(); + } + } + + bool m_Grid; + bool m_Tree; + QColor m_Color; +}; \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/TreeWidgetEx.h b/SandboxiePlus/MiscHelpers/Common/TreeWidgetEx.h new file mode 100644 index 00000000..6ee272b5 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/TreeWidgetEx.h @@ -0,0 +1,150 @@ +#pragma once + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT QTreeWidgetEx: public QTreeWidget +{ + Q_OBJECT +public: + QTreeWidgetEx(QWidget *parent = 0) : QTreeWidget(parent) + { + setUniformRowHeights(true); + + m_AutoFitMax = 0; + m_ColumnReset = 1; + + header()->setContextMenuPolicy(Qt::CustomContextMenu); + connect(header(), SIGNAL(customContextMenuRequested( const QPoint& )), this, SLOT(OnMenuRequested(const QPoint &))); + + m_pMenu = new QMenu(this); + + // Important: if something is shown/hidden we need a new size + connect(this->model(), SIGNAL(rowsInserted(const QModelIndex &, int, int)), this, SLOT(OnExpandCollapsed())); + connect(this->model(), SIGNAL(rowsRemoved(const QModelIndex &, int, int)), this, SLOT(OnExpandCollapsed())); + //connect(this->header(), SIGNAL(geometriesChanged()), this, SLOT(OnExpandCollapsed())); // fixme + connect(this, SIGNAL(expanded(const QModelIndex &)), this, SLOT(OnExpandCollapsed())); + connect(this, SIGNAL(collapsed(const QModelIndex &)), this, SLOT(OnExpandCollapsed())); + } + + static void AddSubItem(QTreeWidgetItem* pRoot, const QString& Key, const QString& Value) + { + QTreeWidgetItem* pItem = new QTreeWidgetItem(QStringList(Key)); + pItem->setText(1, Value); + pRoot->addChild(pItem); + } + + // Use this function to make the widget autosize to show all items up to this value + void setAutoFitMax(int AutoFitMax) + { + m_AutoFitMax = AutoFitMax; + } + + void setColumnReset(int iMode) + { + m_ColumnReset = iMode; + } + + QSize sizeHint() const {return m_AutoFitMax ? MySize() : QTreeWidget::sizeHint(); }; + QSize minimumSizeHint() const { return m_AutoFitMax ? MySize() : QTreeWidget::sizeHint(); }; + +signals: + void ResetColumns(); + +public slots: + void OnResetColumns() + { + for (int i = 0; i < columnCount(); i++) + setColumnHidden(i, false); + } + +private slots: + void OnMenuRequested(const QPoint &point) + { + if(m_Columns.isEmpty()) + { + QTreeWidgetItem* pHeader = headerItem(); + for(int i=0; i < columnCount(); i++) + { + QAction* pAction = new QAction(pHeader->text(i), m_pMenu); + pAction->setCheckable(true); + connect(pAction, SIGNAL(triggered()), this, SLOT(OnMenu())); + m_pMenu->addAction(pAction); + m_Columns[pAction] = i; + } + + if (m_ColumnReset) + { + m_pMenu->addSeparator(); + QAction* pAction = m_pMenu->addAction(tr("Reset columns")); + if(m_ColumnReset == 1) + connect(pAction, SIGNAL(triggered()), this, SLOT(OnResetColumns())); + else + connect(pAction, SIGNAL(triggered()), this, SIGNAL(ResetColumns())); + } + } + + for(QMap::iterator I = m_Columns.begin(); I != m_Columns.end(); I++) + I.key()->setChecked(!isColumnHidden(I.value())); + + m_pMenu->popup(QCursor::pos()); + } + + void OnMenu() + { + QAction* pAction = (QAction*)sender(); + int Column = m_Columns.value(pAction, -1); + setColumnHidden(Column, !pAction->isChecked()); + QTimer::singleShot(10, this, SLOT(OnExpandCollapsed())); + } + + void OnExpandCollapsed() + { + if (m_AutoFitMax) + updateGeometry(); + } + +protected: + QSize MySize() const + { //QSize tst(sizeHintForColumn(0) + 2 * frameWidth(), sizeHintForRow(0) + 2 * frameWidth()); + + int neededHight= 2 * frameWidth()+ this->header()->height(); + + QAbstractItemModel* m = this->model(); + + QModelIndex root = this->rootIndex(); + //if(this->rootIsDecorated()) + neededHight += recursiveHeightHint(root,m); + + if (this->horizontalScrollBar()->isVisible()) + neededHight += this->horizontalScrollBar()->height(); + + if (neededHight > m_AutoFitMax) + neededHight = m_AutoFitMax; + + QSize temp = QTreeView::sizeHint(); + temp.setHeight(neededHight); + return QSize(1,neededHight); + } + + // we need the size of all visible items -> isExpanded + // the root item is usually shown as a non-Valid index -> !i.isValid() + int recursiveHeightHint(QModelIndex i,QAbstractItemModel* m) const + { + int temp=sizeHintForIndex(i).height() + 1; + if(this->isExpanded(i) || !i.isValid()) + { + if(m->hasChildren(i)) + { + int numRows = m->rowCount(i); + for(int count =0;countindex(count,0,i),m); + } + } + return temp; + } + + QMenu* m_pMenu; + QMap m_Columns; + int m_AutoFitMax; + int m_ColumnReset; +}; diff --git a/SandboxiePlus/MiscHelpers/Common/Xml.cpp b/SandboxiePlus/MiscHelpers/Common/Xml.cpp new file mode 100644 index 00000000..010ede48 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/Xml.cpp @@ -0,0 +1,289 @@ +#include "stdafx.h" +#include "Xml.h" + +QString CXml::Serialize(const QVariant &Variant, bool bLazy) +{ + QString String; + QXmlStreamWriter xml(&String); + Serialize(Variant, xml, bLazy); + return String; +} + +QVariant CXml::Parse(const QString& String, bool bLazy) +{ + QXmlStreamReader xml(String); + return Parse(xml, bLazy); +} + +void CXml::Serialize(const QVariant& Variant, QFile* pFile) +{ + QXmlStreamWriter xml(pFile); + Serialize(Variant, xml); +} + +QVariant CXml::Parse(QFile* pFile) +{ + QXmlStreamReader xml(pFile); + return Parse(xml); +} + +void CXml::Write(const QVariant& Variant, const QString& FileName) +{ + QFile File(FileName + ".tmp"); + File.open(QFile::WriteOnly); + Serialize(Variant, &File); + + QFile::rename(FileName, FileName + ".bak"); + File.rename(FileName); + File.remove(FileName + ".bak"); +} + +QVariant CXml::Read(const QString& FileName) +{ + QFile File(FileName); + File.open(QFile::ReadOnly); + return Parse(&File); +} + +void CXml::Serialize(const QVariant& Variant, QXmlStreamWriter &xml, bool bLazy) +{ +#ifdef _DEBUG + xml.setAutoFormatting(true); +#endif + xml.writeStartDocument(); + Serialize("Variant", Variant, xml, bLazy); + xml.writeEndDocument(); +} + +QVariant CXml::Parse(QXmlStreamReader &xml, bool bLazy) +{ + QVariant Variant; + QString Temp; + Parse(Temp, Variant, xml, bLazy); + return Variant; +} + +void CXml::Serialize(const QString& Name, const QVariant& Variant, QXmlStreamWriter &xml, bool bLazy) +{ + xml.writeStartElement(Name); + xml.writeAttribute("Type", GetTypeStr(Variant.type())); + switch(Variant.type()) + { + case QVariant::Map: + { + QVariantMap Map = Variant.toMap(); + for(QVariantMap::iterator I = Map.begin(); I != Map.end(); ++I) + Serialize(I.key(), I.value(), xml, bLazy); + break; + } + case QVariant::Hash: + { + QVariantHash Hash = Variant.toHash(); + for(QVariantHash::iterator I = Hash.begin(); I != Hash.end(); ++I) + Serialize(I.key(), I.value(), xml, bLazy); + break; + } + case QVariant::List: + { + QVariantList List = Variant.toList(); + for(QVariantList::iterator I = List.begin(); I != List.end(); ++I) + Serialize("Variant", *I, xml, bLazy); + break; + } + case QVariant::StringList: + { + QStringList List = Variant.toStringList(); + for(QStringList::iterator I = List.begin(); I != List.end(); ++I) + Serialize("Variant", *I, xml, bLazy); + break; + } + case QVariant::ByteArray: + if(!bLazy) + { + xml.writeCharacters (Variant.toByteArray().toBase64()); + break; + } + default: + ASSERT(Variant.canConvert(QVariant::String)); + xml.writeCharacters(Variant.toString().toUtf8().toPercentEncoding(" :;/|,'+()")); + //xml.writeCharacters (Variant.toString().replace("\\","\\\\").replace("\r","\\r").replace("\n","\\n")); + break; + case QVariant::Invalid: + break; + } + xml.writeEndElement(); +} + +bool CXml::Parse(QString &Name, QVariant &Variant, QXmlStreamReader &xml, bool bLazy) +{ + bool bOpen = false; + QVariant::Type eType = QVariant::Invalid; + QString Text; + while (!xml.atEnd()) + { + xml.readNext(); + if (xml.error()) + break; + if (xml.isEndDocument()) + continue; + + if (xml.isStartElement()) + { + bOpen = true; + Name = xml.name().toString(); + eType = GetType(xml.attributes().value("Type").toString()); + QString Temp; + QVariant Item; + switch(eType) + { + case QVariant::Map: + { + QVariantMap Map; + while(Parse(Temp, Item, xml, bLazy)) + Map.insert(Temp, Item); + Variant = Map; + return true; + } + case QVariant::Hash: + { + QVariantHash Hash; + while(Parse(Temp, Item, xml, bLazy)) + Hash.insert(Temp, Item); + Variant = Hash; + return true; + } + case QVariant::List: + { + QVariantList List; + while(Parse(Temp, Item, xml, bLazy)) + List.append(Item); + Variant = List; + return true; + } + case QVariant::StringList: + { + QStringList List; + while(Parse(Temp, Item, xml, bLazy)) + List.append(Item.toString()); + Variant = List; + return true; + } + } + } + else if (xml.isCharacters()) + { + if(bOpen) + Text.append(xml.text().toString()); + } + else if (xml.isEndElement()) + { + if(bOpen) + { + if(eType == QVariant::ByteArray && !bLazy) + Variant.setValue(QByteArray::fromBase64(Text.toLatin1())); + else + { + /*bool bEsc = false; + for(int i = 0; i < Text.size(); i++) + { + if(bEsc) // ESC sequence handling + { + switch(Text.at(i).unicode()) + { + case L'\\': Text.replace(--i,2,"\\"); break; + case L'r': Text.replace(--i,2,"\r"); break; + case L'n': Text.replace(--i,2,"\n"); break; + default: Text.replace(--i,2,"?"); break; + } + bEsc = false; + } + else if(Text.at(i) == L'\\') + bEsc = true; + }*/ + + Variant = QString::fromUtf8(QByteArray::fromPercentEncoding(Text.toLatin1())); + if(eType) // type is optional + { + ASSERT(Variant.canConvert(eType)); + Variant.convert(eType); + } + } + return true; + } + return false; + } + } + + //ASSERT(0); // incomplete XML + return false; +} + + +/* SQVariants provides a lookup list of all known QVariant types for to/from string conversion +* +* Note: All commented out types dont have native to string converion +* If there is a need to use them a manual conversion must be implemented in CXml +*/ +struct SQVariants{ + SQVariants() + { + Map.insert("Invalid" , QVariant::Invalid); + + Map.insert("Bool" , QVariant::Bool); + Map.insert("Int" , QVariant::Int); + Map.insert("UInt" , QVariant::UInt); + Map.insert("LongLong" , QVariant::LongLong); + Map.insert("ULongLong" , QVariant::ULongLong); + Map.insert("Double" , QVariant::Double); + Map.insert("Char" , QVariant::Char); + Map.insert("Map" , QVariant::Map); // conainter type + Map.insert("List" , QVariant::List); // conainter type + Map.insert("String" , QVariant::String); + Map.insert("StringList" , QVariant::StringList); // conainter type + Map.insert("ByteArray" , QVariant::ByteArray); + //Map.insert("BitArray" , QVariant::BitArray); + Map.insert("Date" , QVariant::Date); + Map.insert("Time" , QVariant::Time); + Map.insert("DateTime" , QVariant::DateTime); + Map.insert("Url" , QVariant::Url); + /*Map.insert("Locale" , 18); + Map.insert("Rect" , 19); + Map.insert("RectF" , 20); + Map.insert("Size" , 21); + Map.insert("SizeF" , 22); + Map.insert("Line" , 23); + Map.insert("LineF" , 24); + Map.insert("Point" , 25); + Map.insert("PointF" , 26); + Map.insert("RegExp" , 27);*/ + Map.insert("Hash" , QVariant::Hash); // conainter type + + /*Map.insert("Font" , 64); + Map.insert("Pixmap" , 65); + Map.insert("Brush" , 66); + Map.insert("Color" , 67); + Map.insert("Palette" , 68); + Map.insert("Icon" , 69); + Map.insert("Image" , 70); + Map.insert("Polygon" , 71); + Map.insert("Region" , 72); + Map.insert("Bitmap" , 73); + Map.insert("Cursor" , 74); + Map.insert("SizePolicy" , 75); + Map.insert("KeySequence", 76); + Map.insert("Pen" , 77); + Map.insert("TextLength" , 78); + Map.insert("TextFormat" , 79); + Map.insert("Matrix" , 80); + Map.insert("Transform" , 81); + Map.insert("Matrix4x4" , 82); + Map.insert("Vector2D" , 83); + Map.insert("Vector3D" , 84); + Map.insert("Vector4D" , 85); + Map.insert("Quaternion" , 86);*/ + } + QMap Map; +} SQVariants; + +QString CXml::GetTypeStr(int Type) {return SQVariants.Map.key(Type, "Invalid");} +QVariant::Type CXml::GetType(QString Type) {return (QVariant::Type)SQVariants.Map.value(Type, 0);} \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/Common/Xml.h b/SandboxiePlus/MiscHelpers/Common/Xml.h new file mode 100644 index 00000000..84ab07a1 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/Common/Xml.h @@ -0,0 +1,25 @@ +#pragma once + +#include "../mischelpers_global.h" + +class MISCHELPERS_EXPORT CXml { +public: + static QString Serialize(const QVariant& Variant, bool bLazy = false); + static QVariant Parse(const QString& String, bool bLazy = false); + + static void Serialize(const QVariant& Variant, QFile* pFile); + static QVariant Parse(QFile* pFile); + + static void Write(const QVariant& Variant, const QString& FileName); + static QVariant Read(const QString& FileName); + +protected: + static void Serialize(const QVariant& Variant, QXmlStreamWriter &xml, bool bLazy = false); + static QVariant Parse(QXmlStreamReader &xml, bool bLazy = false); + + static void Serialize(const QString& Name, const QVariant& Variant, QXmlStreamWriter &xml, bool bLazy = false); + static bool Parse(QString &Name, QVariant &Variant, QXmlStreamReader &xml, bool bLazy = false); + + static QString GetTypeStr(int Type); + static QVariant::Type GetType(QString Type); +}; diff --git a/SandboxiePlus/MiscHelpers/MiscHelpers.cpp b/SandboxiePlus/MiscHelpers/MiscHelpers.cpp new file mode 100644 index 00000000..b1d8872b --- /dev/null +++ b/SandboxiePlus/MiscHelpers/MiscHelpers.cpp @@ -0,0 +1,6 @@ +#include "stdafx.h" +#include "MiscHelpers.h" + +MiscHelpers::MiscHelpers() +{ +} diff --git a/SandboxiePlus/MiscHelpers/MiscHelpers.h b/SandboxiePlus/MiscHelpers/MiscHelpers.h new file mode 100644 index 00000000..0bbb1026 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/MiscHelpers.h @@ -0,0 +1,35 @@ +#pragma once + +#include "mischelpers_global.h" + +#include ".\Common\CheckableMessageBox.h" +#include ".\Common\ComboInputDialog.h" +#include ".\Common\Common.h" +#include ".\Common\DebugHelpers.h" +#include ".\Common\ExitDialog.h" +#include ".\Common\Finder.h" +#include ".\Common\FlexError.h" +#include ".\Common\FlowLayout.h" +#include ".\Common\HistoryGraph.h" +#include ".\Common\ItemChooser.h" +#include ".\Common\KeyValueInputDialog.h" +#include ".\Common\ListItemModel.h" +#include ".\Common\MultiLineInputDialog.h" +#include ".\Common\PanelView.h" +#include ".\Common\ProgressDialog.h" +#include ".\Common\Settings.h" +#include ".\Common\SettingsWidgets.h" +#include ".\Common\SmartGridWidget.h" +#include ".\Common\SortFilterProxyModel.h" +#include ".\Common\SplitTreeView.h" +#include ".\Common\TabPanel.h" +#include ".\Common\TreeItemModel.h" +#include ".\Common\TreeViewEx.h" +#include ".\Common\TreeWidgetEx.h" +#include ".\Common\Xml.h" + +class MISCHELPERS_EXPORT MiscHelpers +{ +public: + MiscHelpers(); +}; diff --git a/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj new file mode 100644 index 00000000..06f973e1 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj @@ -0,0 +1,166 @@ + + + + + Debug + x64 + + + Release + x64 + + + + {7AB8215A-59A4-4B8B-8090-16C87A860429} + QtVS_v302 + 10.0.17763.0 + $(MSBuildProjectDirectory)\QtMsBuild + + + + DynamicLibrary + v140 + false + + + DynamicLibrary + v141 + false + + + + + + + + + + + + + + + + + + + msvc2015_64 + core;network;widgets;winextras + + + msvc2017_64_dbg + core;network;widgets;winextras + + + + + + + stdafx.h + + + true + Disabled + ProgramDatabase + MultiThreadedDebugDLL + true + Use + stdafx.h + $(IntDir)$(TargetName).pch + MISCHELPERS_LIB;%(PreprocessorDefinitions) + + + Windows + $(OutDir)\$(ProjectName).dll + true + + + + + stdafx.h + + + true + + MultiThreadedDLL + true + Use + stdafx.h + $(IntDir)$(TargetName).pch + MISCHELPERS_LIB;%(PreprocessorDefinitions) + + + Windows + $(OutDir)\$(ProjectName).dll + false + + + + + + + + + + + + + + + + + + + + + + + + + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.filters b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.filters new file mode 100644 index 00000000..4cecefc0 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.filters @@ -0,0 +1,184 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {964acff8-ecee-4dfc-939d-e2406a533100} + + + + + Source Files + + + Source Files + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + + + Header Files + + + Header Files + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + + + Header Files + + + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + Common + + + \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.user b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.user new file mode 100644 index 00000000..6e2aec7a --- /dev/null +++ b/SandboxiePlus/MiscHelpers/MiscHelpers.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SandboxiePlus/MiscHelpers/mischelpers_global.h b/SandboxiePlus/MiscHelpers/mischelpers_global.h new file mode 100644 index 00000000..9f79c367 --- /dev/null +++ b/SandboxiePlus/MiscHelpers/mischelpers_global.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifndef BUILD_STATIC +# if defined(MISCHELPERS_LIB) +# define MISCHELPERS_EXPORT Q_DECL_EXPORT +# else +# define MISCHELPERS_EXPORT Q_DECL_IMPORT +# endif +#else +# define MISCHELPERS_EXPORT +#endif diff --git a/SandboxiePlus/MiscHelpers/stdafx.cpp b/SandboxiePlus/MiscHelpers/stdafx.cpp new file mode 100644 index 00000000..a27b824d --- /dev/null +++ b/SandboxiePlus/MiscHelpers/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/SandboxiePlus/MiscHelpers/stdafx.h b/SandboxiePlus/MiscHelpers/stdafx.h new file mode 100644 index 00000000..a1e24a1c --- /dev/null +++ b/SandboxiePlus/MiscHelpers/stdafx.h @@ -0,0 +1,135 @@ +#pragma once + +#define _CRT_SECURE_NO_WARNINGS + + + +// std includes +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// other includes + +#define _T(x) L ## x + +#define STR2(X) #X +#define STR(X) STR2(X) + +#define ARRSIZE(x) (sizeof(x)/sizeof(x[0])) + +#ifndef Max +#define Max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef Min +#define Min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifdef _DEBUG +#define SAFE_MODE +#endif + +#include "Common/DebugHelpers.h" + +//#define USE_QEXTWIDGETS diff --git a/SandboxiePlus/QSbieAPI/LICENSE b/SandboxiePlus/QSbieAPI/LICENSE new file mode 100644 index 00000000..11e8067e --- /dev/null +++ b/SandboxiePlus/QSbieAPI/LICENSE @@ -0,0 +1,165 @@ + GNU LESSER GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj b/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj new file mode 100644 index 00000000..39301660 --- /dev/null +++ b/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj @@ -0,0 +1,214 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {1433EC85-BDA4-402E-BEC1-48611206A64A} + QtVS_v302 + 8.1 + $(MSBuildProjectDirectory)\QtMsBuild + + + + DynamicLibrary + v140 + false + + + DynamicLibrary + v141 + false + + + DynamicLibrary + v141 + false + + + DynamicLibrary + v141 + false + + + + + + + + + + + + + + + + + + + + + + + + + msvc2015_64 + core + + + msvc2015_64 + core + + + msvc2015_64 + core + + + msvc2015_64 + core + + + + + + + stdafx.h + + + true + Disabled + ProgramDatabase + MultiThreadedDebugDLL + true + Use + stdafx.h + $(IntDir)$(TargetName).pch + QSBIEAPI_LIB;%(PreprocessorDefinitions) + .;%(AdditionalIncludeDirectories) + + + Windows + $(OutDir)\$(ProjectName).dll + true + ntdll.lib;%(AdditionalDependencies) + + + + + stdafx.h + + + true + Disabled + ProgramDatabase + MultiThreadedDebugDLL + true + Use + stdafx.h + $(IntDir)$(TargetName).pch + QSBIEAPI_LIB;UNICODE;_UNICODE;WIN32;WIN64;%(PreprocessorDefinitions) + .;%(AdditionalIncludeDirectories) + + + Windows + $(OutDir)\$(ProjectName).dll + true + MachineX86 + /SUBSYSTEM:WINDOWS + + + + + stdafx.h + + + true + + MultiThreadedDLL + true + Use + stdafx.h + $(IntDir)$(TargetName).pch + QSBIEAPI_LIB;%(PreprocessorDefinitions) + .;%(AdditionalIncludeDirectories) + + + Windows + $(OutDir)\$(ProjectName).dll + false + ntdll.lib;%(AdditionalDependencies) + + + + + stdafx.h + + + true + + + MultiThreadedDLL + true + Use + stdafx.h + $(IntDir)$(TargetName).pch + QSBIEAPI_LIB;UNICODE;_UNICODE;WIN32;WIN64;%(PreprocessorDefinitions) + .;%(AdditionalIncludeDirectories) + + + Windows + $(OutDir)\$(ProjectName).dll + false + MachineX86 + /SUBSYSTEM:WINDOWS + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj.filters b/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj.filters new file mode 100644 index 00000000..7e236724 --- /dev/null +++ b/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj.filters @@ -0,0 +1,73 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {1cc5894c-f677-4167-9b99-bdc19b3ecd9d} + + + {a34e523b-19e3-4951-b7f2-d0bcd66886d6} + + + + + Source Files + + + SbieAPI + + + Sandboxie + + + Sandboxie + + + Sandboxie + + + + + Header Files + + + SbieAPI + + + + + Header Files + + + + + Sandboxie + + + Sandboxie + + + SbieAPI + + + Sandboxie + + + \ No newline at end of file diff --git a/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj.user b/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj.user new file mode 100644 index 00000000..6e2aec7a --- /dev/null +++ b/SandboxiePlus/QSbieAPI/QSbieAPI.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SandboxiePlus/QSbieAPI/Sandboxie/BoxedProcess.cpp b/SandboxiePlus/QSbieAPI/Sandboxie/BoxedProcess.cpp new file mode 100644 index 00000000..192903e3 --- /dev/null +++ b/SandboxiePlus/QSbieAPI/Sandboxie/BoxedProcess.cpp @@ -0,0 +1,156 @@ +/* + * + * Copyright (c) 2020, David Xanatos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#include "stdafx.h" +#include "BoxedProcess.h" +#include "SandBox.h" +#include "../SbieAPI.h" + +#include +#define WIN32_NO_STATUS +typedef long NTSTATUS; + +#include +#include "..\..\Sandboxie\common\win32_ntddk.h" +#include // For access to GetModuleFileNameEx + +//struct SBoxedProcess +//{ +//}; + +CBoxedProcess::CBoxedProcess(quint64 ProcessId, class CSandBox* pBox) +{ + m_pBox = pBox; + + //m = new SBoxedProcess; + + m_ProcessId = ProcessId; + + m_ParendPID = 0; + m_SessionId = 0; + + m_bTerminated = false; + m_bSuspended = IsSuspended(); +} + +CBoxedProcess::~CBoxedProcess() +{ + //delete m; +} + +void CBoxedProcess::UpdateProcessInfo() +{ + HANDLE ProcessHandle = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, (DWORD)m_ProcessId); + if (ProcessHandle != INVALID_HANDLE_VALUE) + { + PROCESS_BASIC_INFORMATION BasicInformation; + NTSTATUS status = NtQueryInformationProcess(ProcessHandle, ProcessBasicInformation, &BasicInformation, sizeof(PROCESS_BASIC_INFORMATION), NULL); + if (NT_SUCCESS(status)) { + m_ParendPID = (quint64)BasicInformation.InheritedFromUniqueProcessId; + } + + TCHAR filename[MAX_PATH]; + if (DWORD size = GetModuleFileNameEx(ProcessHandle, NULL, filename, MAX_PATH)) + m_ImagePath = QString::fromWCharArray(filename); + + NtClose(ProcessHandle); + } +} + +QString CBoxedProcess::GetStatusStr() const +{ + if (m_bTerminated) + return tr("Terminated"); + if (m_bSuspended) + return tr("Suspended"); + return tr("Running"); +} + +extern "C" +{ + NTSYSCALLAPI NTSTATUS NTAPI NtTerminateProcess(_In_opt_ HANDLE ProcessHandle, _In_ NTSTATUS ExitStatus); + NTSYSCALLAPI NTSTATUS NTAPI NtSuspendProcess(_In_ HANDLE ProcessHandle); + NTSYSCALLAPI NTSTATUS NTAPI NtResumeProcess(_In_ HANDLE ProcessHandle); +} + +#include + +SB_STATUS CBoxedProcess::Terminate() +{ + return m_pBox->Api()->Terminate(m_ProcessId); +} + +SB_STATUS CBoxedProcess::SetSuspend(bool bSet) +{ + HANDLE ProcessHandle = OpenProcess(PROCESS_SUSPEND_RESUME, FALSE, (DWORD)m_ProcessId); + if (ProcessHandle != INVALID_HANDLE_VALUE) + { + NTSTATUS status; + if(bSet) + status = NtSuspendProcess(ProcessHandle); + else + status = NtResumeProcess(ProcessHandle); + NtClose(ProcessHandle); + + if (!NT_SUCCESS(status)) + return SB_ERR(status); + m_bSuspended = IsSuspended(); + return SB_OK; + } + return SB_ERR(); +} + +bool CBoxedProcess::IsSuspended() const +{ + bool isSuspended = true; + + // todo: do that globaly once per sec for all boxed processes + + // Note: If the specified process is a 64-bit process and the caller is a 32-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299). + HANDLE hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); + if (hThreadSnap == INVALID_HANDLE_VALUE) + return false; + + THREADENTRY32 te32 = { 0 }; + te32.dwSize = sizeof(THREADENTRY32); + if (Thread32First(hThreadSnap, &te32)) + { + do + { + if (te32.th32OwnerProcessID != m_ProcessId) + continue; + + HANDLE hThread = OpenThread(THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID); + + ULONG SuspendCount = 0; + NTSTATUS status = NtQueryInformationThread(hThread, (THREADINFOCLASS)35/*ThreadSuspendCount*/, &SuspendCount, sizeof(ULONG), NULL); + + CloseHandle(hThread); + + if (SuspendCount == 0) + { + isSuspended = false; + break; + } + + } while (Thread32Next(hThreadSnap, &te32)); + } + + CloseHandle(hThreadSnap); + + return isSuspended; +} \ No newline at end of file diff --git a/SandboxiePlus/QSbieAPI/Sandboxie/BoxedProcess.h b/SandboxiePlus/QSbieAPI/Sandboxie/BoxedProcess.h new file mode 100644 index 00000000..c5761535 --- /dev/null +++ b/SandboxiePlus/QSbieAPI/Sandboxie/BoxedProcess.h @@ -0,0 +1,66 @@ +/* + * + * Copyright (c) 2020, David Xanatos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#pragma once +#include + + +#include "../SbieError.h" + +class CBoxedProcess : public QObject +{ + Q_OBJECT +public: + CBoxedProcess(quint64 ProcessId, class CSandBox* pBox); + virtual ~CBoxedProcess(); + + virtual void UpdateProcessInfo(); + + virtual quint64 GetProcessId() const { return m_ProcessId; } + virtual quint64 GetParendPID() const { return m_ParendPID; } + virtual QString GetProcessName() const { return m_ImageName; } + virtual QString GetFileName() const { return m_ImagePath; } + virtual QDateTime GetTimeStamp() const { return m_StartTime; } + virtual QString GetStatusStr() const; + + virtual SB_STATUS Terminate(); + virtual bool IsTerminated() const { return m_bTerminated; } + virtual void SetTerminated() { m_bTerminated = true; } + + virtual SB_STATUS SetSuspend(bool bSet); + virtual bool IsSuspended() const; + +protected: + friend class CSbieAPI; + + quint64 m_ProcessId; + quint64 m_ParendPID; + QString m_ImageName; + QString m_ImagePath; + quint32 m_SessionId; + QDateTime m_StartTime; + bool m_bTerminated; + bool m_bSuspended; + + class CSandBox* m_pBox; + +//private: +// struct SBoxedProcess* m; +}; + +typedef QSharedPointer CBoxedProcessPtr; +typedef QWeakPointer CBoxedProcessRef; \ No newline at end of file diff --git a/SandboxiePlus/QSbieAPI/Sandboxie/IniSection.cpp b/SandboxiePlus/QSbieAPI/Sandboxie/IniSection.cpp new file mode 100644 index 00000000..cf093d8c --- /dev/null +++ b/SandboxiePlus/QSbieAPI/Sandboxie/IniSection.cpp @@ -0,0 +1,103 @@ +/* + * + * Copyright (c) 2020, David Xanatos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#include "stdafx.h" +#include "IniSection.h" +#include "../SbieAPI.h" +#include "..\..\Sandboxie\core\drv\api_flags.h" + +CIniSection::CIniSection(const QString& Section, class CSbieAPI* pAPI, QObject* parent) : QObject(parent) +{ + m_Name = Section; + m_pAPI = pAPI; +} + +CIniSection::~CIniSection() +{ +} + +SB_STATUS CIniSection::SetText(const QString& Setting, const QString& Value) +{ + if (GetText(Setting) == Value) + return SB_OK; + return m_pAPI->SbieIniSet(m_Name, Setting, Value); +} + +SB_STATUS CIniSection::SetNum(const QString& Setting, int Value) +{ + return SetText(Setting, QString::number(Value)); +} + +SB_STATUS CIniSection::SetNum64(const QString& Setting, __int64 Value) +{ + return SetText(Setting, QString::number(Value)); +} + +SB_STATUS CIniSection::SetBool(const QString& Setting, bool Value) +{ + return SetText(Setting, Value ? "y" : "n"); +} + +QString CIniSection::GetText(const QString& Setting, const QString& Default) const +{ + QString Value = m_pAPI->SbieIniGet(m_Name, Setting, CONF_GET_NO_GLOBAL | CONF_GET_NO_EXPAND); + if (Value.isNull()) Value = Default; + return Value; +} + +int CIniSection::GetNum(const QString& Setting, int Default) const +{ + QString StrValue = GetText(Setting); + bool ok; + int Value = StrValue.toInt(&ok); + if (!ok) return Default; + return Value; +} + +__int64 CIniSection::GetNum64(const QString& Setting, __int64 Default) const +{ + QString StrValue = GetText(Setting); + bool ok; + __int64 Value = StrValue.toULongLong(&ok); + if (!ok) return Default; + return Value; +} + +bool CIniSection::GetBool(const QString& Setting, bool Default) const +{ + QString StrValue = GetText(Setting); + if (StrValue.compare("y", Qt::CaseInsensitive) == 0) + return true; + if (StrValue.compare("n", Qt::CaseInsensitive) == 0) + return false; + return Default; +} + +SB_STATUS CIniSection::InsertText(const QString& Setting, const QString& Value) +{ + return m_pAPI->SbieIniSet(m_Name, Setting, Value, CSbieAPI::eIniInsert); +} + +SB_STATUS CIniSection::AppendText(const QString& Setting, const QString& Value) +{ + return m_pAPI->SbieIniSet(m_Name, Setting, Value, CSbieAPI::eIniAppend); +} + +SB_STATUS CIniSection::DelValue(const QString& Setting, const QString& Value) +{ + return m_pAPI->SbieIniSet(m_Name, Setting, Value, CSbieAPI::eIniDelete); +} \ No newline at end of file diff --git a/SandboxiePlus/QSbieAPI/Sandboxie/IniSection.h b/SandboxiePlus/QSbieAPI/Sandboxie/IniSection.h new file mode 100644 index 00000000..af5d96af --- /dev/null +++ b/SandboxiePlus/QSbieAPI/Sandboxie/IniSection.h @@ -0,0 +1,32 @@ +#pragma once +#include + +#include "../SbieError.h" + +class CIniSection: public QObject +{ + Q_OBJECT +public: + CIniSection(const QString& Section, class CSbieAPI* pAPI, QObject* parent = 0); + virtual ~CIniSection(); + + virtual SB_STATUS SetText(const QString& Setting, const QString& Value); + virtual SB_STATUS SetNum(const QString& Setting, int Value); + virtual SB_STATUS SetNum64(const QString& Setting, __int64 Value); + virtual SB_STATUS SetBool(const QString& Setting, bool Value); + + virtual QString GetText(const QString& Setting, const QString& Default = QString()) const; + virtual int GetNum(const QString& Setting, int Default = 0) const; + virtual __int64 GetNum64(const QString& Setting, __int64 Default = 0) const; + virtual bool GetBool(const QString& Setting, bool Default = false) const; + + virtual SB_STATUS InsertText(const QString& Setting, const QString& Value); + virtual SB_STATUS AppendText(const QString& Setting, const QString& Value); + + virtual SB_STATUS DelValue(const QString& Setting, const QString& Value); + +protected: + + QString m_Name; + class CSbieAPI* m_pAPI; +}; \ No newline at end of file diff --git a/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.cpp b/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.cpp new file mode 100644 index 00000000..25454c7e --- /dev/null +++ b/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.cpp @@ -0,0 +1,98 @@ +/* + * + * Copyright (c) 2020, David Xanatos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#include "stdafx.h" +#include "SandBox.h" +#include "../SbieAPI.h" + +//struct SSandBox +//{ +//}; + +CSandBox::CSandBox(const QString& BoxName, class CSbieAPI* pAPI) : CIniSection(BoxName, pAPI) +{ + //m = new SSandBox; + + // when loading a sandbox that is not initialized, initialize it + int cfglvl = GetNum("ConfigLevel"); + if (cfglvl >= 7) + return; + SetNum("ConfigLevel", 7); + + if (cfglvl == 6) { + //SetDefaultTemplates7(*this); + } + else if (cfglvl >= 1) { + //UpdateTemplates(*this); + } + else + { + SetBool("AutoRecover", true); + SetBool("BlockNetworkFiles", true); + + //SetDefaultTemplates6(*this); // why 6? + + InsertText("RecoverFolder", "%Desktop%"); + //InsertText("RecoverFolder", "%Favorites%"); // obsolete + InsertText("RecoverFolder", "%Personal%"); + InsertText("RecoverFolder", "%{374DE290-123F-4565-9164-39C4925E467B}%"); // %USERPROFILE%\Downloads + + SetText("BorderColor", "#00FFFF,ttl"); // "#00FFFF,off" + } +} + +CSandBox::~CSandBox() +{ + //delete m; +} + +SB_STATUS CSandBox::RunStart(const QString& Command) +{ + return m_pAPI->RunStart(m_Name, Command); +} + +SB_STATUS CSandBox::RunCommand(const QString& Command) +{ + return m_pAPI->RunSandboxed(m_Name, Command); +} + +SB_STATUS CSandBox::TerminateAll() +{ + return m_pAPI->TerminateAll(m_Name); +} + +SB_STATUS CSandBox::CleanBox() +{ + SB_STATUS Status = m_pAPI->TerminateAll(m_Name); + if (Status.IsError()) + return Status; + return m_pAPI->CleanBox(m_Name); +} + +SB_STATUS CSandBox::RenameBox(const QString& NewName) +{ + if (QDir(m_pAPI->Nt2DosPath(m_FilePath)).exists()) + return SB_ERR("A sandbox must be emptied before it can be renamed."); + return m_pAPI->RenameBox(m_Name, NewName); +} + +SB_STATUS CSandBox::RemoveBox() +{ + if (QDir(m_pAPI->Nt2DosPath(m_FilePath)).exists()) + return SB_ERR("A sandbox must be emptied before it can be deleted."); + return m_pAPI->RemoveBox(m_Name); +} diff --git a/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.h b/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.h new file mode 100644 index 00000000..0dc6bcbd --- /dev/null +++ b/SandboxiePlus/QSbieAPI/Sandboxie/SandBox.h @@ -0,0 +1,59 @@ +/* + * + * Copyright (c) 2020, David Xanatos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#pragma once +#include + +#include "BoxedProcess.h" +#include "IniSection.h" + +class CSandBox : public CIniSection +{ + Q_OBJECT +public: + CSandBox(const QString& BoxName, class CSbieAPI* pAPI); + virtual ~CSandBox(); + + virtual QString GetName() const { return m_Name; } + + virtual QMap GetProcessList() const { return m_ProcessList; } + + virtual SB_STATUS RunStart(const QString& Command); + virtual SB_STATUS RunCommand(const QString& Command); + virtual SB_STATUS TerminateAll(); + + virtual SB_STATUS CleanBox(); + virtual SB_STATUS RenameBox(const QString& NewName); + virtual SB_STATUS RemoveBox(); + + class CSbieAPI* Api() { return m_pAPI; } + +protected: + friend class CSbieAPI; + + QString m_FilePath; + QString m_RegPath; + QString m_IpcPath; + + QMap m_ProcessList; + +//private: +// struct SSandBox* m; +}; + +typedef QSharedPointer CSandBoxPtr; +typedef QWeakPointer CSandBoxRef; \ No newline at end of file diff --git a/SandboxiePlus/QSbieAPI/SbieAPI.cpp b/SandboxiePlus/QSbieAPI/SbieAPI.cpp new file mode 100644 index 00000000..cd95e0e0 --- /dev/null +++ b/SandboxiePlus/QSbieAPI/SbieAPI.cpp @@ -0,0 +1,1268 @@ +/* + * + * Copyright (c) 2020, David Xanatos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#include "stdafx.h" +#include +#include "SbieAPI.h" + +#include +#define WIN32_NO_STATUS +typedef long NTSTATUS; + +#include +#include "..\..\Sandboxie\common\win32_ntddk.h" + +#define SANDBOXIE L"Sandboxie" +#include "..\..\Sandboxie\core\drv\api_defs.h" + +#define REQUEST_LEN 4096 +#define CONF_LINE_LEN 2000 + +#include "..\..\Sandboxie\core\svc\msgids.h" +#include "..\..\Sandboxie\core\svc\ProcessWire.h" +#include "..\..\Sandboxie\core\svc\sbieiniwire.h" + +#define SBIESVC_PORT L"\\RPC Control\\SbieSvcPort" + +#define SBIESTART_EXE L"Start.exe" + +#define SBIEMSG_DLL L"SbieMsg.dll" + + +struct SSbieAPI +{ + SSbieAPI() + { + SbieApiHandle = INVALID_HANDLE_VALUE; + + PortHandle = NULL; + MaxDataLen = 0; + SizeofPortMsg = 0; + CallSeqNumber = 0; + + if (!ProcessIdToSessionId(GetCurrentProcessId(), &sessionId)) + sessionId = 0; + + lastMessageNum = 0; + lastRecordNum = 0; + + SbieMsgDll = NULL; + } + ~SSbieAPI() { + } + + NTSTATUS IoControl(ULONG64 *parms) + { + IO_STATUS_BLOCK IoStatusBlock; + return NtDeviceIoControlFile(SbieApiHandle, NULL, NULL, NULL, &IoStatusBlock, API_SBIEDRV_CTLCODE, parms, sizeof(ULONG64) * API_NUM_ARGS, NULL, 0); + } + + + HANDLE SbieApiHandle = INVALID_HANDLE_VALUE; + + HANDLE PortHandle; + ULONG MaxDataLen; + ULONG SizeofPortMsg; + ULONG CallSeqNumber; + + QString Password; // todo: suppor lcoked configurations + + ULONG sessionId; + + ULONG lastMessageNum; + ULONG lastRecordNum; + + HMODULE SbieMsgDll; +}; + +quint64 FILETIME2ms(quint64 fileTime) +{ + if (fileTime < 116444736000000000ULL) + return 0; + return (fileTime - 116444736000000000ULL) / 10000ULL; +} + +time_t FILETIME2time(quint64 fileTime) +{ + return FILETIME2ms(fileTime) / 1000ULL; +} + + +CSbieAPI::CSbieAPI(QObject* parent) : QThread(parent) +{ + m = new SSbieAPI(); + + UNICODE_STRING uni; + RtlInitUnicodeString(&uni, API_DEVICE_NAME); + + OBJECT_ATTRIBUTES objattrs; + InitializeObjectAttributes(&objattrs, &uni, OBJ_CASE_INSENSITIVE, NULL, NULL); + + IO_STATUS_BLOCK IoStatusBlock; + NTSTATUS status = NtOpenFile(&m->SbieApiHandle, FILE_GENERIC_READ, &objattrs, &IoStatusBlock, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0); + + if (status == STATUS_OBJECT_NAME_NOT_FOUND || status == STATUS_NO_SUCH_DEVICE) + status = STATUS_SERVER_DISABLED; + + if (status != STATUS_SUCCESS) { + m->SbieApiHandle = INVALID_HANDLE_VALUE; + return; + } + + UpdateDriveLetters(); + + m_SbiePath = GetSbieHome(); + + m->SbieMsgDll = LoadLibraryEx((m_SbiePath.toStdWString() + L"\\" SBIEMSG_DLL).c_str(), NULL, LOAD_LIBRARY_AS_DATAFILE); + + QProcessEnvironment env = QProcessEnvironment::systemEnvironment(); + QString WinDir = env.value("windir"); + m_IniPath = WinDir + "\\Sandboxie.ini"; + + ReloadBoxes(); + + m_bTerminate = false; + start(); +} + +CSbieAPI::~CSbieAPI() +{ + m_bTerminate = true; + if(!wait(10*1000)) + terminate(); + + if (m->SbieApiHandle != INVALID_HANDLE_VALUE) + NtClose(m->SbieApiHandle); + + if (m->SbieMsgDll) + FreeLibrary(m->SbieMsgDll); + + delete m; +} + +bool CSbieAPI::IsValid() const +{ + return m->SbieApiHandle != INVALID_HANDLE_VALUE; +} + +void CSbieAPI::run() +{ + while (!m_bTerminate) + { + int Done = 0; + + if (GetLog()) // this emits sbie message events if there are any + Done++; + + if (GetMonitor()) { + Done++; + //QMetaObject::invokeMethod(this, "OnMonitorEntry", Qt::AutoConnection, Q_ARG(quint64, ProcessId), Q_ARG(quint32, Type), Q_ARG(const QString&, Value)); + } + + if (Done == 0) + QThread::msleep(10); + } +} + +/*void CSbieAPI::OnMonitorEntry(quint64 ProcessId, quint32 Type, const QString& Value) +{ + QMap::iterator I = m_BoxedProxesses.find(ProcessId); + if (I == m_BoxedProxesses.end()) + { + UpdateProcesses(true); + I = m_BoxedProxesses.find(ProcessId); + } + if (I == m_BoxedProxesses.end()) + return; + + I.value()->AddResourceEntry(Type, Value); +}*/ + +QString CSbieAPI::GetVersion() +{ + WCHAR out_version[16]; + + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_GET_VERSION_ARGS *args = (API_GET_VERSION_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_GET_VERSION; + args->string.val = out_version; + + if (! NT_SUCCESS(m->IoControl(parms))) + wcscpy(out_version, L"unknown"); + + return QString::fromWCharArray(out_version); +} + +SB_STATUS CSbieAPI::TakeOver() +{ + __declspec(align(8)) ULONG64 ResultValue; + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_SESSION_LEADER_ARGS *args = (API_SESSION_LEADER_ARGS *)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_SESSION_LEADER; + args->token_handle.val64 = 0; + args->process_id.val64 = 0; + + NTSTATUS status = m->IoControl(parms); + if (!NT_SUCCESS(status)) + return SB_ERR(status); + return SB_OK; +} + +void CSbieAPI::UpdateDriveLetters() +{ + m_DriveLetters.clear(); + + // \Device\HarddiskVolume10 + // \Device\HarddiskVolume1 + + // \Device\LanmanRedirector\server\share\file.txt + // \Device\LanmanRedirector\;Q:0000000000001234\server\share + + wchar_t lpTargetPath [MAX_PATH]; + for (wchar_t ltr = L'A'; ltr <= L'Z'; ltr++) + { + wchar_t drv[] = { ltr, L':', '\0' }; + uint size = QueryDosDevice(drv, lpTargetPath, MAX_PATH); + if (size > 0) + { + QString Key = QString::fromWCharArray(lpTargetPath); + QStringList Chunks = Key.split("\\"); + if (Chunks.count() >= 5 && Chunks[2].compare("LanmanRedirector", Qt::CaseInsensitive) == 0) { + Chunks.removeAt(3); + Key = Chunks.join("\\"); + } + Key.append("\\"); + m_DriveLetters.insert(Key, QString::fromWCharArray(drv) + "\\"); + } + } +} + +QString CSbieAPI::Nt2DosPath(QString NtPath) const +{ + for (QMap::const_iterator I = m_DriveLetters.begin(); I != m_DriveLetters.end(); ++I) + { + const QString& Key = I.key(); + if (Key.compare(NtPath.left(Key.length()), Qt::CaseInsensitive) == 0) + return NtPath.replace(0, Key.length(), I.value()); + } + return NtPath; +} + +QString CSbieAPI::GetSbieHome() const +{ + WCHAR DosPath[MAX_PATH]; + ULONG DosPathMaxLen = MAX_PATH; + + __declspec(align(8)) UNICODE_STRING64 dos_path_uni = { 0, (USHORT)(DosPathMaxLen * sizeof(WCHAR)), (ULONG64)DosPath }; + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_GET_HOME_PATH_ARGS *args = (API_GET_HOME_PATH_ARGS *)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_GET_HOME_PATH; + args->nt_path.val64 = NULL; + if (DosPath) + args->dos_path.val64 = (ULONG64)(ULONG_PTR)&dos_path_uni; + + NTSTATUS status = m->IoControl(parms); + if (!NT_SUCCESS(status)) + return QString(); + return QString::fromWCharArray(DosPath); +} + +SB_STATUS CSbieAPI::RunStart(const QString& BoxName, const QString& Command) +{ + if (m_SbiePath.isEmpty()) + return SB_ERR(tr("Can't find Sandboxie instal path.")); + + QStringList Arguments; + Arguments.append("/box:" + BoxName); + Arguments.append(Command); + QProcess::startDetached(m_SbiePath + "//" + QString::fromWCharArray(SBIESTART_EXE), Arguments); + + return SB_OK; +} + +SB_STATUS CSbieAPI::ReloadBoxes() +{ + QMap OldSandBoxes = m_SandBoxes; + + for (int i = 0;;i++) + { + QString BoxName = SbieIniGet(QString(), QString(), (i | CONF_GET_NO_EXPAND)); + if (BoxName.isNull()) + break; + if (!IsBoxEnabled(BoxName)) + continue; + + CSandBoxPtr pBox = OldSandBoxes.take(BoxName); + if (!pBox) + { + pBox = CSandBoxPtr(new CSandBox(BoxName, this)); + m_SandBoxes.insert(BoxName, pBox); + + SetBoxPaths(pBox); + } + + // todo: + } + + foreach(const QString& BoxName, OldSandBoxes.keys()) + m_SandBoxes.remove(BoxName); + + return SB_OK; +} + +bool CSbieAPI__IsWow64() +{ + static bool IsWow64 = false; +#ifndef _WIN64 + static bool init = false; + if (!init) + { + ULONG_PTR wow64; + if (NT_SUCCESS(NtQueryInformationProcess(NtCurrentProcess(), ProcessWow64Information, &wow64, sizeof(ULONG_PTR), NULL))) { + IsWow64 = !!wow64; + } + init = true; + } +#endif + return IsWow64; +} + +SB_STATUS CSbieAPI__ConnectPort(SSbieAPI* m) +{ + if (m->PortHandle) + return SB_OK; + + SECURITY_QUALITY_OF_SERVICE QoS; + QoS.Length = sizeof(SECURITY_QUALITY_OF_SERVICE); + QoS.ImpersonationLevel = SecurityImpersonation; + QoS.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; + QoS.EffectiveOnly = TRUE; + + UNICODE_STRING PortName; + RtlInitUnicodeString(&PortName, SBIESVC_PORT); + NTSTATUS status = NtConnectPort(&m->PortHandle, &PortName, &QoS, NULL, NULL, &m->MaxDataLen, NULL, NULL); + if (!NT_SUCCESS(status)) + return SB_ERR(status); // 2203 + + // Function associate PortHandle with thread, and sends LPC_TERMINATION_MESSAGE to specified port immediatelly after call NtTerminateThread. + NtRegisterThreadTerminatePort(m->PortHandle); + + m->SizeofPortMsg = sizeof(PORT_MESSAGE); + if (CSbieAPI__IsWow64()) + m->SizeofPortMsg += sizeof(ULONG) * 4; + m->MaxDataLen -= m->SizeofPortMsg; + + return SB_OK; +} + +SB_STATUS CSbieAPI__CallServer(SSbieAPI* m, MSG_HEADER* req, MSG_HEADER* &rpl) +{ + if (!m->PortHandle) { + SB_STATUS Status = CSbieAPI__ConnectPort(m); + if (!Status) + return Status; + } + + UCHAR RequestBuff[MAX_PORTMSG_LENGTH]; + PORT_MESSAGE* ReqHeader = (PORT_MESSAGE*)RequestBuff; + UCHAR* ReqData = RequestBuff + m->SizeofPortMsg; + + UCHAR ResponseBuff[MAX_PORTMSG_LENGTH]; + PORT_MESSAGE* ResHeader = (PORT_MESSAGE*)ResponseBuff; + UCHAR* ResData = ResponseBuff + m->SizeofPortMsg; + + UCHAR CurSeqNumber = (UCHAR)m->CallSeqNumber++; + + // Send the request in chunks + UCHAR* Buffer = (UCHAR*)req; + ULONG BuffLen = req->length; + while (BuffLen) + { + ULONG send_len = BuffLen > m->MaxDataLen ? m->MaxDataLen : BuffLen; + + // set header + memset(ReqHeader, 0, m->SizeofPortMsg); + ReqHeader->u1.s1.DataLength = (USHORT)send_len; + ReqHeader->u1.s1.TotalLength = (USHORT)(m->SizeofPortMsg + send_len); + + // set req data + memcpy(ReqData, Buffer, send_len); + + // use highest byte of the length as sequence field + if (Buffer == (UCHAR*)req) + ReqData[3] = CurSeqNumber; + + // advance position + Buffer += send_len; + BuffLen -= send_len; + + NTSTATUS status = NtRequestWaitReplyPort(m->PortHandle, (PORT_MESSAGE*)RequestBuff, (PORT_MESSAGE*)ResponseBuff); + + if (!NT_SUCCESS(status)) + { + NtClose(m->PortHandle); + m->PortHandle = NULL; + return SB_ERR(CSbieAPI::tr("request %1").arg(status, 8, 16), status); // 2203 + } + + if (BuffLen && ResHeader->u1.s1.DataLength) + return SB_ERR(CSbieAPI::tr("early reply")); // 2203 + } + + // the last call to NtRequestWaitReplyPort should return the first chunk of the reply + if (ResHeader->u1.s1.DataLength >= sizeof(MSG_HEADER)) + { + if (ResData[3] != CurSeqNumber) + return SB_ERR(CSbieAPI::tr("mismatched reply")); // 2203 + + // clear highest byte of the size filed + ResData[3] = 0; + BuffLen = ((MSG_HEADER*)ResData)->length; + } + else + BuffLen = 0; + if (BuffLen == 0) + return SB_ERR(CSbieAPI::tr("null reply (msg %1 len %2)").arg(req->msgid, 8, 16).arg(req->length)); // 2203 + + // read remining chunks + rpl = (MSG_HEADER*)malloc(BuffLen); + Buffer = (UCHAR*)rpl; + for (;;) + { + NTSTATUS status; + if (ResHeader->u1.s1.DataLength > BuffLen) + status = STATUS_PORT_MESSAGE_TOO_LONG; + else + { + // get data + memcpy(Buffer, ResData, ResHeader->u1.s1.DataLength); + + // adcance position + Buffer += ResHeader->u1.s1.DataLength; + BuffLen -= ResHeader->u1.s1.DataLength; + + // are we done yet? + if (!BuffLen) + break; + + // set header + memset(ReqHeader, 0, m->SizeofPortMsg); + ReqHeader->u1.s1.TotalLength = (USHORT)m->SizeofPortMsg; + + status = NtRequestWaitReplyPort(m->PortHandle, (PORT_MESSAGE*)RequestBuff, (PORT_MESSAGE*)ResponseBuff); + } + + if (!NT_SUCCESS(status)) + { + free(rpl); + rpl = NULL; + + NtClose(m->PortHandle); + m->PortHandle = NULL; + return SB_ERR(CSbieAPI::tr("reply %1").arg(status, 8, 16), status); // 2203 + } + } + + return SB_OK; +} + +SB_STATUS CSbieAPI__SbieIniSet(SSbieAPI* m, void *RequestBuf, WCHAR *pPasswordWithinRequestBuf, const QString& SectionName, const QString& SettingName) +{ + m->Password.toWCharArray(pPasswordWithinRequestBuf); // fix-me: potential overflow + + MSG_HEADER *rpl = NULL; + SB_STATUS Status = CSbieAPI__CallServer(m, (MSG_HEADER *)RequestBuf, rpl); + SecureZeroMemory(pPasswordWithinRequestBuf, sizeof(WCHAR) * 64); + if (!Status || !rpl) + return Status; + ULONG status = rpl->status; + free(rpl); + if (status == 0) + return SB_OK; + if (status == STATUS_LOGON_NOT_GRANTED || status == STATUS_WRONG_PASSWORD) + return SB_ERR(CSbieAPI::tr("You are not authorized to update configuration in section '%1'").arg(SectionName), status); + return SB_ERR(CSbieAPI::tr("Failed to set configuration setting %1 in section %2: %3").arg(SettingName).arg(SectionName).arg(status, 8, 16), status); +} + +SB_STATUS CSbieAPI::SbieIniSet(const QString& Section, const QString& Setting, const QString& Value, ESetMode Mode) +{ + if (Section.isEmpty()) + return SB_ERR(); + + ULONG msgid = 0; + switch (Mode) + { + case eIniUpdate: msgid = MSGID_SBIE_INI_SET_SETTING; break; + case eIniAppend: msgid = MSGID_SBIE_INI_ADD_SETTING; break; + case eIniInsert: msgid = MSGID_SBIE_INI_INS_SETTING; break; + case eIniDelete: msgid = MSGID_SBIE_INI_DEL_SETTING; break; + default: + return SB_ERR(); + } + + SBIE_INI_SETTING_REQ *req = (SBIE_INI_SETTING_REQ *)malloc(REQUEST_LEN); + + Section.toWCharArray(req->section); // fix-me: potential overflow + req->section[Section.length()] = L'\0'; + Setting.toWCharArray(req->setting); // fix-me: potential overflow + req->setting[Setting.length()] = L'\0'; + Value.toWCharArray(req->value); // fix-me: potential overflow + req->value[Value.length()] = L'\0'; + req->value_len = Value.length(); + req->h.msgid = msgid; + req->h.length = sizeof(SBIE_INI_SETTING_REQ) + req->value_len * sizeof(WCHAR); + + SB_STATUS Status = CSbieAPI__SbieIniSet(m, req, req->password, Section, Setting); + if (!Status) + emit LogMessage(tr("Failed to communicate with Sandboxie Service: %1").arg(Status.GetText())); + free(req); + return Status; +} + +QString CSbieAPI::SbieIniGet(const QString& Section, const QString& Setting, quint32 Index, qint32* ErrCode) +{ + wstring section = Section.toStdWString(); + wstring setting = Setting.toStdWString(); + + WCHAR out_buffer[CONF_LINE_LEN] = { 0 }; + + __declspec(align(8)) UNICODE_STRING64 Output = { 0, CONF_LINE_LEN - 4 , (ULONG64)out_buffer }; + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + + memset(parms, 0, sizeof(parms)); + parms[0] = API_QUERY_CONF; + parms[1] = (ULONG64)section.c_str(); + parms[2] = (ULONG64)setting.c_str(); + parms[3] = (ULONG64)&Index; + parms[4] = (ULONG64)&Output; + + NTSTATUS status = m->IoControl(parms); + if (ErrCode) + *ErrCode = status; + if (!NT_SUCCESS(status)) + return QString(); + return QString::fromWCharArray(out_buffer); +} + +SB_STATUS CSbieAPI::CreateBox(const QString& BoxName) +{ + return SbieIniSet(BoxName, "Enabled", "y"); +} + +SB_STATUS CSbieAPI::CleanBox(const QString& BoxName) +{ + // ToDo-later: do that manually + return RunStart(BoxName, "delete_sandbox"); +} + +SB_STATUS CSbieAPI::RenameBox(const QString& OldName, const QString& NewName, bool deleteOld) // Note: deleteOld is used when duplicating a box +{ + if (OldName.isEmpty() || NewName.isEmpty()) + return SB_ERR(); + bool SameName = (bool)(NewName.compare(OldName, Qt::CaseInsensitive) == 0); + + qint32 status = STATUS_SUCCESS; + + // Get all Settigns + QList> Settings; + for (int setting_index = 0; ; setting_index++) + { + QString setting_name = SbieIniGet(OldName, NULL, setting_index | CONF_GET_NO_TEMPLS | CONF_GET_NO_EXPAND, &status); + if (status == STATUS_RESOURCE_NAME_NOT_FOUND) { + status = STATUS_SUCCESS; + break; + } + if (status != STATUS_SUCCESS) + break; + + for (int value_index = 0; ; value_index++) + { + QString setting_value = SbieIniGet(OldName, setting_name, value_index | CONF_GET_NO_GLOBAL | CONF_GET_NO_TEMPLS | CONF_GET_NO_EXPAND, &status); + if (status == STATUS_RESOURCE_NAME_NOT_FOUND) { + status = STATUS_SUCCESS; + break; + } + if (status != STATUS_SUCCESS) + break; + + Settings.append(qMakePair(setting_name, setting_value)); + } + + if (status != STATUS_SUCCESS) + break; + } + + if (status != STATUS_SUCCESS) + return SB_ERR(CSbieAPI::tr("Failed to copy configuration from sandbox %1: %2").arg(OldName).arg(status, 8, 16), status); + + // check if such a box already exists + if (!SameName) + { + SbieIniGet(NewName, NULL, CONF_GET_NO_EXPAND, &status); + if (status != STATUS_RESOURCE_NAME_NOT_FOUND) + return SB_ERR(CSbieAPI::tr("A sandbox of the name %1 already exists").arg(NewName)); + } + + // if the name is the same we first delete than write, + // else we first write and than delete, fro safety reasons + if (deleteOld && SameName) + goto do_delete; + +do_write: + // Apply all Settigns + for (QList>::iterator I = Settings.begin(); I != Settings.end(); ++I) + { + SB_STATUS Status = SbieIniSet(NewName, I->first, I->second); + if (Status.IsError()) + return Status; + } + +do_delete: + // Selete ini section + if (deleteOld) + { + SB_STATUS Status = SbieIniSet(OldName, "*", ""); + if (Status.IsError()) + return SB_ERR(CSbieAPI::tr("Failed to delete sandbox %1: %2").arg(OldName).arg(Status.GetStatus(), 8, 16), Status.GetStatus()); + deleteOld = false; + + if (SameName) + goto do_write; + } + + return SB_OK; +} + +SB_STATUS CSbieAPI::RemoveBox(const QString& BoxName) +{ + // Note: SandBox must be emptied at this point + return SbieIniSet(BoxName, "*", ""); +} + +SB_STATUS CSbieAPI::UpdateProcesses(bool bKeep) +{ + foreach(const CSandBoxPtr& pBox, m_SandBoxes) + UpdateProcesses(bKeep, pBox); + return SB_OK; +} + +SB_STATUS CSbieAPI::UpdateProcesses(bool bKeep, const CSandBoxPtr& pBox) +{ + wstring box_name = pBox->GetName().toStdWString(); // WCHAR [34] + BOOLEAN all_sessions = TRUE; + ULONG which_session = 0; // -1 for current session + ULONG boxed_pids[512]; // ULONG [512] + + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + + memset(parms, 0, sizeof(parms)); + parms[0] = API_ENUM_PROCESSES; + parms[1] = (ULONG64)boxed_pids; + parms[2] = (ULONG64)box_name.c_str(); + parms[3] = (ULONG64)all_sessions; + parms[4] = (ULONG64)which_session; + + NTSTATUS status = m->IoControl(parms); + if (!NT_SUCCESS(status)) + return SB_ERR(status); + + QMap OldProcessList = pBox->m_ProcessList; + + for (int i=1; i < boxed_pids[0] + 1; i++) + { + quint64 ProcessId = boxed_pids[i]; + + CBoxedProcessPtr pProcess = OldProcessList.take(ProcessId); + if (!pProcess) + { + pProcess = CBoxedProcessPtr(new CBoxedProcess(ProcessId, pBox.data())); + //pProcess->m_pBox = pBox; + pBox->m_ProcessList.insert(ProcessId, pProcess); + m_BoxedProxesses.insert(ProcessId, pProcess); + + SetProcessInfo(pProcess); + pProcess->UpdateProcessInfo(); + } + + // todo: + } + + + foreach(const CBoxedProcessPtr& pProcess, OldProcessList) { + if (!bKeep) { + pBox->m_ProcessList.remove(pProcess->m_ProcessId); + m_BoxedProxesses.remove(pProcess->m_ProcessId); + } + else if (!pProcess->IsTerminated()) + pProcess->SetTerminated(); + } + + return SB_OK; +} + +SB_STATUS CSbieAPI__QueryBoxPath(SSbieAPI* m, const WCHAR *box_name, WCHAR *out_file_path, WCHAR *out_key_path, WCHAR *out_ipc_path, + ULONG *inout_file_path_len, ULONG *inout_key_path_len, ULONG *inout_ipc_path_len) +{ + __declspec(align(8)) UNICODE_STRING64 FilePath = { 0, (USHORT)*inout_file_path_len, (ULONG64)out_file_path }; + __declspec(align(8)) UNICODE_STRING64 KeyPath = { 0, (USHORT)*inout_key_path_len, (ULONG64)out_key_path }; + __declspec(align(8)) UNICODE_STRING64 IpcPath = { 0, (USHORT)*inout_ipc_path_len, (ULONG64)out_ipc_path }; + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_QUERY_BOX_PATH_ARGS *args = (API_QUERY_BOX_PATH_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_QUERY_BOX_PATH; + args->box_name.val64 = (ULONG64)(ULONG_PTR)box_name; + + args->file_path_len.val = inout_file_path_len; + if (out_file_path) + args->file_path.val64 = (ULONG64)(ULONG_PTR)&FilePath; + + args->key_path_len.val = inout_key_path_len; + if (out_key_path) + args->key_path.val64 = (ULONG64)(ULONG_PTR)&KeyPath; + + args->ipc_path_len.val = inout_ipc_path_len; + if (out_ipc_path) + args->ipc_path.val64 = (ULONG64)(ULONG_PTR)&IpcPath; + + NTSTATUS status = m->IoControl(parms); + if (!NT_SUCCESS(status)) + return SB_ERR(status); + return SB_OK; +} + +SB_STATUS CSbieAPI::SetBoxPaths(const CSandBoxPtr& pSandBox) +{ + wstring boxName = pSandBox->GetName().toStdWString(); + + ULONG filePathLength = 0; + ULONG keyPathLength = 0; + ULONG ipcPathLength = 0; + SB_STATUS Status = CSbieAPI__QueryBoxPath(m, boxName.c_str(), NULL, NULL, NULL, &filePathLength, &keyPathLength, &ipcPathLength); + if (!Status) + return Status; + + wstring FileRoot(filePathLength + 1, '0'); + wstring KeyRoot(filePathLength + 1, '0'); + wstring IpcRoot(filePathLength + 1, '0'); + Status = CSbieAPI__QueryBoxPath(m, boxName.c_str(), (WCHAR*)FileRoot.data(), (WCHAR*)KeyRoot.data(), (WCHAR*)IpcRoot.data(), &filePathLength, &keyPathLength, &ipcPathLength); + if (!Status) + return Status; + + pSandBox->m_FilePath = QString::fromStdWString(FileRoot); + pSandBox->m_RegPath = QString::fromStdWString(KeyRoot); + pSandBox->m_IpcPath = QString::fromStdWString(IpcRoot); + return SB_OK; +} + +SB_STATUS CSbieAPI::SetProcessInfo(const CBoxedProcessPtr& pProcess) +{ + //WCHAR box_name_wchar34[34] = { 0 }; + WCHAR image_name[MAX_PATH]; + //WCHAR sid[96]; + ULONG session_id; + ULONG64 create_time; + + //__declspec(align(8)) UNICODE_STRING64 BoxName = { 0, sizeof(box_name_wchar34) , (ULONG64)box_name_wchar34 }; + __declspec(align(8)) UNICODE_STRING64 ImageName = { 0, sizeof(image_name), (ULONG64)image_name }; + //__declspec(align(8)) UNICODE_STRING64 SidString = { 0, sizeof(sid), (ULONG64)sid }; + __declspec(align(8)) UNICODE_STRING64 SidString; + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_QUERY_PROCESS_ARGS *args = (API_QUERY_PROCESS_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_QUERY_PROCESS; + args->process_id.val64 = (ULONG64)pProcess->m_ProcessId; + //args->box_name.val64 = (ULONG64)&BoxName; + args->image_name.val64 = (ULONG64)&ImageName; + //args->sid_string.val64 = (ULONG64)&SidString; + args->session_id.val64 = (ULONG64)&session_id; + args->create_time.val64 = (ULONG64)&create_time; + + NTSTATUS status = m->IoControl(parms); + if(!NT_SUCCESS(status)) + return SB_ERR(status); + + pProcess->m_ImageName = QString::fromWCharArray(image_name, ImageName.Length/sizeof(WCHAR)); + pProcess->m_SessionId = session_id; + pProcess->m_StartTime = QDateTime::fromMSecsSinceEpoch(FILETIME2ms(create_time)); + // sid todo + + return SB_OK; +} + +SB_STATUS CSbieAPI::TerminateAll(const QString& BoxName) +{ + PROCESS_KILL_ALL_REQ req; + req.h.length = sizeof(PROCESS_KILL_ALL_REQ); + req.h.msgid = MSGID_PROCESS_KILL_ALL; + req.session_id = -1; + BoxName.toWCharArray(req.boxname); // fix-me: potential overflow + req.boxname[BoxName.size()] = L'\0'; + + MSG_HEADER *rpl = NULL; + SB_STATUS Status = CSbieAPI__CallServer(m, &req.h, rpl); + if (!Status) + emit LogMessage(tr("Failed to communicate with Sandboxie Service: %1").arg(Status.GetText())); + if (!Status || !rpl) + return Status; + if(rpl->status != 0) + Status = SB_ERR(rpl->status); + free(rpl); + return Status; +} + +SB_STATUS CSbieAPI::TerminateAll() +{ + foreach(const CSandBoxPtr& pBox, m_SandBoxes) + pBox->TerminateAll(); + return SB_OK; +} + +SB_STATUS CSbieAPI::Terminate(quint64 ProcessId) +{ + PROCESS_KILL_ONE_REQ req; + req.h.length = sizeof(PROCESS_KILL_ONE_REQ); + req.h.msgid = MSGID_PROCESS_KILL_ONE; + req.pid = ProcessId; + + MSG_HEADER *rpl = NULL; + SB_STATUS Status = CSbieAPI__CallServer(m, &req.h, rpl); + if (!Status) + emit LogMessage(tr("Failed to communicate with Sandboxie Service: %1").arg(Status.GetText())); + if (!Status || !rpl) + return Status; + if (rpl->status != 0) + Status = SB_ERR(rpl->status); + free(rpl); + return Status; +} + +LONG CSbieAPI__OpenDeviceMap(SSbieAPI* m, HANDLE *DirectoryHandle) +{ + __declspec(align(8)) ULONG64 ResultHandle; + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_OPEN_DEVICE_MAP_ARGS *args = (API_OPEN_DEVICE_MAP_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_OPEN_DEVICE_MAP; + args->handle.val64 = (ULONG64)&ResultHandle; + + NTSTATUS status = m->IoControl(parms); + if (!NT_SUCCESS(status)) + ResultHandle = 0; + if (DirectoryHandle) + *DirectoryHandle = (HANDLE*)ResultHandle; + return status; +} + +QString CSbieAPI::GetDeviceMap() +{ + UNICODE_STRING objname; + OBJECT_ATTRIBUTES objattrs; + InitializeObjectAttributes(&objattrs, &objname, OBJ_CASE_INSENSITIVE, NULL, NULL); + RtlInitUnicodeString(&objname, L"\\??"); + + HANDLE handle; + NTSTATUS status = NtOpenDirectoryObject(&handle, DIRECTORY_QUERY, &objattrs); + if (status == STATUS_ACCESS_DENIED) // if we are missign admin rights, lets the driver do it + status = CSbieAPI__OpenDeviceMap(m, &handle); + + if (!NT_SUCCESS(status)) { + emit LogMessage(tr("Cannot open device map: %1").arg(status, 8, 16)); + } + else + { + WCHAR dirname[128]; + ULONG length = sizeof(dirname); + status = NtQueryObject(handle, ObjectNameInformation, dirname, length, &length); + + NtClose(handle); + + if (!NT_SUCCESS(status)) { + emit LogMessage(tr("Cannot query device map: %1").arg(status, 8, 16)); + } + else + { + UNICODE_STRING* ustr = &((OBJECT_NAME_INFORMATION*)dirname)->Name; + length = ustr->Length / sizeof(WCHAR); + return QString::fromWCharArray(ustr->Buffer, length); + } + } + return QString(); +} + +QByteArray CSbieAPI::MakeEnvironment(bool AddDeviceMap) +{ + QStringList EnvList; + + //if(AllUsers.length() > 0) + // EnvList.push_back("00000000_SBIE_ALL_USERS=" + AllUsers); + //if(CurrentUser.length() > 0) + // EnvList.push_back("00000000_SBIE_CURRENT_USER=" + CurrentUser); + //if(PublicUser.length() > 0) + // EnvList.push_back("00000000_SBIE_PUBLIC_USER=" + PublicUser); + QString DeviceMap = AddDeviceMap ? GetDeviceMap() : QString(); + if (DeviceMap.length() > 0) + EnvList.push_back("00000000_SBIE_DEVICE_MAP=" + DeviceMap); + + ULONG ExtraLength = 0; + foreach(const QString& Entry, EnvList) + ExtraLength += Entry.length() + 1; + + WCHAR *Environment = GetEnvironmentStrings(); + ULONG EnvLength = 0; + for(WCHAR* envPtr = (WCHAR*)Environment; *envPtr;) + { + ULONG len = wcslen(envPtr) + 1; + envPtr += len; + EnvLength += len; + } + + QByteArray env; + env.resize((ExtraLength + EnvLength + 1) * sizeof(WCHAR)); // key1=value1\0key2=value2\0...keyN=valueN\0\0 + WCHAR* envPtr = (WCHAR*)env.data(); + foreach(const QString& Entry, EnvList) + { + Entry.toWCharArray(envPtr); + envPtr += Entry.length(); + *envPtr++ = L'\0'; + } + + wmemcpy(envPtr, Environment, EnvLength); + envPtr += EnvLength; + + *envPtr = L'\0'; + + return env; +} + +SB_STATUS CSbieAPI::RunSandboxed(const QString& BoxName, const QString& Command, QString WrkDir, quint32 Flags) +{ + DWORD wShowWindow = SW_SHOWNORMAL; + + if (Command.isEmpty()) + return SB_ERR(ERROR_INVALID_PARAMETER); + + if (WrkDir.isEmpty()) + WrkDir = QDir::currentPath(); + + ULONG cmd_len = Command.length(); + ULONG dir_len = WrkDir.length(); + QByteArray env = MakeEnvironment(true); + ULONG env_len = (env.size() - 1) / sizeof(WCHAR); + + ULONG req_len = sizeof(PROCESS_RUN_SANDBOXED_REQ) + (cmd_len + dir_len + env_len + 8) * sizeof(WCHAR); + PROCESS_RUN_SANDBOXED_REQ* req = (PROCESS_RUN_SANDBOXED_REQ*)malloc(req_len); + + req->h.length = req_len; + req->h.msgid = MSGID_PROCESS_RUN_SANDBOXED; + BoxName.toWCharArray(req->boxname); // fix-me: potential overflow + req->boxname[BoxName.length()] = L'\0'; + req->si_flags = STARTF_FORCEOFFFEEDBACK; + req->si_show_window = wShowWindow; + if (req->si_show_window != SW_SHOWNORMAL) + req->si_flags |= STARTF_USESHOWWINDOW; + req->creation_flags = Flags; + + WCHAR* ptr = (WCHAR*)((ULONG_PTR)req + sizeof(PROCESS_RUN_SANDBOXED_REQ)); + + req->cmd_ofs = (ULONG)((ULONG_PTR)ptr - (ULONG_PTR)req); + req->cmd_len = cmd_len; + if (cmd_len) { + Command.toWCharArray(ptr); + ptr += cmd_len; + } + *ptr++ = L'\0'; + + req->dir_ofs = (ULONG)((ULONG_PTR)ptr - (ULONG_PTR)req); + req->dir_len = dir_len; + if (dir_len) { + WrkDir.toWCharArray(ptr); + ptr += dir_len; + } + *ptr++ = L'\0'; + + req->env_ofs = (ULONG)((ULONG_PTR)ptr - (ULONG_PTR)req); + req->env_len = env_len; + if (env_len) { + wmemcpy(ptr, (WCHAR*)env.data(), env_len); + ptr += env_len; + } + *ptr++ = L'\0'; + + PROCESS_RUN_SANDBOXED_RPL *rpl; + SB_STATUS Status = CSbieAPI__CallServer(m, &req->h, (MSG_HEADER*&)rpl); + free(req); + if (!Status) + emit LogMessage(tr("Failed to communicate with Sandboxie Service: %1").arg(Status.GetText())); + if (!Status) + return Status; + if (!rpl) + return SB_ERR(ERROR_SERVER_DISABLED); + + + if (rpl->h.status != 0) + Status = SB_ERR(rpl->h.status); + + PROCESS_INFORMATION pi; + pi.hProcess = (HANDLE)rpl->hProcess; + pi.hThread = (HANDLE)rpl->hThread; + pi.dwProcessId = rpl->dwProcessId; + pi.dwThreadId = rpl->dwThreadId; + + free(rpl); + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + + return Status; +} + + +/////////////////////////////////////////////////////////////////////////////// +// Conf +// + +SB_STATUS CSbieAPI::ReloadConfig(quint32 SessionId) +{ + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + + memset(parms, 0, sizeof(parms)); + parms[0] = API_RELOAD_CONF; + parms[1] = SessionId; + + NTSTATUS status = m->IoControl(parms); + if (!NT_SUCCESS(status)) + return SB_ERR(status); + return SB_OK; +} + +bool CSbieAPI::IsBoxEnabled(const QString& BoxName) +{ + wstring box_name = BoxName.toStdWString(); + + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_IS_BOX_ENABLED_ARGS *args = (API_IS_BOX_ENABLED_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_IS_BOX_ENABLED; + args->box_name.val = (WCHAR*)box_name.c_str(); + + return NT_SUCCESS(m->IoControl(parms)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Log +// + +QString CSbieAPI__FormatSbieMsg(SSbieAPI* m, ULONG code, const WCHAR *ins1, const WCHAR *ins2) +{ + ULONG FormatFlags = FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_ALLOCATE_BUFFER; + ULONG Lang = 1033; // English + + ULONG ret_count = 0; + WCHAR* ret_str = NULL; + + if (m->SbieMsgDll) + { + const WCHAR *ins[6] = { 0 , ins1, ins2, 0, 0, 0 }; + ret_count = FormatMessage(FormatFlags, m->SbieMsgDll, code, Lang, (LPWSTR)&ret_str, 4, (va_list *)ins); + } + + if (ret_count == 0) + { + ret_str = (WCHAR*)LocalAlloc(LMEM_FIXED, 128 * sizeof(WCHAR)); + swprintf(ret_str, L"SBIE%04i: %s; %s", code & 0x0000FFFF, ins1 ? ins1 : L"", ins2 ? ins2 : L""); + } + + QString qStr = QString::fromWCharArray(ret_str); + LocalFree(ret_str); + return qStr.trimmed(); // note messages may have \r\n at the end +} + +bool CSbieAPI::GetLog() +{ + wchar_t* Buffer[4*1024]; + ULONG Length = ARRAYSIZE(Buffer); + + ULONG MessageId = 0; + ULONG MessageNum = m->lastMessageNum; + + __declspec(align(8)) UNICODE_STRING64 msgtext = { 0, (USHORT)Length, (ULONG64)Buffer }; + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_GET_MESSAGE_ARGS *args = (API_GET_MESSAGE_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_GET_MESSAGE; + args->msg_num.val = &MessageNum; + args->session_id.val = m->sessionId; + args->msgid.val = &MessageId; + args->msgtext.val = &msgtext; + + NTSTATUS status = m->IoControl(parms); + if (!NT_SUCCESS(status)) + return false; // error or no more entries + + //if (MessageNum != m->lastMessageNum + 1) + // we missed something + m->lastMessageNum = MessageNum; + + if (MessageId == 0) + return true; // empty dummy message for maintaining sequence consistency + + WCHAR *str1 = (WCHAR*)msgtext.Buffer; + ULONG str1_len = wcslen(str1); + WCHAR *str2 = str1 + str1_len + 1; + ULONG str2_len = wcslen(str2); + + QString Message = CSbieAPI__FormatSbieMsg(m, MessageId, str1, str2); + + emit LogMessage(Message); + + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// Monitor +// + +SB_STATUS CSbieAPI::EnableMonitor(bool Enable) +{ + ULONG uEnable = Enable ? TRUE : FALSE; + + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_MONITOR_CONTROL_ARGS* args = (API_MONITOR_CONTROL_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_MONITOR_CONTROL; + args->set_flag.val = &uEnable; // NewState + args->get_flag.val = NULL; // OldState + + NTSTATUS status = m->IoControl(parms); + if (!NT_SUCCESS(status)) + return SB_ERR(status); + return SB_OK; +} + +bool CSbieAPI::IsMonitoring() +{ + ULONG uEnabled = FALSE; + + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_MONITOR_CONTROL_ARGS* args = (API_MONITOR_CONTROL_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_MONITOR_CONTROL; + args->set_flag.val = NULL; // NewState + args->get_flag.val = &uEnabled; // OldState + + return NT_SUCCESS(m->IoControl(parms)) && uEnabled; +} + +bool CSbieAPI::GetMonitor() +{ + USHORT type; + ULONG64 pid; + WCHAR name[256 + 1] = { 0 }; + + ULONG RecordNum = m->lastRecordNum; + + __declspec(align(8)) ULONG64 parms[API_NUM_ARGS]; + API_MONITOR_GET_EX_ARGS* args = (API_MONITOR_GET_EX_ARGS*)parms; + + memset(parms, 0, sizeof(parms)); + args->func_code = API_MONITOR_GET_EX; + args->name_seq.val = &RecordNum; + args->name_type.val = &type; + args->name_pid.val = &pid; + args->name_len.val = 256 * sizeof(WCHAR); + args->name_ptr.val = name; + + if (!NT_SUCCESS(m->IoControl(parms))) + return false; + + //if (RecordNum != m->lastRecordNum + 1) + // we missed something + m->lastRecordNum = RecordNum; + + if (type == 0) + return false; + + CResLogEntryPtr LogEntry = CResLogEntryPtr(new CResLogEntry(pid, type, QString::fromWCharArray(name))); + + QWriteLocker Lock(&m_ResLogMutex); + m_ResLogList.append(LogEntry); + return true; +} + +/////////////////////////////////////////////////////////////////////////////// +// +// + +CResLogEntry::CResLogEntry(quint64 ProcessId, quint32 Type, const QString& Value) +{ + m_ProcessId = ProcessId; + m_Name = Value; + switch (Type & 0x0FFF) + { + case MONITOR_PIPE: m_Type = "Pipe"; break; + case MONITOR_IPC: m_Type = "Ipc"; break; + case MONITOR_WINCLASS: m_Type = "WinClass"; break; + case MONITOR_DRIVE: m_Type = "Drive"; break; + case MONITOR_COMCLASS: m_Type = "ComClass"; break; + case MONITOR_IGNORE: m_Type = "Ignore"; break; + case MONITOR_IMAGE: m_Type = "Image"; break; + case MONITOR_FILE_OR_KEY: m_Type = "File"; break; + case MONITOR_OTHER: m_Type = "Other"; break; + default: m_Type = "Unknown: " + QString::number(Type); + } + m_Open = (Type & MONITOR_OPEN) != 0; + m_Deny = (Type & MONITOR_DENY) != 0; + //m_Verbose = (Type & MONITOR_VERBOSE) != 0; + //m_User = (Type & MONITOR_USER) != 0; + m_TimeStamp = QDateTime::currentDateTime(); // ms resolution + + static atomic uid = 0; + m_uid = uid.fetch_add(1); +} + +/////////////////////////////////////////////////////////////////////////////// +// +// + +QString GetLastErrorAsString() +{ + DWORD errorMessageID = ::GetLastError(); + if (errorMessageID == 0) + return QString(); + + char* messageBuffer = NULL; + FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + + QString message(messageBuffer); + LocalFree(messageBuffer); + return message; +} + diff --git a/SandboxiePlus/QSbieAPI/SbieAPI.h b/SandboxiePlus/QSbieAPI/SbieAPI.h new file mode 100644 index 00000000..8caaa598 --- /dev/null +++ b/SandboxiePlus/QSbieAPI/SbieAPI.h @@ -0,0 +1,154 @@ +/* + * + * Copyright (c) 2020, David Xanatos + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library 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 + * Lesser 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 . + */ +#pragma once + +#include + +#include "qsbieapi_global.h" + +#include "SbieError.h" + +#include "./Sandboxie/SandBox.h" +#include "./Sandboxie/BoxedProcess.h" + +class CResLogEntry : public QSharedData +{ +public: + CResLogEntry(quint64 ProcessId, quint32 Type, const QString& Value); + + quint64 GetProcessId() const { return m_ProcessId; } + QDateTime GetTimeStamp() const { return m_TimeStamp; } + QString GetType() const { return m_Type; } + QString GetValue() const { return m_Name; } + + quint64 GetUID() const { return m_uid; } + +protected: + QString m_Name; + QString m_Type; + quint64 m_ProcessId; + QDateTime m_TimeStamp; + bool m_Open; + bool m_Deny; + //bool m_Verbose; + //bool m_User; + + quint64 m_uid; +}; + +typedef QSharedDataPointer CResLogEntryPtr; + + +class QSBIEAPI_EXPORT CSbieAPI : public QThread +{ + Q_OBJECT +public: + CSbieAPI(QObject* parent = 0); + virtual ~CSbieAPI(); + + virtual bool IsValid() const; + + virtual QString GetVersion(); + + virtual SB_STATUS TakeOver(); + + virtual QString GetSbiePath() const { return m_SbiePath; } + virtual QString GetIniPath() const { return m_IniPath; } + + virtual void UpdateDriveLetters(); + virtual QString Nt2DosPath(QString NtPath) const; + + virtual SB_STATUS ReloadBoxes(); + virtual SB_STATUS CreateBox(const QString& BoxName); + + virtual SB_STATUS UpdateProcesses(bool bKeep); + virtual SB_STATUS UpdateProcesses(bool bKeep, const CSandBoxPtr& pBox); + + virtual QMap GetAllBoxes() { return m_SandBoxes; } + + virtual SB_STATUS TerminateAll(); + + enum ESetMode + { + eIniUpdate = 0, + eIniAppend, + eIniInsert, + eIniDelete + }; + + // Config + virtual SB_STATUS ReloadConfig(quint32 SessionId = -1); + virtual QString SbieIniGet(const QString& Section, const QString& Setting, quint32 Index = 0, qint32* ErrCode = NULL); + virtual SB_STATUS SbieIniSet(const QString& Section, const QString& Setting, const QString& Value, ESetMode Mode = eIniUpdate); + virtual bool IsBoxEnabled(const QString& BoxName); + + // Monitor + virtual SB_STATUS EnableMonitor(bool Enable); + virtual bool IsMonitoring(); + + virtual QList GetResLog() const { QReadLocker Lock(&m_ResLogMutex); return m_ResLogList; } + +signals: + void LogMessage(const QString& Message); + +private slots: + //virtual void OnMonitorEntry(quint64 ProcessId, quint32 Type, const QString& Value); + +protected: + friend class CSandBox; + friend class CBoxedProcess; + + virtual QString GetSbieHome() const; + + virtual SB_STATUS RunStart(const QString& BoxName, const QString& Command); + + virtual SB_STATUS CleanBox(const QString& BoxName); + virtual SB_STATUS RenameBox(const QString& OldName, const QString& NewName, bool deleteOld = true); + virtual SB_STATUS RemoveBox(const QString& BoxName); + + virtual bool GetLog(); + virtual bool GetMonitor(); + + virtual SB_STATUS TerminateAll(const QString& BoxName); + virtual SB_STATUS Terminate(quint64 ProcessId); + + virtual SB_STATUS RunSandboxed(const QString& BoxName, const QString& Command, QString WrkDir = QString(), quint32 Flags = 0); + + virtual SB_STATUS SetBoxPaths(const CSandBoxPtr& pSandBox); + virtual SB_STATUS SetProcessInfo(const CBoxedProcessPtr& pProcess); + + virtual QString GetDeviceMap(); + virtual QByteArray MakeEnvironment(bool AddDeviceMap); + + virtual void run(); + + QMap m_SandBoxes; + QMap m_BoxedProxesses; + + mutable QReadWriteLock m_ResLogMutex; + QList m_ResLogList; + + QMap m_DriveLetters; + + QString m_SbiePath; + QString m_IniPath; + + bool m_bTerminate; +private: + struct SSbieAPI* m; +}; diff --git a/SandboxiePlus/QSbieAPI/SbieError.h b/SandboxiePlus/QSbieAPI/SbieError.h new file mode 100644 index 00000000..c096cbc2 --- /dev/null +++ b/SandboxiePlus/QSbieAPI/SbieError.h @@ -0,0 +1,105 @@ +#pragma once + +class CSbieError +{ +public: + CSbieError() + { + m = NULL; + } + CSbieError(const QString& Error, long Status = 0xC0000001 /*STATUS_UNSUCCESSFUL*/) : CSbieError() + { + SFlexError* p = new SFlexError(); + p->Error = Error; + p->Status = Status; + Attach(p); + } + CSbieError(long Status) : CSbieError(QObject::tr("Error Code: %1").arg(Status), Status) + { + } + CSbieError(const CSbieError& other) : CSbieError() + { + if(other.m != NULL) + Attach((SFlexError*)other.m); + } + /*virtual*/ ~CSbieError() + { + Detach(); + } + + CSbieError& operator=(const CSbieError& Array) + { + Attach(Array.m); + return *this; + } + + __inline bool IsError() const { return m != NULL; } + __inline long GetStatus() const { return m ? m->Status : 0; } + __inline QString GetText() const { return m ? m->Error: ""; } + + operator bool() const {return !IsError();} + +private: + struct SFlexError + { + QString Error; + long Status; + + mutable atomic aRefCnt; + } *m; + + void Attach(SFlexError* p) + { + Detach(); + + if (p != NULL) + { + p->aRefCnt.fetch_add(1); + m = p; + } + } + + void Detach() + { + if (m != NULL) + { + if (m->aRefCnt.fetch_sub(1) == 1) + delete m; + m = NULL; + } + } +}; + +typedef CSbieError SB_STATUS; +#define SB_OK CSbieError() +#define SB_ERR CSbieError + + +/* +template +class CSbieResult : public CSbieError +{ +public: + CSbieResult(const T& value = T()) : CSbieError() + { + v = value; + } + CSbieResult(const CSbieError& other) : CSbieResult() + { + if (other.m != NULL) + Attach((SFlexError*)other.m); + } + CSbieResult(const CSbieResult& other) : CSbieResult(other) + { + v = other.v; + } + + __inline T GetValue() const { return v; } + +private: + T v; +}; + +#define SB_RESULT(x) CSbieResult +#define SB_RETURN(x,y) CSbieResult(y) +*/ \ No newline at end of file diff --git a/SandboxiePlus/QSbieAPI/qsbieapi_global.h b/SandboxiePlus/QSbieAPI/qsbieapi_global.h new file mode 100644 index 00000000..a64b9982 --- /dev/null +++ b/SandboxiePlus/QSbieAPI/qsbieapi_global.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +#ifndef BUILD_STATIC +# if defined(QSBIEAPI_LIB) +# define QSBIEAPI_EXPORT Q_DECL_EXPORT +# else +# define QSBIEAPI_EXPORT Q_DECL_IMPORT +# endif +#else +# define QSBIEAPI_EXPORT +#endif diff --git a/SandboxiePlus/QSbieAPI/stdafx.cpp b/SandboxiePlus/QSbieAPI/stdafx.cpp new file mode 100644 index 00000000..a27b824d --- /dev/null +++ b/SandboxiePlus/QSbieAPI/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/SandboxiePlus/QSbieAPI/stdafx.h b/SandboxiePlus/QSbieAPI/stdafx.h new file mode 100644 index 00000000..fb189aa6 --- /dev/null +++ b/SandboxiePlus/QSbieAPI/stdafx.h @@ -0,0 +1,43 @@ +#pragma once + +// std includes +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include diff --git a/SandboxiePlus/SandMan/ApiLog.cpp b/SandboxiePlus/SandMan/ApiLog.cpp new file mode 100644 index 00000000..c21a7d18 --- /dev/null +++ b/SandboxiePlus/SandMan/ApiLog.cpp @@ -0,0 +1,82 @@ +#include "stdafx.h" +#include "ApiLog.h" + +CApiLog::CApiLog(QObject* parent) : QThread(parent) +{ + m_pServer = NULL; + + start(); +} + +CApiLog::~CApiLog() +{ + quit(); + if (!wait(10 * 1000)) + terminate(); +} + +void CApiLog::run() +{ + qDebug() << "LogAPI server started"; + + m_pServer = new CApiLogServer(); + + QThread::run(); // run messge loop + + m_pServer->deleteLater(); + + qDebug() << "LogAPI server stopped"; +} + +///////////////////////////////////////////////////////////////////////////////////// +// + +CApiLogServer::CApiLogServer() +{ + m_pServer = new QLocalServer(this); + m_pServer->setSocketOptions(QLocalServer::WorldAccessOption); + if (!m_pServer->listen("LogAPI")) { + qDebug() << "Not able to open Server Pipe"; + return; + } + + connect(m_pServer, SIGNAL(newConnection()), this, SLOT(OnPipe())); + + m_pApiLog = qobject_cast(thread()); +} + +void CApiLogServer::OnPipe() +{ + QLocalSocket *pSocket = m_pServer->nextPendingConnection(); + connect(pSocket, SIGNAL(readyRead()), this, SLOT(OnData())); + connect(pSocket, SIGNAL(disconnected()), this, SLOT(OnClose())); + + m_pClients.insert(pSocket, new SApiLog()); +} + +void CApiLogServer::OnData() +{ + QLocalSocket* pSocket = qobject_cast(sender()); + SApiLog* ApiLog = m_pClients.value(pSocket); + if (!ApiLog) + return; + + ApiLog->Buffer.append(pSocket->readAll()); + + for (;;) + { + int endPos = ApiLog->Buffer.indexOf('\0'); + if (endPos == -1) + break; + + emit m_pApiLog->ApiLogEntry(QString(ApiLog->Buffer.data())); + ApiLog->Buffer.remove(0, endPos + 1); + } +} + +void CApiLogServer::OnClose() +{ + QLocalSocket* pSocket = qobject_cast(sender()); + delete m_pClients.take(pSocket); + pSocket->deleteLater(); +} \ No newline at end of file diff --git a/SandboxiePlus/SandMan/ApiLog.h b/SandboxiePlus/SandMan/ApiLog.h new file mode 100644 index 00000000..e55dae9b --- /dev/null +++ b/SandboxiePlus/SandMan/ApiLog.h @@ -0,0 +1,46 @@ +#pragma once + + +class CApiLog : public QThread +{ + Q_OBJECT +public: + CApiLog(QObject* parent = 0); + virtual ~CApiLog(); + +signals: + void ApiLogEntry(const QString& Message); + +protected: + virtual void run(); + + class CApiLogServer*m_pServer; +}; + +///////////////////////////////////////////////////////////////////////////////////// +// + +class CApiLogServer : public QObject +{ + Q_OBJECT +protected: + CApiLogServer(); + +public slots: + void OnPipe(); + void OnData(); + void OnClose(); + +protected: + friend class CApiLog; + + struct SApiLog + { + QByteArray Buffer; + }; + + QLocalServer* m_pServer; + QMap m_pClients; + + CApiLog* m_pApiLog; +}; \ No newline at end of file diff --git a/SandboxiePlus/SandMan/Dialogs/MultiErrorDialog.cpp b/SandboxiePlus/SandMan/Dialogs/MultiErrorDialog.cpp new file mode 100644 index 00000000..ddcebc85 --- /dev/null +++ b/SandboxiePlus/SandMan/Dialogs/MultiErrorDialog.cpp @@ -0,0 +1,51 @@ +#include "stdafx.h" +#include "../../MiscHelpers/Common/Settings.h" +#include "MultiErrorDialog.h" + + +CMultiErrorDialog::CMultiErrorDialog(const QString& Message, QList Errors, QWidget* parent) + : QDialog(parent) +{ + this->setWindowTitle(tr("Sandboxie-Plus - Error")); + m_pMainLayout = new QGridLayout(this); + + int Row = 0; + m_pMainLayout->addWidget(new QLabel(Message), Row++, 0, 1, 4); + + m_pErrors = new CPanelWidgetEx(); + + //m_pErrors->GetTree()->setHeaderLabels(tr("Message|Status|Error").split("|")); + m_pErrors->GetTree()->setHeaderLabels(tr("Message").split("|")); + + m_pErrors->GetView()->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_pErrors->GetView()->setSortingEnabled(false); + + m_pMainLayout->addWidget(m_pErrors, Row++, 0, 1, 4); + + m_pButtonBox = new QDialogButtonBox(); + m_pButtonBox->setOrientation(Qt::Horizontal); + m_pButtonBox->setStandardButtons(QDialogButtonBox::Ok|QDialogButtonBox::Cancel); + m_pMainLayout->addWidget(m_pButtonBox, Row++, 0, 1, 4); + + connect(m_pButtonBox,SIGNAL(accepted()),this,SLOT(accept())); + connect(m_pButtonBox,SIGNAL(rejected()),this,SLOT(reject())); + + restoreGeometry(theConf->GetBlob("ErrorWindow/Window_Geometry")); + + + foreach(const SB_STATUS& Error, Errors) + { + QTreeWidgetItem* pItem = new QTreeWidgetItem(); + pItem->setText(eMessage, Error.GetText()); + //pItem->setText(eErrorCode, tr("0x%1").arg((quint32)Error.GetStatus(), 8, 16, QChar('0'))); + m_pErrors->GetTree()->addTopLevelItem(pItem); + } + + for(int i = 0; i < m_pErrors->GetTree()->columnCount(); i++) + m_pErrors->GetTree()->resizeColumnToContents(i); +} + +CMultiErrorDialog::~CMultiErrorDialog() +{ + theConf->SetBlob("ErrorWindow/Window_Geometry", saveGeometry()); +} diff --git a/SandboxiePlus/SandMan/Dialogs/MultiErrorDialog.h b/SandboxiePlus/SandMan/Dialogs/MultiErrorDialog.h new file mode 100644 index 00000000..1ddfd212 --- /dev/null +++ b/SandboxiePlus/SandMan/Dialogs/MultiErrorDialog.h @@ -0,0 +1,28 @@ +#pragma once +#include "../../MiscHelpers/Common/PanelView.h" +#include "../../MiscHelpers/Common/FlexError.h" +#include "../../QSbieAPI/SbieError.h" + +class CMultiErrorDialog : public QDialog +{ + Q_OBJECT + +public: + CMultiErrorDialog(const QString& Message, QList Errors, QWidget* parent = 0); + virtual ~CMultiErrorDialog(); + +private: + enum EColumns + { + eMessage, + //eErrorCode, + //eErrorText, + eCount + }; + + QGridLayout* m_pMainLayout; + + CPanelWidgetEx* m_pErrors; + + QDialogButtonBox * m_pButtonBox; +}; \ No newline at end of file diff --git a/SandboxiePlus/SandMan/Models/ResMonModel.cpp b/SandboxiePlus/SandMan/Models/ResMonModel.cpp new file mode 100644 index 00000000..f230baab --- /dev/null +++ b/SandboxiePlus/SandMan/Models/ResMonModel.cpp @@ -0,0 +1,132 @@ +#include "stdafx.h" +#include "ResMonModel.h" +#include "../MiscHelpers/Common/Common.h" + +CResMonModel::CResMonModel(QObject *parent) +:CListItemModel(parent) +{ +} + +CResMonModel::~CResMonModel() +{ +} + +void CResMonModel::Sync(const QList& List, QSet PIDs) +{ + QList New; + QHash Old = m_Map; + + foreach (const CResLogEntryPtr& pEntry, List) + { + QVariant ID = pEntry->GetUID(); + + if (!PIDs.isEmpty() && !PIDs.contains(pEntry->GetProcessId())) + continue; + + int Row = -1; + QHash::iterator I = Old.find(ID); + SResLogNode* pNode = I != Old.end() ? static_cast(I.value()) : NULL; + if(!pNode) + { + pNode = static_cast(MkNode(ID)); + pNode->Values.resize(columnCount()); + pNode->pEntry = pEntry; + New.append(pNode); + } + else + { + I.value() = NULL; + Row = GetRow(pNode); + } + + int Col = 0; + bool State = false; + int Changed = 0; + + /*int RowColor = CTaskExplorer::eNone; + if (pGDI->IsMarkedForRemoval() && CTaskExplorer::UseListColor(CTaskExplorer::eToBeRemoved)) RowColor = CTaskExplorer::eToBeRemoved; + else if (pGDI->IsNewlyCreated() && CTaskExplorer::UseListColor(CTaskExplorer::eAdded)) RowColor = CTaskExplorer::eAdded; + + if (pNode->iColor != RowColor) { + pNode->iColor = RowColor; + pNode->Color = CTaskExplorer::GetListColor(RowColor); + Changed = 2; + }*/ + + for(int section = 0; section < columnCount(); section++) + { + if (!m_Columns.contains(section)) + continue; // ignore columns which are hidden + + QVariant Value; + switch(section) + { + case eProcess: Value = pEntry->GetProcessId(); break; + case eTimeStamp: Value = pEntry->GetTimeStamp(); break; + case eType: Value = pEntry->GetType(); break; + case eValue: Value = pEntry->GetValue(); break; + } + + SResLogNode::SValue& ColValue = pNode->Values[section]; + + if (ColValue.Raw != Value) + { + if(Changed == 0) + Changed = 1; + ColValue.Raw = Value; + + switch (section) + { + case eProcess: ColValue.Formated = QString::number(pEntry->GetProcessId()); break; + case eTimeStamp: ColValue.Formated = pEntry->GetTimeStamp().toString("hh:mm:ss.zzz"); break; + //case eType: ColValue.Formated = ; break; + //case eValue: ColValue.Formated = ; break; + } + } + + if(State != (Changed != 0)) + { + if(State && Row != -1) + emit dataChanged(createIndex(Row, Col), createIndex(Row, section-1)); + State = (Changed != 0); + Col = section; + } + if(Changed == 1) + Changed = 0; + } + if(State && Row != -1) + emit dataChanged(createIndex(Row, Col, pNode), createIndex(Row, columnCount()-1, pNode)); + + } + + CListItemModel::Sync(New, Old); +} + +CResLogEntryPtr CResMonModel::GetEntry(const QModelIndex &index) const +{ + if (!index.isValid()) + return CResLogEntryPtr(); + + SResLogNode* pNode = static_cast(index.internalPointer()); + return pNode->pEntry; +} + +int CResMonModel::columnCount(const QModelIndex &parent) const +{ + return eCount; +} + +QVariant CResMonModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + switch(section) + { + case eProcess: return tr("Process"); + case eTimeStamp: return tr("TimeStamp"); + case eType: return tr("Time"); + case eValue: return tr("Value"); + } + } + return QVariant(); +} diff --git a/SandboxiePlus/SandMan/Models/ResMonModel.h b/SandboxiePlus/SandMan/Models/ResMonModel.h new file mode 100644 index 00000000..a13368cc --- /dev/null +++ b/SandboxiePlus/SandMan/Models/ResMonModel.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include "../../QSbieAPI/SbieAPI.h" +#include "../../MiscHelpers/Common/ListItemModel.h" + +class CResMonModel : public CListItemModel +{ + Q_OBJECT + +public: + CResMonModel(QObject *parent = 0); + ~CResMonModel(); + + void Sync(const QList& List, QSet PIDs); + + CResLogEntryPtr GetEntry(const QModelIndex &index) const; + + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + enum EColumns + { + eProcess = 0, + eTimeStamp, + eType, + eValue, + eCount + }; + +protected: + struct SResLogNode: SListNode + { + SResLogNode(const QVariant& Id) : SListNode(Id), iColor(0) {} + + CResLogEntryPtr pEntry; + + int iColor; + }; + + virtual SListNode* MkNode(const QVariant& Id) { return new SResLogNode(Id); } +}; \ No newline at end of file diff --git a/SandboxiePlus/SandMan/Models/SbieModel.cpp b/SandboxiePlus/SandMan/Models/SbieModel.cpp new file mode 100644 index 00000000..8f812fd9 --- /dev/null +++ b/SandboxiePlus/SandMan/Models/SbieModel.cpp @@ -0,0 +1,305 @@ +#include "stdafx.h" +#include "SbieModel.h" +#include "../../MiscHelpers/Common/Common.h" +#include "../../MiscHelpers/Common/IconExtreactor.h" + + +CSbieModel::CSbieModel(QObject *parent) +:CTreeItemModel(parent) +{ + m_BoxEmpty = QIcon(":/BoxEmpty"); + m_BoxInUse = QIcon(":/BoxInUse"); + m_ExeIcon = QIcon(":/exeIcon32"); + + m_Root = MkNode(QVariant()); +} + +CSbieModel::~CSbieModel() +{ +} + +QList CSbieModel::MakeProcPath(const QString& BoxName, const CBoxedProcessPtr& pProcess, const QMap& ProcessList) +{ + QList Path = MakeProcPath(pProcess, ProcessList); + Path.prepend(BoxName); + return Path; +} + +QList CSbieModel::MakeProcPath(const CBoxedProcessPtr& pProcess, const QMap& ProcessList) +{ + quint64 ParentID = pProcess->GetParendPID(); + CBoxedProcessPtr pParent = ProcessList.value(ParentID); + + QList Path; + if (!pParent.isNull() && ParentID != pProcess->GetProcessId()) + { + Path = MakeProcPath(pParent, ProcessList); + Path.append(ParentID); + } + return Path; +} + +bool CSbieModel::TestProcPath(const QList& Path, const QString& BoxName, const CBoxedProcessPtr& pProcess, const QMap& ProcessList, int Index) +{ + if (Index == 0) + { + if (Path.isEmpty() || BoxName != Path[0]) + return false; + + return TestProcPath(Path, BoxName, pProcess, ProcessList, 1); + } + + quint64 ParentID = pProcess->GetParendPID(); + CBoxedProcessPtr pParent = ProcessList.value(ParentID); + + if (!pParent.isNull() && ParentID != pProcess->GetProcessId()) + { + if(Index >= Path.size() || Path[Path.size() - Index] != ParentID) + return false; + + return TestProcPath(Path, BoxName, pParent, ProcessList, Index + 1); + } + + return Path.size() == Index; +} + +QList CSbieModel::Sync(const QMap& BoxList) +{ + QList Added; + QMap, QList > New; + QHash Old = m_Map; + + foreach (const CSandBoxPtr& pBox, BoxList) + { + QVariant ID = pBox->GetName(); + + QModelIndex Index; + + QHash::iterator I = Old.find(ID); + SSandBoxNode* pNode = I != Old.end() ? static_cast(I.value()) : NULL; + if(!pNode) + { + pNode = static_cast(MkNode(ID)); + pNode->Values.resize(columnCount()); + pNode->pBox = pBox; + New[pNode->Path].append(pNode); + Added.append(ID); + } + else + { + I.value() = NULL; + Index = Find(m_Root, pNode); + } + + int Col = 0; + bool State = false; + int Changed = 0; + + QMap ProcessList = pBox->GetProcessList(); + + bool HasActive = Sync(pBox, ProcessList, New, Old, Added); + + if (pNode->inUse != (HasActive ? 1 : 0)) + { + pNode->inUse = (HasActive ? 1 : 0); + pNode->Icon = pNode->inUse ? m_BoxInUse : m_BoxEmpty; + Changed = 1; // set change for first column + } + + for(int section = 0; section < columnCount(); section++) + { + if (!m_Columns.contains(section)) + continue; // ignore columns which are hidden + + QVariant Value; + switch(section) + { + case eName: Value = pBox->GetName(); break; + } + + SSandBoxNode::SValue& ColValue = pNode->Values[section]; + + if (ColValue.Raw != Value) + { + if(Changed == 0) + Changed = 1; + ColValue.Raw = Value; + + /*switch (section) + { + + }*/ + } + + if(State != (Changed != 0)) + { + if(State && Index.isValid()) + emit dataChanged(createIndex(Index.row(), Col, pNode), createIndex(Index.row(), section-1, pNode)); + State = (Changed != 0); + Col = section; + } + if(Changed == 1) + Changed = 0; + } + if(State && Index.isValid()) + emit dataChanged(createIndex(Index.row(), Col, pNode), createIndex(Index.row(), columnCount()-1, pNode)); + } + + CTreeItemModel::Sync(New, Old); + return Added; +} + +bool CSbieModel::Sync(const CSandBoxPtr& pBox, const QMap& ProcessList, QMap, QList >& New, QHash& Old, QList& Added) +{ + QString BoxName = pBox->GetName(); + + int ActiveCount = 0; + + foreach(const CBoxedProcessPtr& pProcess, ProcessList) + { + QVariant ID = pProcess->GetProcessId(); + + QModelIndex Index; + + QHash::iterator I = Old.find(ID); + SSandBoxNode* pNode = I != Old.end() ? static_cast(I.value()) : NULL; + if (!pNode || (m_bTree ? !TestProcPath(pNode->Path, BoxName, pProcess, ProcessList) : !pNode->Path.isEmpty())) + { + pNode = static_cast(MkNode(ID)); + pNode->Values.resize(columnCount()); + if(m_bTree) + pNode->Path = MakeProcPath(BoxName, pProcess, ProcessList); + pNode->pBox = pBox; + pNode->pProcess = pProcess; + New[pNode->Path].append(pNode); + Added.append(ID); + } + else + { + I.value() = NULL; + Index = Find(m_Root, pNode); + } + + //if(Index.isValid()) // this is to slow, be more precise + // emit dataChanged(createIndex(Index.row(), 0, pNode), createIndex(Index.row(), columnCount()-1, pNode)); + + int Col = 0; + bool State = false; + int Changed = 0; + + bool bIsTerminated = pProcess->IsTerminated(); + if (pNode->IsGray != bIsTerminated) + { + pNode->IsGray = bIsTerminated; + Changed = 2; // update all columns for this item + } + + if (!bIsTerminated) + ActiveCount++; + + if (pNode->Icon.isNull()) + { + PixmapEntryList icons = extractIcons(pProcess->GetFileName(), false); + if (icons.isEmpty()) + pNode->Icon = m_ExeIcon; + else + pNode->Icon = icons.first().pixmap; + } + + for (int section = 0; section < columnCount(); section++) + { + if (!m_Columns.contains(section)) + continue; // ignore columns which are hidden + + QVariant Value; + switch (section) + { + case eName: Value = pProcess->GetProcessName(); break; + case eProcessId: Value = pProcess->GetProcessId(); break; + case eStatus: Value = pProcess->GetStatusStr(); break; + //case eTitle: break; // todo + //case eLogCount: break; // todo Value = pProcess->GetResourceLog().count(); break; + case eTimeStamp: Value = pProcess->GetTimeStamp(); break; + } + + SSandBoxNode::SValue& ColValue = pNode->Values[section]; + + if (ColValue.Raw != Value) + { + if (Changed == 0) + Changed = 1; + ColValue.Raw = Value; + + switch (section) + { + case eProcessId: ColValue.Formated = QString::number(pProcess->GetProcessId()); break; + //case eLogCount: ColValue.Formated = QString::number(Value.toInt()); break; + case eTimeStamp: ColValue.Formated = pProcess->GetTimeStamp().toString("hh:mm:ss"); break; + } + } + + if (State != (Changed != 0)) + { + if (State && Index.isValid()) + emit dataChanged(createIndex(Index.row(), Col, pNode), createIndex(Index.row(), section - 1, pNode)); + State = (Changed != 0); + Col = section; + } + if (Changed == 1) + Changed = 0; + } + if (State && Index.isValid()) + emit dataChanged(createIndex(Index.row(), Col, pNode), createIndex(Index.row(), columnCount() - 1, pNode)); + } + + return ActiveCount != 0; +} + +CSandBoxPtr CSbieModel::GetSandBox(const QModelIndex &index) const +{ + if (!index.isValid()) + return CSandBoxPtr(); + + SSandBoxNode* pNode = static_cast(index.internalPointer()); + ASSERT(pNode); + + return pNode->pBox; +} + +CBoxedProcessPtr CSbieModel::GetProcess(const QModelIndex &index) const +{ + if (!index.isValid()) + return CBoxedProcessPtr(); + + SSandBoxNode* pNode = static_cast(index.internalPointer()); + ASSERT(pNode); + + return pNode->pProcess; +} + +int CSbieModel::columnCount(const QModelIndex &parent) const +{ + return eCount; +} + +QVariant CSbieModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (orientation == Qt::Horizontal && role == Qt::DisplayRole) + { + switch(section) + { + case eName: return tr("Name"); + case eProcessId: return tr("Process ID"); + case eStatus: return tr("Status"); + //case eTitle: return tr("Title"); + //case eLogCount: return tr("Log Count"); + case eTimeStamp: return tr("Start Time"); + } + } + return QVariant(); +} + +/*QVariant CSbieModel::GetDefaultIcon() const +{ + return g_ExeIcon; +}*/ diff --git a/SandboxiePlus/SandMan/Models/SbieModel.h b/SandboxiePlus/SandMan/Models/SbieModel.h new file mode 100644 index 00000000..66ed32c1 --- /dev/null +++ b/SandboxiePlus/SandMan/Models/SbieModel.h @@ -0,0 +1,59 @@ +#pragma once +#include +#include "../../QSbieAPI/Sandboxie/SandBox.h" +#include "../../MiscHelpers/Common/TreeItemModel.h" + + +class CSbieModel : public CTreeItemModel +{ + Q_OBJECT + +public: + CSbieModel(QObject *parent = 0); + ~CSbieModel(); + + QList Sync(const QMap& BoxList); + + CSandBoxPtr GetSandBox(const QModelIndex &index) const; + CBoxedProcessPtr GetProcess(const QModelIndex &index) const; + + int columnCount(const QModelIndex &parent = QModelIndex()) const; + QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; + + enum EColumns + { + eName = 0, + eProcessId, + eStatus, + //eTitle, + //eLogCount, + eTimeStamp, + eCount + }; + +protected: + bool Sync(const CSandBoxPtr& pBox, const QMap& ProcessList, QMap, QList >& New, QHash& Old, QList& Added); + + struct SSandBoxNode: STreeNode + { + SSandBoxNode(const QVariant& Id) : STreeNode(Id) { inUse = -1; } + + CSandBoxPtr pBox; + int inUse; + + CBoxedProcessPtr pProcess; + }; + + virtual STreeNode* MkNode(const QVariant& Id) { return new SSandBoxNode(Id); } + + QList MakeProcPath(const QString& BoxName, const CBoxedProcessPtr& pProcess, const QMap& ProcessList); + QList MakeProcPath(const CBoxedProcessPtr& pProcess, const QMap& ProcessList); + bool TestProcPath(const QList& Path, const QString& BoxName, const CBoxedProcessPtr& pProcess, const QMap& ProcessList, int Index = 0); + + //virtual QVariant GetDefaultIcon() const; + +private: + QIcon m_BoxEmpty; + QIcon m_BoxInUse; + QIcon m_ExeIcon; +}; \ No newline at end of file diff --git a/SandboxiePlus/SandMan/Resources/Actions/SetLogging.png b/SandboxiePlus/SandMan/Resources/Actions/SetLogging.png new file mode 100644 index 00000000..f769c083 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/Actions/SetLogging.png differ diff --git a/SandboxiePlus/SandMan/Resources/Actions/clean.png b/SandboxiePlus/SandMan/Resources/Actions/clean.png new file mode 100644 index 00000000..e52c564b Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/Actions/clean.png differ diff --git a/SandboxiePlus/SandMan/Resources/Actions/edit_ini.png b/SandboxiePlus/SandMan/Resources/Actions/edit_ini.png new file mode 100644 index 00000000..49ed7773 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/Actions/edit_ini.png differ diff --git a/SandboxiePlus/SandMan/Resources/Actions/empty_all.png b/SandboxiePlus/SandMan/Resources/Actions/empty_all.png new file mode 100644 index 00000000..7637be6f Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/Actions/empty_all.png differ diff --git a/SandboxiePlus/SandMan/Resources/Actions/exit.png b/SandboxiePlus/SandMan/Resources/Actions/exit.png new file mode 100644 index 00000000..f7cfe2c4 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/Actions/exit.png differ diff --git a/SandboxiePlus/SandMan/Resources/Actions/keep.png b/SandboxiePlus/SandMan/Resources/Actions/keep.png new file mode 100644 index 00000000..69ecfd4a Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/Actions/keep.png differ diff --git a/SandboxiePlus/SandMan/Resources/Actions/log_api.png b/SandboxiePlus/SandMan/Resources/Actions/log_api.png new file mode 100644 index 00000000..e744afaa Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/Actions/log_api.png differ diff --git a/SandboxiePlus/SandMan/Resources/Actions/new_box.png b/SandboxiePlus/SandMan/Resources/Actions/new_box.png new file mode 100644 index 00000000..e47686d1 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/Actions/new_box.png differ diff --git a/SandboxiePlus/SandMan/Resources/Actions/reload_ini.png b/SandboxiePlus/SandMan/Resources/Actions/reload_ini.png new file mode 100644 index 00000000..8a4db58a Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/Actions/reload_ini.png differ diff --git a/SandboxiePlus/SandMan/Resources/SandMan.ico b/SandboxiePlus/SandMan/Resources/SandMan.ico new file mode 100644 index 00000000..c05a26c5 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/SandMan.ico differ diff --git a/SandboxiePlus/SandMan/Resources/SandMan.png b/SandboxiePlus/SandMan/Resources/SandMan.png new file mode 100644 index 00000000..973fbab3 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/SandMan.png differ diff --git a/SandboxiePlus/SandMan/Resources/SandMan.qrc b/SandboxiePlus/SandMan/Resources/SandMan.qrc new file mode 100644 index 00000000..d1b40015 --- /dev/null +++ b/SandboxiePlus/SandMan/Resources/SandMan.qrc @@ -0,0 +1,21 @@ + + + SandMan.png + sandbox-empty.png + sandbox-full.png + dll32.png + exe32.png + close.png + + + Actions/SetLogging.png + Actions/exit.png + Actions/keep.png + Actions/clean.png + Actions/empty_all.png + Actions/new_box.png + Actions/reload_ini.png + Actions/edit_ini.png + Actions/log_api.png + + diff --git a/SandboxiePlus/SandMan/Resources/SandMan.rc b/SandboxiePlus/SandMan/Resources/SandMan.rc new file mode 100644 index 00000000..0c28c2cd --- /dev/null +++ b/SandboxiePlus/SandMan/Resources/SandMan.rc @@ -0,0 +1,100 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" +///////////////////////////////////////////////////////////////////////////// +// German (Austria) resources + +#include "winres.h" + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_DEA) +LANGUAGE LANG_GERMAN, SUBLANG_GERMAN_AUSTRIAN +#pragma code_page(1252) + +///////////////////////////////////////////////////////////////////////////// +// +// Icon +// + +// Icon with lowest ID value placed first to ensure application icon +// remains consistent on all systems. +IDI_ICON1 ICON ".\\Resources\\SandMan.ico" + + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\n" + "\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +VS_VERSION_INFO VERSIONINFO + FILEVERSION 1,0,0,1 + PRODUCTVERSION 1,0,0,1 + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x40004L + FILETYPE 0x1L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "0c0704b0" + BEGIN + //VALUE "CompanyName", "xanasoft.net" + VALUE "FileDescription", "SandBox Manager" + VALUE "FileVersion", "1.0.0.1" + VALUE "InternalName", "SandMan.exe" + VALUE "LegalCopyright", "Copyright (C) 2020 by DavidXanatos" + VALUE "OriginalFilename", "SandMan.exe" + VALUE "ProductName", "SandBox Manager" + VALUE "ProductVersion", "1.0.0.1" + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0xc07, 1200 + END +END + +#endif // German (Austria) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/SandboxiePlus/SandMan/Resources/close.png b/SandboxiePlus/SandMan/Resources/close.png new file mode 100644 index 00000000..ef9e0208 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/close.png differ diff --git a/SandboxiePlus/SandMan/Resources/dll32.png b/SandboxiePlus/SandMan/Resources/dll32.png new file mode 100644 index 00000000..04e13689 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/dll32.png differ diff --git a/SandboxiePlus/SandMan/Resources/exe32.png b/SandboxiePlus/SandMan/Resources/exe32.png new file mode 100644 index 00000000..726a7f78 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/exe32.png differ diff --git a/SandboxiePlus/SandMan/Resources/resource.h b/SandboxiePlus/SandMan/Resources/resource.h new file mode 100644 index 00000000..058f8af9 --- /dev/null +++ b/SandboxiePlus/SandMan/Resources/resource.h @@ -0,0 +1,15 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by SandMan.rc +// + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1000 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/SandboxiePlus/SandMan/Resources/sandbox-empty.png b/SandboxiePlus/SandMan/Resources/sandbox-empty.png new file mode 100644 index 00000000..79301033 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/sandbox-empty.png differ diff --git a/SandboxiePlus/SandMan/Resources/sandbox-full.png b/SandboxiePlus/SandMan/Resources/sandbox-full.png new file mode 100644 index 00000000..884d6a54 Binary files /dev/null and b/SandboxiePlus/SandMan/Resources/sandbox-full.png differ diff --git a/SandboxiePlus/SandMan/SandMan.cpp b/SandboxiePlus/SandMan/SandMan.cpp new file mode 100644 index 00000000..a489b44a --- /dev/null +++ b/SandboxiePlus/SandMan/SandMan.cpp @@ -0,0 +1,735 @@ +#include "stdafx.h" +#include "SandMan.h" +#include "../MiscHelpers/Common/ExitDialog.h" +#include "../MiscHelpers/Common/SortFilterProxyModel.h" +#include "Views/SbieView.h" +#include "../MiscHelpers/Common/CheckableMessageBox.h" +#include +#include "ApiLog.h" +#include "./Dialogs/MultiErrorDialog.h" + +CSbieAPI* theAPI = NULL; + +#if defined(Q_OS_WIN) +#include +#include +#include + +BOOLEAN OnWM_Notify(NMHDR *Header, LRESULT *Result); + +class CNativeEventFilter : public QAbstractNativeEventFilter +{ +public: + virtual bool nativeEventFilter(const QByteArray &eventType, void *message, long *result) + { + if (eventType == "windows_generic_MSG" || eventType == "windows_dispatcher_MSG") + { + MSG *msg = static_cast(message); + + //if(msg->message != 275 && msg->message != 1025) + // qDebug() << msg->message; + + if (msg->message == WM_NOTIFY) + { + LRESULT ret; + if (OnWM_Notify((NMHDR*)msg->lParam, &ret)) + *result = ret; + return true; + } + else if (msg->message == WM_DEVICECHANGE) + { + if (msg->wParam == DBT_DEVICEARRIVAL // Drive letter added + || msg->wParam == DBT_DEVICEREMOVECOMPLETE) // Drive letter removed + { + /*DEV_BROADCAST_HDR* deviceBroadcast = (DEV_BROADCAST_HDR*)msg->lParam; + if (deviceBroadcast->dbch_devicetype == DBT_DEVTYP_VOLUME) { + }*/ + if (theAPI) + theAPI->UpdateDriveLetters(); + } + /*else if ((msg->wParam & 0xFF80) == 0xAA00 && msg->lParam == 'xobs') + { + UCHAR driveNumber = (UCHAR)(msg->wParam & 0x1F); + if (driveNumber < 26) { + } + } + else if (msg->wParam == DBT_DEVNODES_CHANGED) // hardware changed + { + }*/ + } + } + return false; + } +}; + +HWND MainWndHandle = NULL; +#endif + +CSandMan::CSandMan(QWidget *parent) + : QMainWindow(parent) +{ +#if defined(Q_OS_WIN) + MainWndHandle = (HWND)QWidget::winId(); + + QApplication::instance()->installNativeEventFilter(new CNativeEventFilter); +#endif + + m_bExit = false; + + theAPI = new CSbieAPI(this); + + QString appTitle = tr("Sandboxie-Plus v%1").arg(GetVersion()); + appTitle.append(tr(" - Driver: v%1").arg(theAPI->GetVersion())); + this->setWindowTitle(appTitle); + + theAPI->TakeOver(); + + m_pMainWidget = new QWidget(); + m_pMainLayout = new QVBoxLayout(m_pMainWidget); + m_pMainLayout->setMargin(2); + m_pMainLayout->setSpacing(0); + this->setCentralWidget(m_pMainWidget); + + m_pToolBar = new QToolBar(); + m_pMainLayout->insertWidget(0, m_pToolBar); + + + m_pLogSplitter = new QSplitter(); + m_pLogSplitter->setOrientation(Qt::Vertical); + m_pMainLayout->addWidget(m_pLogSplitter); + + m_pPanelSplitter = new QSplitter(); + m_pPanelSplitter->setOrientation(Qt::Horizontal); + m_pLogSplitter->addWidget(m_pPanelSplitter); + + /* + // Box Tree + m_pBoxModel = new CSbieModel(); + m_pBoxModel->SetTree(true); + m_pBoxModel->SetUseIcons(true); + + m_pSortProxy = new CSortFilterProxyModel(false, this); + m_pSortProxy->setSortRole(Qt::EditRole); + m_pSortProxy->setSourceModel(m_pBoxModel); + m_pSortProxy->setDynamicSortFilter(true); + + m_pBoxTree = new QTreeViewEx(); + //m_pBoxTree->setItemDelegate(theGUI->GetItemDelegate()); + + m_pBoxTree->setModel(m_pSortProxy); + + m_pBoxTree->setSelectionMode(QAbstractItemView::ExtendedSelection); +#ifdef WIN32 + QStyle* pStyle = QStyleFactory::create("windows"); + m_pBoxTree->setStyle(pStyle); +#endif + m_pBoxTree->setSortingEnabled(true); + + m_pBoxTree->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_pBoxTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(OnMenu(const QPoint &))); + + //connect(theGUI, SIGNAL(ReloadPanels()), m_pWindowModel, SLOT(Clear())); + + m_pBoxTree->setColumnReset(2); + connect(m_pBoxTree, SIGNAL(ResetColumns()), this, SLOT(OnResetColumns())); + connect(m_pBoxTree, SIGNAL(ColumnChanged(int, bool)), this, SLOT(OnColumnsChanged())); + + //m_pSplitter->addWidget(CFinder::AddFinder(m_pBoxTree, m_pSortProxy)); + //m_pSplitter->setCollapsible(0, false); + // + + //connect(m_pBoxTree, SIGNAL(clicked(const QModelIndex&)), this, SLOT(OnItemSelected(const QModelIndex&))); + //connect(m_pBoxTree->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(OnItemSelected(QModelIndex))); + + m_pPanelSplitter->addWidget(m_pBoxTree); + */ + + m_pBoxView = new CSbieView(); + m_pPanelSplitter->addWidget(m_pBoxView); + + connect(m_pBoxView->GetTree()->selectionModel(), SIGNAL(currentChanged(QModelIndex, QModelIndex)), this, SLOT(OnSelectionChanged())); + + //m_pPanelSplitter->addWidget(); + + m_pLogTabs = new QTabWidget(); + m_pLogSplitter->addWidget(m_pLogTabs); + + // Message Log + m_pMessageLog = new CPanelWidgetEx(); + + //m_pMessageLog->GetView()->setItemDelegate(theGUI->GetItemDelegate()); + ((QTreeWidgetEx*)m_pMessageLog->GetView())->setHeaderLabels(tr("Time|Message").split("|")); + + m_pMessageLog->GetView()->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_pMessageLog->GetView()->setSortingEnabled(false); + + + m_pLogTabs->addTab(m_pMessageLog, tr("Sbie Messages")); + // + + // Res Log + m_pResMonModel = new CResMonModel(); + //m_pResMonModel->SetUseIcons(true); + + m_pResourceLog = new CPanelViewImpl(m_pResMonModel); + + //m_pResourceLog->GetView()->setItemDelegate(theGUI->GetItemDelegate()); + + m_pResourceLog->GetView()->setSelectionMode(QAbstractItemView::ExtendedSelection); + + m_pLogTabs->addTab(m_pResourceLog, tr("Resource Monitor")); + // + + // Api Log + m_pApiLog = new CPanelWidgetEx(); + + //m_pApiLog->GetView()->setItemDelegate(theGUI->GetItemDelegate()); + ((QTreeWidgetEx*)m_pApiLog->GetView())->setHeaderLabels(tr("Time|Entry").split("|")); + + m_pApiLog->GetView()->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_pApiLog->GetView()->setSortingEnabled(false); + + m_pLogTabs->addTab(m_pApiLog, tr("Api Call Log")); + m_pApiLog->setEnabled(false); + // + + m_pMenuFile = menuBar()->addMenu(tr("&Sandbox")); + m_pMenuNew = m_pMenuFile->addAction(QIcon(":/Actions/NewBox"), tr("Create New Box"), this, SLOT(OnNewBox())); + m_pMenuFile->addSeparator(); + m_pMenuEmptyAll = m_pMenuFile->addAction(QIcon(":/Actions/EmptyAll"), tr("Terminate All Processes"), this, SLOT(OnEmptyAll())); + m_pMenuFile->addSeparator(); + m_pMenuExit = m_pMenuFile->addAction(QIcon(":/Actions/Exit"), tr("Exit"), this, SLOT(OnExit())); + + + m_pMenuView = menuBar()->addMenu(tr("&View")); + m_pCleanUp = m_pMenuView->addAction(QIcon(":/Actions/Clean"), tr("Clean Up"), this, SLOT(OnCleanUp())); + m_pKeepTerminated = m_pMenuView->addAction(QIcon(":/Actions/Keep"), tr("Keep closed"), this, SLOT(OnSetKeep())); + m_pKeepTerminated->setCheckable(true); + + m_pMenuOptions = menuBar()->addMenu(tr("&Options")); + m_pEditIni = m_pMenuOptions->addAction(QIcon(":/Actions/EditIni"), tr("Edit ini file"), this, SLOT(OnEditIni())); + m_pReloadIni = m_pMenuOptions->addAction(QIcon(":/Actions/ReloadIni"), tr("Reload ini file"), this, SLOT(OnReloadIni())); + m_pMenuOptions->addSeparator(); + m_pEnableMonitoring = m_pMenuOptions->addAction(QIcon(":/Actions/SetLogging"), tr("Resource Logging"), this, SLOT(OnSetMonitoring())); + m_pEnableMonitoring->setCheckable(true); + m_pMenuOptions->addSeparator(); + m_pEnableLogging = m_pMenuOptions->addAction(QIcon(":/Actions/LogAPI"), tr("API Call Logging"), this, SLOT(OnSetLogging())); + m_pEnableLogging->setCheckable(true); + + + m_pMenuHelp = menuBar()->addMenu(tr("&Help")); + m_pMenuSupport = m_pMenuHelp->addAction(tr("Support Sandboxie-Plus on Patreon"), this, SLOT(OnAbout())); + m_pMenuHelp->addSeparator(); + m_pMenuAboutQt = m_pMenuHelp->addAction(tr("About the Qt Framework"), this, SLOT(OnAbout())); + //m_pMenuHelp->addSeparator(); + m_pMenuAbout = m_pMenuHelp->addAction(QIcon(":/SandMan.png"), tr("About Sandboxie-Plus"), this, SLOT(OnAbout())); + + + + //m_pToolBar->addAction(m_pMenuNew); + //m_pToolBar->addAction(m_pMenuEmptyAll); + //m_pToolBar->addSeparator(); + m_pToolBar->addAction(m_pKeepTerminated); + m_pToolBar->addAction(m_pCleanUp); + m_pToolBar->addSeparator(); + m_pToolBar->addAction(m_pEditIni); + m_pToolBar->addSeparator(); + m_pToolBar->addAction(m_pEnableMonitoring); + m_pToolBar->addAction(m_pEnableLogging); + m_pToolBar->addSeparator(); + + + /*QWidget* pSpacer = new QWidget(); + pSpacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + m_pToolBar->addWidget(pSpacer); + + //m_pToolBar->addAction(m_pMenuElevate); + + m_pToolBar->addSeparator(); + m_pToolBar->addWidget(new QLabel(" ")); + QLabel* pSupport = new QLabel("Support Sandboxie-Plus on Patreon"); + pSupport->setTextInteractionFlags(Qt::TextBrowserInteraction); + connect(pSupport, SIGNAL(linkActivated(const QString&)), this, SLOT(OnAbout())); + m_pToolBar->addWidget(pSupport); + m_pToolBar->addWidget(new QLabel(" "));*/ + + + + QIcon Icon; + Icon.addFile(":/SandMan.png"); + m_pTrayIcon = new QSystemTrayIcon(Icon, this); + m_pTrayIcon->setToolTip("Sandboxie-Plus"); + connect(m_pTrayIcon, SIGNAL(activated(QSystemTrayIcon::ActivationReason)), this, SLOT(OnSysTray(QSystemTrayIcon::ActivationReason))); + + m_pTrayMenu = new QMenu(); + m_pTrayMenu->addSeparator(); + m_pTrayMenu->addAction(m_pMenuExit); + + bool bAutoRun = QApplication::arguments().contains("-autorun"); + + m_pTrayIcon->show(); // Note: qt bug; without a first show hide does not work :/ + if(!bAutoRun && !theConf->GetBool("SysTray/Show", true)) + m_pTrayIcon->hide(); + + restoreGeometry(theConf->GetBlob("MainWindow/Window_Geometry")); + //m_pBoxTree->restoreState(theConf->GetBlob("GUI/BoxTree_Columns")); + m_pMessageLog->GetView()->header()->restoreState(theConf->GetBlob("GUI/LogList_Columns")); + QByteArray Columns = theConf->GetBlob("GUI/ResMonList_Columns"); + if (!Columns.isEmpty()) + ((QTreeViewEx*)m_pResourceLog->GetView())->OnResetColumns(); + else + ((QTreeViewEx*)m_pResourceLog->GetView())->restoreState(Columns); + m_pApiLog->GetView()->header()->restoreState(theConf->GetBlob("GUI/ApiLogList_Columns")); + m_pLogSplitter->restoreState(theConf->GetBlob("MainWindow/Log_Splitter")); + m_pPanelSplitter->restoreState(theConf->GetBlob("MainWindow/Panel_Splitter")); + m_pLogTabs->setCurrentIndex(theConf->GetInt("GUI/LogTab", 0)); + + + bool bIsMonitoring = theAPI->IsMonitoring(); + m_pResourceLog->setEnabled(bIsMonitoring); + m_pEnableMonitoring->setChecked(bIsMonitoring); + + m_pKeepTerminated->setChecked(theConf->GetBool("Options/pKeepTerminated")); + + // connect last so if there were unpurged messages they end up no ware + connect(theAPI, SIGNAL(LogMessage(const QString&)), this, SLOT(OnLogMessage(const QString&))); + + m_ApiLog = NULL; + + statusBar()->showMessage("Ready", 3000); + + m_uTimerID = startTimer(250); + +} + +CSandMan::~CSandMan() +{ + if(m_pEnableMonitoring->isChecked()) + theAPI->EnableMonitor(false); + + killTimer(m_uTimerID); + + m_pTrayIcon->hide(); + + theConf->SetBlob("MainWindow/Window_Geometry", saveGeometry()); + //theConf->SetBlob("GUI/BoxTree_Columns", m_pBoxTree->saveState()); + theConf->SetBlob("GUI/LogList_Columns", m_pMessageLog->GetView()->header()->saveState()); + theConf->SetBlob("GUI/ResMonList_Columns", m_pResourceLog->GetView()->header()->saveState()); + theConf->SetBlob("GUI/ApiLogList_Columns", m_pApiLog->GetView()->header()->saveState()); + theConf->SetBlob("MainWindow/Log_Splitter", m_pLogSplitter->saveState()); + theConf->SetBlob("MainWindow/Panel_Splitter", m_pPanelSplitter->saveState()); + theConf->SetValue("GUI/LogTab", m_pLogTabs->currentIndex()); + + theAPI = NULL; +} + +void CSandMan::OnExit() +{ + m_bExit = true; + close(); +} + +void CSandMan::closeEvent(QCloseEvent *e) +{ + if (!m_bExit) + { + QString OnClose = theConf->GetString("Options/OnClose", "ToTray"); + if (m_pTrayIcon->isVisible() && OnClose.compare("ToTray", Qt::CaseInsensitive) == 0) + { + hide(); + + e->ignore(); + return; + } + else if(OnClose.compare("Prompt", Qt::CaseInsensitive) == 0) + { + CExitDialog ExitDialog(tr("Do you want to close Sandboxie Manager?")); + if (!ExitDialog.exec()) + { + e->ignore(); + return; + } + } + } + + QApplication::quit(); +} + +void CSandMan::timerEvent(QTimerEvent* pEvent) +{ + if (pEvent->timerId() != m_uTimerID) + return; + + theAPI->ReloadBoxes(); + theAPI->UpdateProcesses(m_pKeepTerminated->isChecked()); + + m_pBoxView->Refresh(); + + /*QList Added = m_pBoxModel->Sync(theAPI->GetAllBoxes()); + + if (m_pBoxModel->IsTree()) + { + QTimer::singleShot(100, this, [this, Added]() { + foreach(const QVariant ID, Added) { + m_pBoxTree->expand(m_pSortProxy->mapFromSource(m_pBoxModel->FindIndex(ID))); + } + }); + }*/ + + OnSelectionChanged(); +} + +void CSandMan::OnSelectionChanged() +{ + QList Processes = m_pBoxView->GetSelectedProcesses(); + /*if (Processes.isEmpty()) + { + QListBoxes = m_pBoxView->GetSelectedBoxes(); + foreach(const CSandBoxPtr& pBox, Boxes) + Processes.append(pBox->GetProcessList().values()); + }*/ + + QSet Pids; + foreach(const CBoxedProcessPtr& pProcess, Processes) + { + Pids.insert(pProcess->GetProcessId()); + } + + QList ResourceLog = theAPI->GetResLog(); + + m_pResMonModel->Sync(ResourceLog, Pids); +} + +void CSandMan::OnLogMessage(const QString& Message) +{ + QTreeWidgetItem* pItem = new QTreeWidgetItem(); // Time|Message + pItem->setText(0, QDateTime::currentDateTime().toString("hh:mm:ss.zzz")); + pItem->setText(1, Message); + m_pMessageLog->GetTree()->addTopLevelItem(pItem); + + m_pMessageLog->GetView()->verticalScrollBar()->setValue(m_pMessageLog->GetView()->verticalScrollBar()->maximum()); + + statusBar()->showMessage(Message); +} + +void CSandMan::OnApiLogEntry(const QString& Message) +{ + QTreeWidgetItem* pItem = new QTreeWidgetItem(); // Time|Message + pItem->setText(0, QDateTime::currentDateTime().toString("hh:mm:ss.zzz")); + pItem->setText(1, Message); + m_pApiLog->GetTree()->addTopLevelItem(pItem); + + m_pApiLog->GetView()->verticalScrollBar()->setValue(m_pApiLog->GetView()->verticalScrollBar()->maximum()); +} +/* +void CSandMan::OnResetColumns() +{ + for (int i = 0; i < m_pBoxModel->columnCount(); i++) + m_pBoxTree->SetColumnHidden(i, false); +} + +void CSandMan::OnColumnsChanged() +{ + m_pBoxModel->Sync(theAPI->GetAllBoxes()); +} + +void CSandMan::OnMenu(const QPoint& Point) +{ +}*/ + +void CSandMan::OnNewBox() +{ + QString Value = QInputDialog::getText(this, "Sandboxie-Plus", "Please enter a name for the new Sandbox.", QLineEdit::Normal, "NewBox"); + if (Value.isEmpty()) + return; + theAPI->CreateBox(Value); +} + +void CSandMan::OnEmptyAll() +{ + theAPI->TerminateAll(); +} + +void CSandMan::OnCleanUp() +{ + theAPI->UpdateProcesses(false); +} + +void CSandMan::OnSetKeep() +{ + theConf->SetValue("Options/pKeepTerminated", m_pKeepTerminated->isChecked()); +} + +void CSandMan::OnEditIni() +{ + if (theConf->GetBool("Options/NoEditInfo", true)) + { + bool State = false; + CCheckableMessageBox::question(this, "Sandboxie-Plus", tr("The changes will be applyed automatically as soon as the editor is closed.") + , tr("Don't show this message again."), &State, QDialogButtonBox::Ok, QDialogButtonBox::Ok, QMessageBox::Information); + + if (State) + theConf->SetValue("Options/NoEditInfo", false); + } + + wstring IniPath = theAPI->GetIniPath().toStdWString(); + + SHELLEXECUTEINFO si = { 0 }; + si.cbSize = sizeof(SHELLEXECUTEINFO); + si.fMask = SEE_MASK_NOCLOSEPROCESS; + si.hwnd = NULL; + si.lpVerb = L"runas"; + si.lpFile = L"notepad.exe"; + si.lpParameters = IniPath.c_str(); + si.lpDirectory = NULL; + si.nShow = SW_SHOW; + si.hInstApp = NULL; + ShellExecuteEx(&si); + //WaitForSingleObject(si.hProcess, INFINITE); + //CloseHandle(si.hProcess); + + QWinEventNotifier* processFinishedNotifier = new QWinEventNotifier(si.hProcess); + processFinishedNotifier->setEnabled(true); + connect(processFinishedNotifier, &QWinEventNotifier::activated, this, [processFinishedNotifier, this, si]() { + processFinishedNotifier->setEnabled(false); + processFinishedNotifier->deleteLater(); + this->OnReloadIni(); + CloseHandle(si.hProcess); + }); +} + +void CSandMan::OnReloadIni() +{ + theAPI->ReloadConfig(); + statusBar()->showMessage(tr("Sandboxie config has been reloaded."), 3000); +} + +void CSandMan::OnSetMonitoring() +{ + theAPI->EnableMonitor(m_pEnableMonitoring->isChecked()); + m_pResourceLog->setEnabled(m_pEnableMonitoring->isChecked()); +} + +void CSandMan::OnSetLogging() +{ + if (m_pEnableLogging->isChecked()) + { + if (theConf->GetBool("Options/ApiLogInfo", true)) + { + QString Message = tr("To use API logging you must first set up the LogApiDll from https://github.com/sandboxie-plus/LogApiDll with one or more sand boxes.\n" + "Please download the latest release and set it up with the sandboxie.ini as instructed in the README.md of the project."); + + bool State = false; + CCheckableMessageBox::question(this, "Sandboxie-Plus", Message + , tr("Don't show this message again."), &State, QDialogButtonBox::Ok, QDialogButtonBox::Ok, QMessageBox::Information); + + if (State) + theConf->SetValue("Options/ApiLogInfo", false); + } + + if (!m_ApiLog) { + m_ApiLog = new CApiLog(); + connect(m_ApiLog, SIGNAL(ApiLogEntry(const QString&)), this, SLOT(OnApiLogEntry(const QString&))); + m_pApiLog->setEnabled(true); + } + } + else + { + if (m_ApiLog) { + m_pApiLog->setEnabled(false); + m_ApiLog->deleteLater(); + m_ApiLog = NULL; + } + } +} + +void CSandMan::CheckResults(QList Results) +{ + for (QList::iterator I = Results.begin(); I != Results.end(); ) + { + if (!I->IsError()) + I = Results.erase(I); + else + I++; + } + + if (Results.count() == 1) + QMessageBox::warning(NULL, tr("Sandboxie-Plus - Error"), Results[0].GetText()); + else if (Results.count() > 1) + { + CMultiErrorDialog Dialog(tr("Operation failed for %1 item(s).").arg(Results.size()), Results); + Dialog.exec(); + } +} + +void CSandMan::OnSysTray(QSystemTrayIcon::ActivationReason Reason) +{ + static bool TriggerSet = false; + static bool NullifyTrigger = false; + switch(Reason) + { + case QSystemTrayIcon::Context: + m_pTrayMenu->popup(QCursor::pos()); + break; + case QSystemTrayIcon::DoubleClick: + if (isVisible()) + { + if(TriggerSet) + NullifyTrigger = true; + hide(); + break; + } + show(); + case QSystemTrayIcon::Trigger: +#ifdef WIN32 + if (isVisible() && !TriggerSet) + { + TriggerSet = true; + QTimer::singleShot(100, [this]() { + TriggerSet = false; + if (NullifyTrigger) { + NullifyTrigger = false; + return; + } + setWindowState(Qt::WindowActive); + //WINDOWPLACEMENT placement = { sizeof(placement) }; + //GetWindowPlacement(PhMainWndHandle, &placement); + //if (placement.showCmd == SW_MINIMIZE || placement.showCmd == SW_SHOWMINIMIZED) + // ShowWindowAsync(PhMainWndHandle, SW_RESTORE); + //SetForegroundWindow(PhMainWndHandle); + } ); + } +#endif + break; + } +} + + +QString CSandMan::GetVersion() +{ + QString Version = QString::number(VERSION_MJR) + "." + QString::number(VERSION_MIN) //.rightJustified(2, '0') +#if VERSION_REV > 0 + + "." + QString::number(VERSION_REV) +#endif +#if VERSION_UPD > 0 + + QString('a' + VERSION_UPD - 1) +#endif + ; + return Version; +} + +void CSandMan::OnAbout() +{ + if (sender() == m_pMenuAbout) + { + QString AboutCaption = tr( + "

About Sandboxie-Plus

" + "

Version %1

" + "

by DavidXanatos

" + "

Copyright (c) 2020

" + ).arg(GetVersion()); + QString AboutText = tr( + "

Sandboxie-Plus is a powerfull sandboxing and application virtualization tool. Based on the well known Sandboxie.

" + "

" + "

Visit Sandboxie-Plus on github for more information.

" + "

" + "

" + "

" + "

Icons from icons8.com

" + "

" + ); + QMessageBox *msgBox = new QMessageBox(this); + msgBox->setAttribute(Qt::WA_DeleteOnClose); + msgBox->setWindowTitle(tr("About Sandboxie-Plus")); + msgBox->setText(AboutCaption); + msgBox->setInformativeText(AboutText); + + QIcon ico(QLatin1String(":/SandMan.png")); + msgBox->setIconPixmap(ico.pixmap(128, 128)); + + msgBox->exec(); + } + else if (sender() == m_pMenuAboutQt) + QMessageBox::aboutQt(this); + else + QDesktopServices::openUrl(QUrl("https://www.patreon.com/DavidXanatos")); +} + +////////////////////////////////////////////////////////////////////////////////////////// +// + +#include +#include + +#define RFF_NOBROWSE 0x0001 +#define RFF_NODEFAULT 0x0002 +#define RFF_CALCDIRECTORY 0x0004 +#define RFF_NOLABEL 0x0008 +#define RFF_NOSEPARATEMEM 0x0020 +#define RFF_OPTRUNAS 0x0040 + +#define RFN_VALIDATE (-510) +#define RFN_LIMITEDRUNAS (-511) + +#define RF_OK 0x0000 +#define RF_CANCEL 0x0001 +#define RF_RETRY 0x0002 + +typedef struct _NMRUNFILEDLGW +{ + NMHDR hdr; + PWSTR lpszFile; + PWSTR lpszDirectory; + UINT ShowCmd; +} NMRUNFILEDLGW, *LPNMRUNFILEDLGW, *PNMRUNFILEDLGW; + +QString g_RunDialogCommand; + +BOOLEAN OnWM_Notify(NMHDR *Header, LRESULT *Result) +{ + LPNMRUNFILEDLGW runFileDlg = (LPNMRUNFILEDLGW)Header; + if (Header->code == RFN_VALIDATE) + { + g_RunDialogCommand = QString::fromWCharArray(runFileDlg->lpszFile); + + *Result = RF_CANCEL; + return TRUE; + } + /*else if (Header->code == RFN_LIMITEDRUNAS) + { + + }*/ + return FALSE; +} + +extern "C" +{ + NTSYSCALLAPI NTSTATUS NTAPI LdrGetProcedureAddress(IN PVOID DllHandle, IN VOID* /*PANSI_STRING*/ ProcedureName OPTIONAL, IN ULONG ProcedureNumber OPTIONAL, OUT PVOID *ProcedureAddress, IN BOOLEAN RunInitRoutines); + //NTSTATUS(NTAPI *LdrGetProcedureAddress)(HMODULE ModuleHandle, PANSI_STRING FunctionName, WORD Oridinal, PVOID *FunctionAddress); +} + +BOOLEAN NTAPI ShowRunFileDialog(HWND WindowHandle, HICON WindowIcon, LPCWSTR WorkingDirectory, LPCWSTR WindowTitle, LPCWSTR WindowDescription, ULONG Flags) +{ + typedef BOOL(WINAPI *RunFileDlg_I)(HWND hwndOwner, HICON hIcon, LPCWSTR lpszDirectory, LPCWSTR lpszTitle, LPCWSTR lpszDescription, ULONG uFlags); + + BOOLEAN result = FALSE; + + if (HMODULE shell32Handle = LoadLibrary(L"shell32.dll")) + { + RunFileDlg_I dialog = NULL; + if (LdrGetProcedureAddress(shell32Handle, NULL, 61, (void**)&dialog, TRUE) == 0 /*STATUS_SUCCESS*/) + result = !!dialog(WindowHandle, WindowIcon, WorkingDirectory, WindowTitle, WindowDescription, Flags); + + FreeLibrary(shell32Handle); + } + + return result; +} + +QString ShowRunDialog(const QString& BoxName) +{ + g_RunDialogCommand.clear(); + wstring boxName = BoxName.toStdWString(); + ShowRunFileDialog(MainWndHandle, NULL, NULL, boxName.c_str(), L"Enter the path of a program that will be created in a sandbox.", 0); // RFF_OPTRUNAS); + return g_RunDialogCommand; +} \ No newline at end of file diff --git a/SandboxiePlus/SandMan/SandMan.h b/SandboxiePlus/SandMan/SandMan.h new file mode 100644 index 00000000..22ff9997 --- /dev/null +++ b/SandboxiePlus/SandMan/SandMan.h @@ -0,0 +1,115 @@ +#pragma once + +#include + +#include "../MiscHelpers/Common/Settings.h" +#include "../MiscHelpers/Common/TreeViewEx.h" +#include "../MiscHelpers/Common/PanelView.h" +#include "Models/ResMonModel.h" + +#define VERSION_MJR 0 +#define VERSION_MIN 1 +#define VERSION_REV 0 +#define VERSION_UPD 0 + + +#include "../QSbieAPI/SbieAPI.h" + +class CSbieView; +class CApiLog; + +class CSandMan : public QMainWindow +{ + Q_OBJECT + +public: + CSandMan(QWidget *parent = Q_NULLPTR); + virtual ~CSandMan(); + + static QString GetVersion(); + + static void CheckResults(QList Results); + +protected: + void closeEvent(QCloseEvent *e); + void timerEvent(QTimerEvent* pEvent); + int m_uTimerID; + + CApiLog* m_ApiLog; + +public slots: + void OnLogMessage(const QString& Message); + void OnApiLogEntry(const QString& Message); + +private slots: + void OnSelectionChanged(); + + //void OnResetColumns(); + //void OnColumnsChanged(); + //void OnMenu(const QPoint& Point); + + void OnNewBox(); + void OnEmptyAll(); + + void OnCleanUp(); + void OnSetKeep(); + + void OnEditIni(); + void OnReloadIni(); + void OnSetMonitoring(); + void OnSetLogging(); + + void OnExit(); + void OnAbout(); + + void OnSysTray(QSystemTrayIcon::ActivationReason Reason); + +private: + QWidget* m_pMainWidget; + QVBoxLayout* m_pMainLayout; + + QToolBar* m_pToolBar; + + QSplitter* m_pPanelSplitter; + + QSplitter* m_pLogSplitter; + + //QTreeViewEx* m_pBoxTree; + //CSbieModel* m_pBoxModel; + //QSortFilterProxyModel* m_pSortProxy; + CSbieView* m_pBoxView; + + + QTabWidget* m_pLogTabs; + + CPanelWidgetEx* m_pMessageLog; + CPanelViewImpl* m_pResourceLog; + CResMonModel* m_pResMonModel; + CPanelWidgetEx* m_pApiLog; + + + QMenu* m_pMenuFile; + QAction* m_pMenuNew; + QAction* m_pMenuEmptyAll; + QAction* m_pMenuExit; + + QMenu* m_pMenuView; + QAction* m_pCleanUp; + QAction* m_pKeepTerminated; + + QMenu* m_pMenuOptions; + QAction* m_pEditIni; + QAction* m_pReloadIni; + QAction* m_pEnableMonitoring; + QAction* m_pEnableLogging; + + QMenu* m_pMenuHelp; + QAction* m_pMenuAbout; + QAction* m_pMenuSupport; + QAction* m_pMenuAboutQt; + + QSystemTrayIcon* m_pTrayIcon; + QMenu* m_pTrayMenu; + + bool m_bExit; +}; diff --git a/SandboxiePlus/SandMan/SandMan.vcxproj b/SandboxiePlus/SandMan/SandMan.vcxproj new file mode 100644 index 00000000..51cb71f6 --- /dev/null +++ b/SandboxiePlus/SandMan/SandMan.vcxproj @@ -0,0 +1,226 @@ + + + + + Debug + Win32 + + + Debug + x64 + + + Release + Win32 + + + Release + x64 + + + + {B7A8576D-A08A-4A97-84E8-013DAF4D4F1F} + QtVS_v302 + 8.1 + $(MSBuildProjectDirectory)\QtMsBuild + + + + Application + v140 + false + + + Application + v140 + false + + + Application + v140 + false + + + Application + v140 + false + + + + + + + + + + + + + + + + + + + + + + $(SolutionDir)$(Platform)\$(Configuration)\;$(LibraryPath) + + + $(SolutionDir)$(Platform)\$(Configuration)\;$(LibraryPath) + + + + + + msvc2015_64 + core;gui;network;widgets;winextras + + + msvc2015_64 + core;gui;network;widgets;winextras + + + msvc2015_64 + core;gui;network;widgets;winextras + + + msvc2015_64 + core;gui;network;widgets;winextras + + + + + + + stdafx.h + + + true + Disabled + ProgramDatabase + MultiThreadedDebugDLL + true + Use + stdafx.h + $(IntDir)$(TargetName).pch + .;%(AdditionalIncludeDirectories) + + + Windows + $(OutDir)\$(ProjectName).exe + true + QSbieAPI.lib;MiscHelpers.lib;ntdll.lib;%(AdditionalDependencies) + + + + + stdafx.h + + + true + Disabled + ProgramDatabase + MultiThreadedDebugDLL + true + Use + stdafx.h + $(IntDir)$(TargetName).pch + UNICODE;_UNICODE;WIN32;WIN64;%(PreprocessorDefinitions) + .;%(AdditionalIncludeDirectories) + + + Windows + $(OutDir)\$(ProjectName).exe + true + MachineX86 + /SUBSYSTEM:WINDOWS + + + + + stdafx.h + + + true + + MultiThreadedDLL + true + Use + stdafx.h + $(IntDir)$(TargetName).pch + .;%(AdditionalIncludeDirectories) + + + Windows + $(OutDir)\$(ProjectName).exe + false + QSbieAPI.lib;MiscHelpers.lib;ntdll.lib;%(AdditionalDependencies) + + + + + stdafx.h + + + true + + + MultiThreadedDLL + true + Use + stdafx.h + $(IntDir)$(TargetName).pch + UNICODE;_UNICODE;WIN32;WIN64;%(PreprocessorDefinitions) + .;%(AdditionalIncludeDirectories) + + + Windows + $(OutDir)\$(ProjectName).exe + false + MachineX86 + /SUBSYSTEM:WINDOWS + + + + + + + + + + + Create + Create + Create + Create + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/SandboxiePlus/SandMan/SandMan.vcxproj.filters b/SandboxiePlus/SandMan/SandMan.vcxproj.filters new file mode 100644 index 00000000..5ad8c298 --- /dev/null +++ b/SandboxiePlus/SandMan/SandMan.vcxproj.filters @@ -0,0 +1,108 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;hm;inl;inc;xsd + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {99349809-55BA-4b9d-BF79-8FDBB0286EB3} + ui + + + {D9D6E242-F8AF-46E4-B9FD-80ECBC20BA3E} + qrc;* + false + + + {66baf3d2-2745-4dfe-a2a8-b3a6e1b9edca} + + + {4cb3f267-32bd-4389-b61a-47e231070586} + + + {34eca1e5-cd50-4876-9f54-9eec4c393150} + + + {6accf3ae-da17-4c0f-ba83-214e3874b029} + + + + + Source Files + + + Source Files + + + SandMan + + + Models + + + Views + + + Models + + + SandMan + + + Dialogs + + + + + Header Files + + + Resource Files + + + + + SandMan + + + Models + + + Views + + + Models + + + SandMan + + + Dialogs + + + + + Resource Files + + + + + Resource Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/SandboxiePlus/SandMan/SandMan.vcxproj.user b/SandboxiePlus/SandMan/SandMan.vcxproj.user new file mode 100644 index 00000000..6e2aec7a --- /dev/null +++ b/SandboxiePlus/SandMan/SandMan.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/SandboxiePlus/SandMan/Views/SbieView.cpp b/SandboxiePlus/SandMan/Views/SbieView.cpp new file mode 100644 index 00000000..a9bfe848 --- /dev/null +++ b/SandboxiePlus/SandMan/Views/SbieView.cpp @@ -0,0 +1,250 @@ +#include "stdafx.h" +#include "SbieView.h" +#include "..\SandMan.h" +#include "../QSbieAPI/SbieAPI.h" +#include "../../MiscHelpers/Common/SortFilterProxyModel.h" +#include "../../MiscHelpers/Common/Settings.h" + +CSbieView::CSbieView(QWidget* parent) : CPanelView(parent) +{ + m_pMainLayout = new QVBoxLayout(); + m_pMainLayout->setMargin(0); + this->setLayout(m_pMainLayout); + + m_pSbieModel = new CSbieModel(); + m_pSbieModel->SetTree(true); + m_pSbieModel->SetUseIcons(true); + + m_pSortProxy = new CSortFilterProxyModel(false, this); + m_pSortProxy->setSortRole(Qt::EditRole); + m_pSortProxy->setSourceModel(m_pSbieModel); + m_pSortProxy->setDynamicSortFilter(true); + + + // SbieTree + m_pSbieTree = new QTreeViewEx(); + //m_pSbieTree->setItemDelegate(theGUI->GetItemDelegate()); + + m_pSbieTree->setModel(m_pSortProxy); + + m_pSbieTree->setSelectionMode(QAbstractItemView::ExtendedSelection); + m_pSbieTree->setSortingEnabled(true); + + m_pSbieTree->setContextMenuPolicy(Qt::CustomContextMenu); + connect(m_pSbieTree, SIGNAL(customContextMenuRequested(const QPoint&)), this, SLOT(OnMenu(const QPoint &))); + connect(m_pSbieTree->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), SLOT(ProcessSelection(QItemSelection, QItemSelection))); + + //connect(theGUI, SIGNAL(ReloadPanels()), m_pSbieModel, SLOT(Clear())); + + m_pMainLayout->addWidget(m_pSbieTree); + // + + m_pMainLayout->addWidget(new CFinder(m_pSortProxy, this)); + + m_pMenuRun = m_pMenu->addMenu(tr("Run")); + m_pMenuRunAny = m_pMenuRun->addAction(tr("Run Program"), this, SLOT(OnSandBoxAction())); + m_pMenuRunBrowser = m_pMenuRun->addAction(tr("Run Web Browser"), this, SLOT(OnSandBoxAction())); + m_pMenuRunExplorer = m_pMenuRun->addAction(tr("Run Explorer"), this, SLOT(OnSandBoxAction())); + m_pMenuRunCmd = m_pMenuRun->addAction(tr("Run Cmd.exe"), this, SLOT(OnSandBoxAction())); + m_pMenuEmptyBox = m_pMenu->addAction(tr("Terminate All Programs"), this, SLOT(OnSandBoxAction())); + m_pMenu->addSeparator(); + m_pMenuCleanUp = m_pMenu->addAction(tr("Delete Content"), this, SLOT(OnSandBoxAction())); + m_pMenu->addSeparator(); + m_pMenuRename = m_pMenu->addAction(tr("Rename Sadbox"), this, SLOT(OnSandBoxAction())); + m_pMenuRemove = m_pMenu->addAction(tr("Remove Sandbox"), this, SLOT(OnSandBoxAction())); + m_iMenuBox = m_pMenu->actions().count(); + + m_pMenuTerminate = m_pMenu->addAction(tr("Terminate"), this, SLOT(OnProcessAction())); + m_pMenuSuspend = m_pMenu->addAction(tr("Suspend"), this, SLOT(OnProcessAction())); + m_pMenuResume = m_pMenu->addAction(tr("Resume"), this, SLOT(OnProcessAction())); + m_iMenuProc = m_pMenu->actions().count(); + + QByteArray Columns = theConf->GetBlob("GUI/BoxTree_Columns"); + if (Columns.isEmpty()) + m_pSbieTree->OnResetColumns(); + else + m_pSbieTree->restoreState(Columns); + + //m_pMenu = new QMenu(); + AddPanelItemsToMenu(); +} + +CSbieView::~CSbieView() +{ + theConf->SetBlob("GUI/BoxTree_Columns", m_pSbieTree->saveState()); +} + +void CSbieView::Refresh() +{ + QList Added = m_pSbieModel->Sync(theAPI->GetAllBoxes()); + + if (m_pSbieModel->IsTree()) + { + QTimer::singleShot(100, this, [this, Added]() { + foreach(const QVariant ID, Added) { + m_pSbieTree->expand(m_pSortProxy->mapFromSource(m_pSbieModel->FindIndex(ID))); + } + }); + } +} + +void CSbieView::OnMenu(const QPoint& Point) +{ + int iProcessCount = 0; + int iSandBoxeCount = 0; + int iSuspendedCount = 0; + foreach(const QModelIndex& Index, m_pSbieTree->selectedRows()) + { + QModelIndex ModelIndex = m_pSortProxy->mapToSource(Index); + CBoxedProcessPtr pProcess = m_pSbieModel->GetProcess(ModelIndex); + if (pProcess) + { + iProcessCount++; + if (pProcess->IsSuspended()) + iSuspendedCount++; + } + else + { + CSandBoxPtr pBox = m_pSbieModel->GetSandBox(ModelIndex); + if (pBox) + iSandBoxeCount++; + } + } + + QList MenuActions = m_pMenu->actions(); + + for (int i = 0; i < m_iMenuBox; i++) + MenuActions[i]->setVisible(iSandBoxeCount > 0 && iProcessCount == 0); + m_pMenuRun->setEnabled(iSandBoxeCount == 1); + m_pMenuRename->setEnabled(iSandBoxeCount == 1); + + for (int i = m_iMenuBox; i < m_iMenuProc; i++) + MenuActions[i]->setVisible(iProcessCount > 0 && iSandBoxeCount == 0); + m_pMenuSuspend->setEnabled(iProcessCount > iSuspendedCount); + m_pMenuResume->setEnabled(iSuspendedCount > 0); + + CPanelView::OnMenu(Point); +} + +QString ShowRunDialog(const QString& BoxName); + +void CSbieView::OnSandBoxAction() +{ + QList Results; + + QAction* Action = qobject_cast(sender()); + QList SandBoxes = CSbieView::GetSelectedBoxes(); + if (SandBoxes.isEmpty()) + return; + if (Action == m_pMenuRunAny) + { + /*QString Command = ShowRunDialog(SandBoxes.first()->GetName()); + if(!Command.isEmpty()) + SandBoxes.first()->RunCommand(Command);*/ + + Results.append(SandBoxes.first()->RunStart("run_dialog")); + } + else if (Action == m_pMenuRunBrowser) + Results.append(SandBoxes.first()->RunStart("default_browser")); + else if (Action == m_pMenuRunExplorer) + Results.append(SandBoxes.first()->RunCommand("explorer.exe /e,::{20D04FE0-3AEA-1069-A2D8-08002B30309D}")); + else if (Action == m_pMenuRunCmd) + Results.append(SandBoxes.first()->RunCommand("cmd.exe")); + else if (Action == m_pMenuRename) + { + QString Value = QInputDialog::getText(this, "Sandboxie-Plus", "Please enter a new name for the Sandbox.", QLineEdit::Normal, SandBoxes.first()->GetName()); + if (Value.isEmpty() || Value == SandBoxes.first()->GetName()) + return; + Results.append((SandBoxes.first()->RenameBox(Value))); + } + else if (Action == m_pMenuRemove) + { + if (QMessageBox("Sandboxie-Plus", tr("Do you really want remove the sellected sandboxes?"), QMessageBox::Warning, QMessageBox::Yes, QMessageBox::No | QMessageBox::Default | QMessageBox::Escape, QMessageBox::NoButton).exec() != QMessageBox::Yes) + return; + + foreach(const CSandBoxPtr& pBox, SandBoxes) + Results.append(pBox->RemoveBox()); + } + else if (Action == m_pMenuCleanUp) + { + if (QMessageBox("Sandboxie-Plus", tr("Do you really want delete teh content of the sellected sandboxes?"), QMessageBox::Warning, QMessageBox::Yes, QMessageBox::No | QMessageBox::Default | QMessageBox::Escape, QMessageBox::NoButton).exec() != QMessageBox::Yes) + return; + + foreach(const CSandBoxPtr& pBox, SandBoxes) + Results.append(pBox->CleanBox()); + } + else if (Action == m_pMenuEmptyBox) + { + foreach(const CSandBoxPtr& pBox, SandBoxes) + Results.append(pBox->TerminateAll()); + } + + CSandMan::CheckResults(Results); +} + +void CSbieView::OnProcessAction() +{ + QAction* Action = qobject_cast(sender()); + foreach(const CBoxedProcessPtr& pProcess, CSbieView::GetSelectedProcesses()) + { + if (Action == m_pMenuTerminate) + pProcess->Terminate(); + else if (Action == m_pMenuSuspend) + pProcess->SetSuspend(true); + else if (Action == m_pMenuResume) + pProcess->SetSuspend(false); + } +} + +void CSbieView::ProcessSelection(const QItemSelection& selected, const QItemSelection& deselected) +{ + if (selected.empty()) + return; + + QItemSelectionModel* selectionModel = m_pSbieTree->selectionModel(); + + QItemSelection selection = selectionModel->selection(); + QModelIndex root_parent = m_pSbieTree->currentIndex().parent(); + while (root_parent.isValid() && root_parent.parent().isValid()) + root_parent = root_parent.parent(); + + QItemSelection invalid; + foreach(const QModelIndex& index, selection.indexes()) + { + QModelIndex parent = index.parent(); + while (parent.isValid() && parent.parent().isValid()) + parent = parent.parent(); + + if (parent != root_parent) + invalid.select(index, index); + } + selectionModel->select(invalid, QItemSelectionModel::Deselect); +} + +QList CSbieView::GetSelectedBoxes() +{ + QList List; + foreach(const QModelIndex& Index, m_pSbieTree->selectedRows()) + { + QModelIndex ModelIndex = m_pSortProxy->mapToSource(Index); + CSandBoxPtr pBox = m_pSbieModel->GetSandBox(ModelIndex); + if (!pBox) + continue; + List.append(pBox); + } + return List; +} + +QList CSbieView::GetSelectedProcesses() +{ + QList List; + foreach(const QModelIndex& Index, m_pSbieTree->selectedRows()) + { + QModelIndex ModelIndex = m_pSortProxy->mapToSource(Index); + CBoxedProcessPtr pProcess = m_pSbieModel->GetProcess(ModelIndex); + if (!pProcess) + return QList < CBoxedProcessPtr>(); + List.append(pProcess); + } + return List; +} \ No newline at end of file diff --git a/SandboxiePlus/SandMan/Views/SbieView.h b/SandboxiePlus/SandMan/Views/SbieView.h new file mode 100644 index 00000000..bcb1b625 --- /dev/null +++ b/SandboxiePlus/SandMan/Views/SbieView.h @@ -0,0 +1,55 @@ + +#include "../../MiscHelpers/Common/PanelView.h" +#include "../../MiscHelpers/Common/TreeviewEx.h" +#include "../Models/SbieModel.h" + +class CSbieView : public CPanelView +{ + Q_OBJECT +public: + CSbieView(QWidget* parent = 0); + virtual ~CSbieView(); + + virtual QTreeViewEx* GetTree() { return m_pSbieTree; } + + virtual QList GetSelectedBoxes(); + virtual QList GetSelectedProcesses(); + +public slots: + void Refresh(); + +private slots: + void ProcessSelection(const QItemSelection& selected, const QItemSelection& deselected); + + void OnSandBoxAction(); + void OnProcessAction(); + +protected: + virtual void OnMenu(const QPoint& Point); + virtual QTreeView* GetView() { return m_pSbieTree; } + virtual QAbstractItemModel* GetModel() { return m_pSortProxy; } + +private: + + QVBoxLayout* m_pMainLayout; + + QTreeViewEx* m_pSbieTree; + CSbieModel* m_pSbieModel; + QSortFilterProxyModel* m_pSortProxy; + + + QMenu* m_pMenuRun; + QAction* m_pMenuRunAny; + QAction* m_pMenuRunBrowser; + QAction* m_pMenuRunExplorer; + QAction* m_pMenuRunCmd; + QAction* m_pMenuEmptyBox; + QAction* m_pMenuCleanUp; + QAction* m_pMenuRemove; + QAction* m_pMenuRename; + int m_iMenuBox; + QAction* m_pMenuTerminate; + QAction* m_pMenuSuspend; + QAction* m_pMenuResume; + int m_iMenuProc; +}; \ No newline at end of file diff --git a/SandboxiePlus/SandMan/main.cpp b/SandboxiePlus/SandMan/main.cpp new file mode 100644 index 00000000..154952f5 --- /dev/null +++ b/SandboxiePlus/SandMan/main.cpp @@ -0,0 +1,27 @@ +#include "stdafx.h" +#include "SandMan.h" +#include +#include "../QSbieAPI/SbieAPI.h" + +CSettings* theConf = NULL; + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + + theConf = new CSettings("Sandboxie-Plus"); + + //QThreadPool::globalInstance()->setMaxThreadCount(theConf->GetInt("Options/MaxThreadPool", 10)); + + CSandMan* pWnd = new CSandMan(); + pWnd->show(); + + int ret = app.exec(); + + delete pWnd; + + delete theConf; + theConf = NULL; + + return ret; +} diff --git a/SandboxiePlus/SandMan/stdafx.cpp b/SandboxiePlus/SandMan/stdafx.cpp new file mode 100644 index 00000000..a27b824d --- /dev/null +++ b/SandboxiePlus/SandMan/stdafx.cpp @@ -0,0 +1 @@ +#include "stdafx.h" diff --git a/SandboxiePlus/SandMan/stdafx.h b/SandboxiePlus/SandMan/stdafx.h new file mode 100644 index 00000000..99e826a6 --- /dev/null +++ b/SandboxiePlus/SandMan/stdafx.h @@ -0,0 +1,136 @@ +#pragma once + +#define _CRT_SECURE_NO_WARNINGS + + + +// std includes +#include +#include +#include +#include +#include +#include +#include +#include + + +using namespace std; + +// Qt includes +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// other includes + +#define _T(x) L ## x + +#define STR2(X) #X +#define STR(X) STR2(X) + +#define ARRSIZE(x) (sizeof(x)/sizeof(x[0])) + +#ifndef Max +#define Max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +#ifndef Min +#define Min(a,b) (((a) < (b)) ? (a) : (b)) +#endif + +#ifdef _DEBUG +#define SAFE_MODE +#endif + +#include "../MiscHelpers/Common/DebugHelpers.h" + +#define USE_QEXTWIDGETS + +extern class CSettings* theConf; +extern class CSbieAPI* theAPI; \ No newline at end of file diff --git a/SandboxiePlus/SandboxiePlus.sln b/SandboxiePlus/SandboxiePlus.sln new file mode 100644 index 00000000..40533b8a --- /dev/null +++ b/SandboxiePlus/SandboxiePlus.sln @@ -0,0 +1,58 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.28307.1022 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SandMan", "SandMan\SandMan.vcxproj", "{B7A8576D-A08A-4A97-84E8-013DAF4D4F1F}" + ProjectSection(ProjectDependencies) = postProject + {7AB8215A-59A4-4B8B-8090-16C87A860429} = {7AB8215A-59A4-4B8B-8090-16C87A860429} + {1433EC85-BDA4-402E-BEC1-48611206A64A} = {1433EC85-BDA4-402E-BEC1-48611206A64A} + EndProjectSection +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "QSbieAPI", "QSbieAPI\QSbieAPI.vcxproj", "{1433EC85-BDA4-402E-BEC1-48611206A64A}" +EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MiscHelpers", "MiscHelpers\MiscHelpers.vcxproj", "{7AB8215A-59A4-4B8B-8090-16C87A860429}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D28BB96E-F953-4FDF-818F-0CC48A5499FB}" + ProjectSection(SolutionItems) = preProject + ..\CHANGELOG.md = ..\CHANGELOG.md + ..\TODO.md = ..\TODO.md + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 + Release|x64 = Release|x64 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {B7A8576D-A08A-4A97-84E8-013DAF4D4F1F}.Debug|x64.ActiveCfg = Debug|x64 + {B7A8576D-A08A-4A97-84E8-013DAF4D4F1F}.Debug|x64.Build.0 = Debug|x64 + {B7A8576D-A08A-4A97-84E8-013DAF4D4F1F}.Debug|x86.ActiveCfg = Debug|Win32 + {B7A8576D-A08A-4A97-84E8-013DAF4D4F1F}.Debug|x86.Build.0 = Debug|Win32 + {B7A8576D-A08A-4A97-84E8-013DAF4D4F1F}.Release|x64.ActiveCfg = Release|x64 + {B7A8576D-A08A-4A97-84E8-013DAF4D4F1F}.Release|x64.Build.0 = Release|x64 + {B7A8576D-A08A-4A97-84E8-013DAF4D4F1F}.Release|x86.ActiveCfg = Release|Win32 + {B7A8576D-A08A-4A97-84E8-013DAF4D4F1F}.Release|x86.Build.0 = Release|Win32 + {1433EC85-BDA4-402E-BEC1-48611206A64A}.Debug|x64.ActiveCfg = Debug|x64 + {1433EC85-BDA4-402E-BEC1-48611206A64A}.Debug|x64.Build.0 = Debug|x64 + {1433EC85-BDA4-402E-BEC1-48611206A64A}.Debug|x86.ActiveCfg = Debug|x64 + {1433EC85-BDA4-402E-BEC1-48611206A64A}.Release|x64.ActiveCfg = Release|x64 + {1433EC85-BDA4-402E-BEC1-48611206A64A}.Release|x64.Build.0 = Release|x64 + {1433EC85-BDA4-402E-BEC1-48611206A64A}.Release|x86.ActiveCfg = Release|x64 + {7AB8215A-59A4-4B8B-8090-16C87A860429}.Debug|x64.ActiveCfg = Debug|x64 + {7AB8215A-59A4-4B8B-8090-16C87A860429}.Debug|x64.Build.0 = Debug|x64 + {7AB8215A-59A4-4B8B-8090-16C87A860429}.Debug|x86.ActiveCfg = Debug|x64 + {7AB8215A-59A4-4B8B-8090-16C87A860429}.Release|x64.ActiveCfg = Release|x64 + {7AB8215A-59A4-4B8B-8090-16C87A860429}.Release|x64.Build.0 = Release|x64 + {7AB8215A-59A4-4B8B-8090-16C87A860429}.Release|x86.ActiveCfg = Release|x64 + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {1DAAF42A-43C6-40CB-848F-DF67122CD602} + Qt5Version = msvc2015_64 + EndGlobalSection +EndGlobal