2021-10-15 16:39:43 +01:00
# include "stdafx.h"
# include "TraceView.h"
# include "..\SandMan.h"
2023-07-01 17:54:53 +01:00
# include "..\AddonManager.h"
2021-10-15 16:39:43 +01:00
# include "../QSbieAPI/SbieAPI.h"
# include "..\Models\TraceModel.h"
# include "..\..\MiscHelpers\Common\Common.h"
2021-10-30 08:46:49 +01:00
# include "..\..\MiscHelpers\Common\CheckList.h"
2021-10-15 16:39:43 +01:00
# include "SbieView.h"
2023-07-01 17:54:53 +01:00
# include <QtConcurrent>
2021-10-15 16:39:43 +01:00
//class CTraceFilterProxyModel : public CSortFilterProxyModel
//{
//public:
2022-12-07 16:32:40 +00:00
// CTraceFilterProxyModel(QObject* parent = 0) : CSortFilterProxyModel(parent)
2021-10-15 16:39:43 +01:00
// {
// m_FilterPid = 0;
// m_FilterTid = 0;
// }
//
// bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const
// {
// CTraceModel* pTraceModel = (CTraceModel*)sourceModel();
//
// QModelIndex index = pTraceModel->index(source_row, 0, source_parent);
// //CTraceEntryPtr pEntry = pTraceModel->GetEntry(index);
// //if (pEntry.data() == NULL)
// {
// QVariant Id = pTraceModel->GetItemID(index);
// StrPair typeId = Split2(Id.toString(), "_");
//
// if (m_FilterPid != 0 && typeId.first == "pid") {
// if (m_FilterPid != typeId.second.toUInt())
// return false;
// }
//
// if (m_FilterTid != 0 && typeId.first == "tid") {
// if (m_FilterTid != typeId.second.toUInt())
// return false;
// }
// }
//
// return CSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
// }
//
// quint32 m_FilterPid;
// quint32 m_FilterTid;
//};
2022-03-26 09:09:24 +00:00
////////////////////////////////////////////////////////////////////////////////////////
// CTraceTree
CTraceTree : : CTraceTree ( QWidget * parent )
2023-05-27 08:03:42 +01:00
: CPanelView ( parent )
2022-03-26 09:09:24 +00:00
{
2023-05-27 08:03:42 +01:00
m_pMainLayout = new QVBoxLayout ( ) ;
m_pMainLayout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
this - > setLayout ( m_pMainLayout ) ;
m_pSplitter = new QSplitter ( ) ;
m_pSplitter - > setOrientation ( Qt : : Horizontal ) ;
m_pMainLayout - > addWidget ( m_pSplitter ) ;
m_pTreeList = new QTreeViewEx ( ) ;
m_pTreeList - > setColumnFixed ( 0 , true ) ;
m_pTreeList - > setColumnFixed ( 1 , true ) ;
m_pTreeList - > setColumnFixed ( 2 , true ) ;
m_pTreeList - > setColumnFixed ( 3 , true ) ;
m_pTreeList - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
connect ( m_pTreeList , SIGNAL ( customContextMenuRequested ( const QPoint & ) ) , this , SLOT ( OnMenu ( const QPoint & ) ) ) ;
//m_pSplitter->addWidget(m_pTreeList);
m_pTreeList - > setMinimumHeight ( 50 ) ;
AddPanelItemsToMenu ( ) ;
2023-01-12 22:10:50 +00:00
m_bHighLight = false ;
//m_FilterCol = -1;
2022-07-29 09:24:32 +01:00
m_pTreeList - > setAlternatingRowColors ( theConf - > GetBool ( " Options/AltRowColors " , false ) ) ;
2022-03-26 09:09:24 +00:00
m_pTreeList - > setSelectionMode ( QAbstractItemView : : ExtendedSelection ) ;
2023-01-07 15:57:55 +00:00
m_pTreeList - > setUniformRowHeights ( true ) ; // critical for good performance with huge data sets
2022-03-26 09:09:24 +00:00
2023-01-07 15:57:55 +00:00
m_pTraceModel = new CTraceModel ( this ) ;
2022-03-26 09:09:24 +00:00
//connect(m_pTraceModel, SIGNAL(NewBranche()), this, SLOT(UpdateFilters()));
//m_pSortProxy = new CTraceFilterProxyModel(this);
//m_pSortProxy->setSortRole(Qt::EditRole);
//m_pSortProxy->setSourceModel(m_pTraceModel);
//m_pSortProxy->setDynamicSortFilter(true);
//m_pTreeList->setModel(m_pSortProxy);
//m_pSortProxy->setView(m_pTreeList);
m_pTreeList - > setModel ( m_pTraceModel ) ;
QStyle * pStyle = QStyleFactory : : create ( " windows " ) ;
m_pTreeList - > setStyle ( pStyle ) ;
2022-08-09 17:19:46 +01:00
m_pTreeList - > setItemDelegate ( new CTreeItemDelegate ( ) ) ;
2022-03-26 09:09:24 +00:00
m_pTreeList - > setExpandsOnDoubleClick ( false ) ;
//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()));
//m_pMainLayout->addWidget(CFinder::AddFinder(m_pTreeList, m_pSortProxy));
2023-05-27 08:03:42 +01:00
/*CFinder* pFinder = new CFinder(this, this, CFinder::eHighLightDefault);
pFinder - > SetTree ( m_pTreeList ) ;
m_pMainLayout - > addWidget ( pFinder ) ; */
2023-02-04 13:37:39 +00:00
CFinder * pFinder ;
2023-05-27 08:03:42 +01:00
//m_pMainLayout->addWidget(CFinder::AddFinder(m_pTreeList, this, CFinder::eHighLightDefault, &pFinder));
m_pSplitter - > addWidget ( CFinder : : AddFinder ( m_pTreeList , this , CFinder : : eHighLightDefault , & pFinder ) ) ;
2023-02-04 13:42:45 +00:00
pFinder - > SetModel ( m_pTraceModel ) ;
2023-01-12 22:10:50 +00:00
//QObject::connect(pFinder, SIGNAL(SelectNext()), this, SLOT(SelectNext()));
2022-03-26 09:09:24 +00:00
2023-05-27 08:03:42 +01:00
m_pStackView = new CStackView ( ) ;
m_pSplitter - > addWidget ( m_pStackView ) ;
connect ( m_pTreeList - > selectionModel ( ) , SIGNAL ( selectionChanged ( QItemSelection , QItemSelection ) ) , SLOT ( ItemSelection ( QItemSelection , QItemSelection ) ) ) ;
2022-03-26 09:09:24 +00:00
QByteArray Columns = theConf - > GetBlob ( " MainWindow/TraceLog_Columns " ) ;
if ( ! Columns . isEmpty ( ) )
( ( QTreeViewEx * ) GetView ( ) ) - > restoreState ( Columns ) ;
2023-05-27 08:03:42 +01:00
QByteArray Split = theConf - > GetBlob ( " MainWindow/TraceSplitter " ) ;
if ( ! Split . isEmpty ( ) )
m_pSplitter - > restoreState ( Split ) ;
2023-08-14 19:20:54 +01:00
//else { // by default collapse the details panel
2023-05-27 08:03:42 +01:00
// auto Sizes = m_pSplitter->sizes();
// Sizes[1] = 0;
// m_pSplitter->setSizes(Sizes);
//}
2022-03-26 09:09:24 +00:00
}
CTraceTree : : ~ CTraceTree ( )
{
theConf - > SetBlob ( " MainWindow/TraceLog_Columns " , GetView ( ) - > header ( ) - > saveState ( ) ) ;
2023-05-27 08:03:42 +01:00
theConf - > SetBlob ( " MainWindow/TraceSplitter " , m_pSplitter - > saveState ( ) ) ;
2022-03-26 09:09:24 +00:00
}
2023-01-12 22:10:50 +00:00
void CTraceTree : : SetFilter ( const QString & Exp , int iOptions , int Column )
{
2023-03-12 15:36:22 +00:00
bool bReset = m_bHighLight ! = ( ( iOptions & CFinder : : eHighLight ) ! = 0 ) | | ( ! m_bHighLight & & m_FilterExp ! = Exp ) ;
2023-01-12 22:10:50 +00:00
//QString ExpStr = ((iOptions & CFinder::eRegExp) == 0) ? Exp : (".*" + QRegularExpression::escape(Exp) + ".*");
//QRegularExpression RegExp(ExpStr, (iOptions & CFinder::eCaseSens) != 0 ? QRegularExpression::NoPatternOption : QRegularExpression::CaseInsensitiveOption);
//m_FilterExp = RegExp;
m_FilterExp = Exp ;
m_bHighLight = ( iOptions & CFinder : : eHighLight ) ! = 0 ;
//m_FilterCol = Col;
if ( bReset )
emit FilterChanged ( ) ;
}
2023-05-27 08:03:42 +01:00
void CTraceTree : : ItemSelection ( const QItemSelection & selected , const QItemSelection & deselected )
{
QItemSelectionModel * selectionModel = m_pTreeList - > selectionModel ( ) ;
QItemSelection selection = selectionModel - > selection ( ) ;
if ( selection . indexes ( ) . isEmpty ( ) )
return ;
CTraceEntryPtr pEntry = m_pTraceModel - > GetEntry ( selection . indexes ( ) . first ( ) ) ;
2023-06-17 20:13:12 +01:00
if ( pEntry . data ( ) = = NULL )
return ;
2023-05-27 08:03:42 +01:00
CBoxedProcessPtr pProcess = theAPI - > GetProcessById ( pEntry - > GetProcessId ( ) ) ;
if ( ! pProcess . isNull ( ) )
m_pStackView - > ShowStack ( pEntry - > GetStack ( ) , pProcess ) ;
}
2023-01-12 22:10:50 +00:00
2022-03-26 09:09:24 +00:00
////////////////////////////////////////////////////////////////////////////////////////
// CMonitorList
CMonitorList : : CMonitorList ( QWidget * parent )
: CPanelWidget < QTreeViewEx > ( parent )
{
2022-07-29 09:24:32 +01:00
m_pTreeList - > setAlternatingRowColors ( theConf - > GetBool ( " Options/AltRowColors " , false ) ) ;
2022-03-26 09:09:24 +00:00
m_pTreeList - > setSelectionMode ( QAbstractItemView : : ExtendedSelection ) ;
2023-01-07 15:57:55 +00:00
m_pMonitorModel = new CMonitorModel ( this ) ;
2022-04-16 14:01:01 +01:00
//connect(m_pMonitorModel, SIGNAL(NewBranche()), this, SLOT(UpdateFilters()));
2022-03-26 09:09:24 +00:00
2022-07-29 09:24:32 +01:00
m_pSortProxy = new CSortFilterProxyModel ( this ) ;
2022-03-26 09:09:24 +00:00
m_pSortProxy - > setSortRole ( Qt : : EditRole ) ;
m_pSortProxy - > setSourceModel ( m_pMonitorModel ) ;
m_pSortProxy - > setDynamicSortFilter ( true ) ;
m_pTreeList - > setModel ( m_pSortProxy ) ;
QStyle * pStyle = QStyleFactory : : create ( " windows " ) ;
m_pTreeList - > setStyle ( pStyle ) ;
2022-07-20 08:35:07 +01:00
m_pTreeList - > setItemDelegate ( new CTreeItemDelegate ( ) ) ;
2022-03-26 09:09:24 +00:00
m_pTreeList - > setExpandsOnDoubleClick ( false ) ;
m_pTreeList - > setSortingEnabled ( true ) ;
m_pTreeList - > setContextMenuPolicy ( Qt : : CustomContextMenu ) ;
connect ( m_pTreeList , SIGNAL ( customContextMenuRequested ( const QPoint & ) ) , this , SLOT ( OnMenu ( const QPoint & ) ) ) ;
2023-05-25 19:07:13 +01:00
m_pTreeList - > setColumnFixed ( 0 , true ) ;
m_pTreeList - > setColumnFixed ( 1 , true ) ;
m_pTreeList - > setColumnFixed ( 2 , true ) ;
2022-03-26 09:09:24 +00:00
m_pTreeList - > setColumnReset ( 1 ) ;
//connect(m_pTreeList, SIGNAL(ResetColumns()), m_pTreeList, SLOT(OnResetColumns()));
//connect(m_pBoxTree, SIGNAL(ColumnChanged(int, bool)), this, SLOT(OnColumnsChanged()));
m_pMainLayout - > addWidget ( CFinder : : AddFinder ( m_pTreeList , m_pSortProxy ) ) ;
QByteArray Columns = theConf - > GetBlob ( " MainWindow/Monitor_Columns " ) ;
if ( ! Columns . isEmpty ( ) )
( ( QTreeViewEx * ) GetView ( ) ) - > restoreState ( Columns ) ;
}
CMonitorList : : ~ CMonitorList ( )
{
theConf - > SetBlob ( " MainWindow/Monitor_Columns " , GetView ( ) - > header ( ) - > saveState ( ) ) ;
}
2023-01-07 15:57:55 +00:00
2022-03-26 09:09:24 +00:00
////////////////////////////////////////////////////////////////////////////////////////
// CTraceView
2022-07-09 10:46:07 +01:00
CTraceView : : CTraceView ( bool bStandAlone , QWidget * parent ) : QWidget ( parent )
2021-10-15 16:39:43 +01:00
{
m_FullRefresh = true ;
2022-03-26 09:09:24 +00:00
m_LastID = 0 ;
m_LastCount = 0 ;
m_bUpdatePending = false ;
2021-10-15 16:39:43 +01:00
m_FilterPid = 0 ;
m_FilterTid = 0 ;
m_FilterStatus = 0 ;
2022-03-26 09:09:24 +00:00
m_pMainLayout = new QVBoxLayout ( ) ;
2022-09-29 17:28:48 +01:00
m_pMainLayout - > setContentsMargins ( 0 , 0 , 0 , 0 ) ;
2022-03-26 09:09:24 +00:00
this - > setLayout ( m_pMainLayout ) ;
2021-10-15 16:39:43 +01:00
m_pTraceToolBar = new QToolBar ( ) ;
2022-03-26 09:09:24 +00:00
m_pMonitorMode = m_pTraceToolBar - > addAction ( CSandMan : : GetIcon ( " Monitor " ) , tr ( " Monitor mode " ) , this , SLOT ( OnSetMode ( ) ) ) ;
m_pMonitorMode - > setCheckable ( true ) ;
2023-01-12 22:10:50 +00:00
m_pMonitorMode - > setChecked ( theConf - > GetBool ( " Options/UseMonitorMode " , true ) ) ;
2022-03-26 09:09:24 +00:00
2021-10-15 16:39:43 +01:00
m_pTraceTree = m_pTraceToolBar - > addAction ( CSandMan : : GetIcon ( " Tree " ) , tr ( " Show as task tree " ) , this , SLOT ( OnSetTree ( ) ) ) ;
m_pTraceTree - > setCheckable ( true ) ;
m_pTraceTree - > setChecked ( theConf - > GetBool ( " Options/UseLogTree " ) ) ;
2022-03-26 09:09:24 +00:00
2023-01-07 15:57:55 +00:00
m_pObjectTree = m_pTraceToolBar - > addAction ( CSandMan : : GetIcon ( " Objects " ) , tr ( " Show NT Object Tree " ) , this , SLOT ( OnObjTree ( ) ) ) ;
m_pObjectTree - > setCheckable ( true ) ;
m_pObjectTree - > setChecked ( theConf - > GetBool ( " Options/UseObjectTree " ) ) ;
2021-10-15 16:39:43 +01:00
m_pTraceToolBar - > addSeparator ( ) ;
m_pTraceToolBar - > layout ( ) - > setSpacing ( 3 ) ;
m_pTraceToolBar - > addWidget ( new QLabel ( tr ( " PID: " ) ) ) ;
m_pTracePid = new QComboBox ( ) ;
m_pTracePid - > addItem ( tr ( " [All] " ) , 0 ) ;
m_pTracePid - > setMinimumWidth ( 225 ) ;
connect ( m_pTracePid , SIGNAL ( currentIndexChanged ( int ) ) , this , SLOT ( OnSetPidFilter ( ) ) ) ;
m_pTraceToolBar - > addWidget ( m_pTracePid ) ;
m_pTraceToolBar - > addWidget ( new QLabel ( tr ( " TID: " ) ) ) ;
m_pTraceTid = new QComboBox ( ) ;
m_pTraceTid - > addItem ( tr ( " [All] " ) , 0 ) ;
m_pTraceTid - > setMinimumWidth ( 75 ) ;
2023-03-12 15:36:22 +00:00
m_pTraceTid - > setEditable ( true ) ;
2021-10-15 16:39:43 +01:00
connect ( m_pTraceTid , SIGNAL ( currentIndexChanged ( int ) ) , this , SLOT ( OnSetTidFilter ( ) ) ) ;
m_pTraceToolBar - > addWidget ( m_pTraceTid ) ;
m_pTraceToolBar - > addWidget ( new QLabel ( tr ( " Type: " ) ) ) ;
2021-10-30 08:46:49 +01:00
m_pTraceType = new QCheckList ( ) ;
//m_pTraceType->addItem(tr("[All]"), 0);
m_pTraceType - > setAllCheckedText ( tr ( " [All] " ) ) ;
m_pTraceType - > setNoneCheckedText ( tr ( " [All] " ) ) ;
foreach ( quint32 type , CTraceEntry : : AllTypes ( ) )
m_pTraceType - > addCheckItem ( CTraceEntry : : GetTypeStr ( type ) , type , Qt : : Unchecked ) ;
m_pTraceType - > setMinimumWidth ( 100 ) ;
connect ( m_pTraceType , SIGNAL ( globalCheckStateChanged ( int ) ) , this , SLOT ( OnSetFilter ( ) ) ) ;
2021-10-15 16:39:43 +01:00
m_pTraceToolBar - > addWidget ( m_pTraceType ) ;
m_pTraceToolBar - > addWidget ( new QLabel ( tr ( " Status: " ) ) ) ;
m_pTraceStatus = new QComboBox ( ) ;
m_pTraceStatus - > addItem ( tr ( " [All] " ) , 0 ) ;
m_pTraceStatus - > addItem ( tr ( " Open " ) , 1 ) ;
m_pTraceStatus - > addItem ( tr ( " Closed " ) , 2 ) ;
m_pTraceStatus - > addItem ( tr ( " Trace " ) , 3 ) ;
m_pTraceStatus - > addItem ( tr ( " Other " ) , 4 ) ;
m_pTraceStatus - > setMinimumWidth ( 75 ) ;
connect ( m_pTraceStatus , SIGNAL ( currentIndexChanged ( int ) ) , this , SLOT ( OnSetFilter ( ) ) ) ;
m_pTraceToolBar - > addWidget ( m_pTraceStatus ) ;
2022-07-09 10:46:07 +01:00
if ( bStandAlone )
m_pAllBoxes = NULL ;
else {
m_pAllBoxes = m_pTraceToolBar - > addAction ( CSandMan : : GetIcon ( " All " ) , tr ( " Show All Boxes " ) , this , SLOT ( OnSetFilter ( ) ) ) ;
m_pAllBoxes - > setCheckable ( true ) ;
}
2021-10-16 17:24:16 +01:00
2023-05-27 08:03:42 +01:00
m_pShowStack = m_pTraceToolBar - > addAction ( CSandMan : : GetIcon ( " Stack " ) , tr ( " Show Stack Trace " ) , this , SLOT ( OnShowStack ( ) ) ) ;
m_pShowStack - > setCheckable ( true ) ;
2021-10-16 17:24:16 +01:00
m_pTraceToolBar - > addSeparator ( ) ;
m_pSaveToFile = m_pTraceToolBar - > addAction ( CSandMan : : GetIcon ( " Save " ) , tr ( " Save to file " ) , this , SLOT ( SaveToFile ( ) ) ) ;
2021-10-15 16:39:43 +01:00
m_pMainLayout - > setSpacing ( 0 ) ;
2022-03-26 09:09:24 +00:00
m_pMainLayout - > addWidget ( m_pTraceToolBar ) ;
2021-10-15 16:39:43 +01:00
2022-03-26 09:09:24 +00:00
m_pView = new QWidget ( this ) ;
m_pLayout = new QStackedLayout ( m_pView ) ;
m_pTrace = new CTraceTree ( this ) ;
2023-01-07 15:57:55 +00:00
m_pTrace - > m_pTraceModel - > SetTree ( m_pTraceTree - > isChecked ( ) ) ;
2022-07-09 10:46:07 +01:00
2023-07-22 10:59:54 +01:00
m_pTrace - > m_pAutoScroll = new QAction ( tr ( " Auto Scroll " ) ) ;
2023-07-24 18:08:26 +01:00
m_pTrace - > m_pAutoScroll - > setCheckable ( true ) ;
2023-07-22 10:59:54 +01:00
m_pTrace - > m_pAutoScroll - > setChecked ( theConf - > GetBool ( " Options/TraceAutoScroll " ) ) ;
m_pTrace - > GetMenu ( ) - > insertAction ( m_pTrace - > GetMenu ( ) - > actions ( ) [ 0 ] , m_pTrace - > m_pAutoScroll ) ;
2022-07-09 10:46:07 +01:00
if ( bStandAlone ) {
QAction * pAction = new QAction ( tr ( " Cleanup Trace Log " ) ) ;
connect ( pAction , SIGNAL ( triggered ( ) ) , this , SLOT ( Clear ( ) ) ) ;
2023-07-22 10:59:54 +01:00
m_pTrace - > GetMenu ( ) - > insertAction ( m_pTrace - > GetMenu ( ) - > actions ( ) [ 1 ] , pAction ) ;
2022-07-09 10:46:07 +01:00
}
2022-03-26 09:09:24 +00:00
m_pLayout - > addWidget ( m_pTrace ) ;
2021-10-15 16:39:43 +01:00
2023-01-12 22:10:50 +00:00
QObject : : connect ( m_pTrace , SIGNAL ( FilterChanged ( ) ) , this , SLOT ( OnFilterChanged ( ) ) ) ;
2021-10-15 16:39:43 +01:00
2022-03-26 09:09:24 +00:00
m_pMonitor = new CMonitorList ( this ) ;
2023-01-07 15:57:55 +00:00
m_pMonitor - > m_pMonitorModel - > SetObjTree ( m_pObjectTree - > isChecked ( ) ) ;
2022-03-26 09:09:24 +00:00
m_pLayout - > addWidget ( m_pMonitor ) ;
2021-10-15 16:39:43 +01:00
2022-03-26 09:09:24 +00:00
m_pView - > setLayout ( m_pLayout ) ;
m_pMainLayout - > addWidget ( m_pView ) ;
2021-10-15 16:39:43 +01:00
2022-03-26 09:09:24 +00:00
OnSetMode ( ) ;
2022-07-09 10:46:07 +01:00
m_uTimerID = startTimer ( 1000 ) ;
2021-10-15 16:39:43 +01:00
}
CTraceView : : ~ CTraceView ( )
{
2023-07-22 10:59:54 +01:00
theConf - > SetValue ( " Options/TraceAutoScroll " , m_pTrace - > m_pAutoScroll - > isChecked ( ) ) ;
2022-07-09 10:46:07 +01:00
killTimer ( m_uTimerID ) ;
}
void CTraceView : : timerEvent ( QTimerEvent * pEvent )
{
if ( pEvent - > timerId ( ) ! = m_uTimerID )
return ;
Refresh ( ) ;
2021-10-15 16:39:43 +01:00
}
2023-05-27 08:03:42 +01:00
void CTraceView : : SetEnabled ( bool bSet )
{
setEnabled ( bSet ) ;
m_pShowStack - > setChecked ( theAPI - > GetGlobalSettings ( ) - > GetBool ( " MonitorStackTrace " , false ) ) ;
m_pTrace - > m_pStackView - > setVisible ( m_pShowStack - > isChecked ( ) ) ;
}
void CTraceView : : OnShowStack ( )
{
2023-07-30 13:28:35 +01:00
if ( ! theGUI - > GetAddonManager ( ) - > GetAddon ( " DbgHelp " , CAddonManager : : eInstalled ) . isNull ( ) )
2023-07-01 17:54:53 +01:00
theGUI - > GetAddonManager ( ) - > TryInstallAddon ( " DbgHelp " , this , tr ( " To use the stack traces feature the DbgHelp.dll and SymSrv.dll are required, do you want to download and install them? " ) ) ;
2023-05-27 08:03:42 +01:00
theAPI - > GetGlobalSettings ( ) - > SetBool ( " MonitorStackTrace " , m_pShowStack - > isChecked ( ) ) ;
m_pTrace - > m_pStackView - > setVisible ( m_pShowStack - > isChecked ( ) ) ;
}
2021-10-15 16:39:43 +01:00
void CTraceView : : Refresh ( )
{
QList < CSandBoxPtr > Boxes ;
2022-07-09 10:46:07 +01:00
if ( m_pAllBoxes & & ! m_pAllBoxes - > isChecked ( ) )
2021-10-15 16:39:43 +01:00
Boxes = theGUI - > GetBoxView ( ) - > GetSelectedBoxes ( ) ;
if ( m_pCurrentBox ! = ( Boxes . count ( ) = = 1 ? Boxes . first ( ) . data ( ) : NULL ) ) {
m_pCurrentBox = Boxes . count ( ) = = 1 ? Boxes . first ( ) . data ( ) : NULL ;
m_FullRefresh = true ;
}
2022-03-26 09:09:24 +00:00
bool bMonitorMode = m_pMonitorMode - > isChecked ( ) ;
if ( m_FullRefresh )
{
m_LastID = 0 ;
m_LastCount = 0 ;
m_PidMap . clear ( ) ;
2023-01-07 15:57:55 +00:00
quint64 start = GetCurCycle ( ) ;
2022-03-26 09:09:24 +00:00
m_pTrace - > m_pTraceModel - > Clear ( ) ;
2023-01-08 10:49:09 +00:00
qDebug ( ) < < " Clear took " < < ( GetCurCycle ( ) - start ) / 1000000.0 < < " s " ;
2023-01-07 15:57:55 +00:00
2022-03-26 09:09:24 +00:00
m_pMonitor - > m_pMonitorModel - > Clear ( ) ;
2021-10-15 16:39:43 +01:00
m_FullRefresh = false ;
}
2023-01-07 15:57:55 +00:00
const QVector < CTraceEntryPtr > & ResourceLog = theAPI - > GetTrace ( ) ;
2022-03-26 09:09:24 +00:00
2023-01-07 15:57:55 +00:00
bool bUpdateFilters = false ;
2022-03-26 09:09:24 +00:00
int i = 0 ;
if ( ResourceLog . count ( ) > = m_LastCount & & m_LastCount > 0 )
2021-10-15 16:39:43 +01:00
{
2022-03-26 09:09:24 +00:00
i = m_LastCount - 1 ;
if ( m_LastID = = ResourceLog . at ( i ) - > GetUID ( ) )
i + + ;
else
i = 0 ;
}
if ( i = = 0 ) {
m_PidMap . clear ( ) ;
2023-01-07 15:57:55 +00:00
m_TraceList . clear ( ) ;
2022-03-26 09:09:24 +00:00
m_MonitorMap . clear ( ) ;
}
2023-01-07 15:57:55 +00:00
if ( m_LastCount = = ResourceLog . count ( ) )
return ;
2023-01-12 22:10:50 +00:00
//bool bHasFilter = !m_pTrace->m_FilterExp.pattern().isEmpty();
bool bHasFilter = ! m_pTrace - > m_FilterExp . isEmpty ( ) ;
2023-01-07 15:57:55 +00:00
2023-01-08 10:49:09 +00:00
quint64 start = GetCurCycle ( ) ;
2022-03-26 09:09:24 +00:00
for ( ; i < ResourceLog . count ( ) ; i + + )
{
2023-01-07 15:57:55 +00:00
const CTraceEntryPtr & pEntry = ResourceLog . at ( i ) ;
2022-03-26 09:09:24 +00:00
SProgInfo & Info = m_PidMap [ pEntry - > GetProcessId ( ) ] ;
if ( Info . Name . isEmpty ( ) ) {
Info . Name = pEntry - > GetProcessName ( ) ;
2023-01-07 15:57:55 +00:00
bUpdateFilters = true ;
2022-03-26 09:09:24 +00:00
}
if ( ! Info . Threads . contains ( pEntry - > GetThreadId ( ) ) ) {
Info . Threads . insert ( pEntry - > GetThreadId ( ) ) ;
2023-01-07 15:57:55 +00:00
bUpdateFilters = true ;
2022-03-26 09:09:24 +00:00
}
2023-01-07 15:57:55 +00:00
if ( m_pCurrentBox ! = NULL & & m_pCurrentBox ! = pEntry - > GetBoxPtr ( ) )
continue ;
2022-03-26 09:09:24 +00:00
2023-01-07 15:57:55 +00:00
if ( m_FilterPid ! = 0 & & m_FilterPid ! = pEntry - > GetProcessId ( ) )
continue ;
2022-03-26 09:09:24 +00:00
2023-01-07 15:57:55 +00:00
if ( m_FilterTid ! = 0 & & m_FilterTid ! = pEntry - > GetThreadId ( ) )
continue ;
2022-03-26 09:09:24 +00:00
2023-01-07 15:57:55 +00:00
if ( ! m_FilterTypes . isEmpty ( ) & & ! m_FilterTypes . contains ( pEntry - > GetType ( ) ) )
continue ;
2022-03-26 09:09:24 +00:00
2023-01-07 15:57:55 +00:00
if ( bMonitorMode )
{
CMonitorEntryPtr & pItem = m_MonitorMap [ pEntry - > GetName ( ) . toLower ( ) ] ;
if ( pItem . data ( ) = = NULL ) {
QString Name = pEntry - > GetName ( ) ;
//if (Name.left(9).compare("\\REGISTRY", Qt::CaseInsensitive) == 0) {
// int pos = Name.indexOf("\\", 10);
// Name = Name.left(pos).toUpper() + Name.mid(pos);
//}
pItem = CMonitorEntryPtr ( new CMonitorEntry ( Name , pEntry - > GetType ( ) ) ) ;
}
2022-03-26 09:09:24 +00:00
pItem - > Merge ( pEntry ) ;
}
2023-01-07 15:57:55 +00:00
else
{
2023-01-12 22:10:50 +00:00
if ( bHasFilter & & ! m_pTrace - > m_bHighLight ) {
2023-01-27 19:43:26 +00:00
if ( ! pEntry - > GetName ( ) . contains ( m_pTrace - > m_FilterExp , Qt : : CaseInsensitive )
& & ! pEntry - > GetMessage ( ) . contains ( m_pTrace - > m_FilterExp , Qt : : CaseInsensitive )
2023-02-03 07:05:10 +00:00
//&& !pEntry->GetTypeStr().contains(m_pTrace->m_FilterExp, Qt::CaseInsensitive) // don't filter on non static strings !!!
//&& !pEntry->GetStautsStr().contains(m_pTrace->m_FilterExp, Qt::CaseInsensitive) // don't filter on non static strings !!!
2023-01-27 19:43:26 +00:00
& & ! pEntry - > GetProcessName ( ) . contains ( m_pTrace - > m_FilterExp , Qt : : CaseInsensitive ) )
2023-01-07 15:57:55 +00:00
continue ;
}
if ( m_FilterStatus ! = 0 ) {
if ( pEntry - > IsOpen ( ) ) {
if ( m_FilterStatus ! = 1 ) continue ;
} else if ( pEntry - > IsClosed ( ) ) {
if ( m_FilterStatus ! = 2 ) continue ;
} else if ( pEntry - > IsTrace ( ) ) {
if ( m_FilterStatus ! = 3 ) continue ;
} else {
if ( m_FilterStatus ! = 4 ) continue ;
}
}
m_TraceList . append ( pEntry ) ;
}
2022-03-26 09:09:24 +00:00
}
2023-01-08 10:49:09 +00:00
qDebug ( ) < < " Filtering took " < < ( GetCurCycle ( ) - start ) / 1000000.0 < < " s " ;
2022-03-26 09:09:24 +00:00
m_LastCount = ResourceLog . count ( ) ;
if ( m_LastCount )
m_LastID = ResourceLog . last ( ) - > GetUID ( ) ;
2023-01-07 15:57:55 +00:00
if ( bUpdateFilters & & ! m_bUpdatePending )
2022-03-26 09:09:24 +00:00
{
m_bUpdatePending = true ;
QTimer : : singleShot ( 500 , this , SLOT ( UpdateFilters ( ) ) ) ;
}
if ( bMonitorMode )
{
2023-01-07 15:57:55 +00:00
QList < QModelIndex > NewBranches = m_pMonitor - > m_pMonitorModel - > Sync ( m_MonitorMap , this ) ;
if ( m_pMonitor - > m_pMonitorModel - > IsObjTree ( ) )
{
2023-05-27 08:03:42 +01:00
QTimer : : singleShot ( 10 , this , [ this , NewBranches ] ( ) {
2023-01-07 15:57:55 +00:00
CSortFilterProxyModel * pSortProxy = m_pMonitor - > m_pSortProxy ;
foreach ( const QModelIndex & Index , NewBranches ) {
m_pMonitor - > GetTree ( ) - > expand ( pSortProxy - > mapFromSource ( Index ) ) ;
}
} ) ;
}
2022-03-26 09:09:24 +00:00
}
else
{
2023-01-12 22:10:50 +00:00
if ( m_pTrace - > m_bHighLight )
m_pTrace - > m_pTraceModel - > SetHighLight ( m_pTrace - > m_FilterExp ) ;
else
m_pTrace - > m_pTraceModel - > SetHighLight ( QString ( ) ) ;
2023-01-07 15:57:55 +00:00
quint64 start = GetCurCycle ( ) ;
QList < QModelIndex > NewBranches = m_pTrace - > m_pTraceModel - > Sync ( m_TraceList ) ;
2023-01-08 10:49:09 +00:00
qDebug ( ) < < " Sync took " < < ( GetCurCycle ( ) - start ) / 1000000.0 < < " s " ;
2022-03-26 09:09:24 +00:00
if ( m_pTrace - > m_pTraceModel - > IsTree ( ) )
{
2023-05-27 08:03:42 +01:00
QTimer : : singleShot ( 10 , this , [ this , NewBranches ] ( ) {
2023-01-07 15:57:55 +00:00
quint64 start = GetCurCycle ( ) ;
foreach ( const QModelIndex & Index , NewBranches )
m_pTrace - > GetTree ( ) - > expand ( Index ) ;
qDebug ( ) < < " Expand took " < < ( GetCurCycle ( ) - start ) / 1000000.0 < < " s " ;
2022-03-26 09:09:24 +00:00
} ) ;
}
2023-07-22 10:59:54 +01:00
if ( m_pTrace - > m_pAutoScroll - > isChecked ( ) )
m_pTrace - > m_pTreeList - > scrollToBottom ( ) ;
2021-10-15 16:39:43 +01:00
}
}
void CTraceView : : Clear ( )
{
m_pTracePid - > clear ( ) ;
m_pTracePid - > addItem ( tr ( " [All] " ) , 0 ) ;
m_pTraceTid - > clear ( ) ;
m_pTraceTid - > addItem ( tr ( " [All] " ) , 0 ) ;
theAPI - > ClearTrace ( ) ;
2023-01-07 15:57:55 +00:00
m_pTrace - > m_pTraceModel - > Clear ( true ) ;
2022-03-26 09:09:24 +00:00
m_pMonitor - > m_pMonitorModel - > Clear ( ) ;
2021-10-15 16:39:43 +01:00
}
2022-04-07 19:49:13 +01:00
void CTraceView : : AddAction ( QAction * pAction )
{
m_pTrace - > GetMenu ( ) - > insertAction ( m_pTrace - > GetMenu ( ) - > actions ( ) [ 0 ] , pAction ) ;
m_pTrace - > GetMenu ( ) - > insertSeparator ( m_pTrace - > GetMenu ( ) - > actions ( ) [ 0 ] ) ;
m_pMonitor - > GetMenu ( ) - > insertAction ( m_pMonitor - > GetMenu ( ) - > actions ( ) [ 0 ] , pAction ) ;
m_pMonitor - > GetMenu ( ) - > insertSeparator ( m_pMonitor - > GetMenu ( ) - > actions ( ) [ 0 ] ) ;
}
2021-10-15 16:39:43 +01:00
void CTraceView : : OnSetTree ( )
{
2023-01-07 15:57:55 +00:00
m_pTrace - > m_pTraceModel - > SetTree ( m_pTraceTree - > isChecked ( ) ) ;
//m_pTrace->m_pTraceModel->Clear();
m_FullRefresh = true ;
2022-09-29 17:28:48 +01:00
Refresh ( ) ;
2023-01-07 15:57:55 +00:00
//m_pTrace->GetTree()->expandAll();
2022-09-29 17:28:48 +01:00
2021-10-15 16:39:43 +01:00
theConf - > SetValue ( " Options/UseLogTree " , m_pTraceTree - > isChecked ( ) ) ;
}
2022-03-26 09:09:24 +00:00
void CTraceView : : OnSetMode ( )
{
if ( m_pMonitorMode - > isChecked ( ) )
m_pLayout - > setCurrentIndex ( 1 ) ; // monitor
else
m_pLayout - > setCurrentIndex ( 0 ) ; // trace
m_pTraceTree - > setEnabled ( ! m_pMonitorMode - > isChecked ( ) ) ;
2023-01-07 15:57:55 +00:00
m_pObjectTree - > setEnabled ( m_pMonitorMode - > isChecked ( ) ) ;
2022-03-26 09:09:24 +00:00
m_pTraceStatus - > setEnabled ( ! m_pMonitorMode - > isChecked ( ) ) ;
2023-05-27 08:03:42 +01:00
m_pShowStack - > setEnabled ( ! m_pMonitorMode - > isChecked ( ) ) ;
2022-03-26 09:09:24 +00:00
m_FullRefresh = true ;
2023-01-07 15:57:55 +00:00
Refresh ( ) ;
2022-03-26 09:09:24 +00:00
2023-01-07 15:57:55 +00:00
theConf - > SetValue ( " Options/UseMonitorMode " , m_pMonitorMode - > isChecked ( ) ) ;
}
void CTraceView : : OnObjTree ( )
{
m_pMonitor - > m_pMonitorModel - > SetObjTree ( m_pObjectTree - > isChecked ( ) ) ;
//m_pMonitor->m_pMonitorModel->Clear();
m_FullRefresh = true ;
2022-03-26 09:09:24 +00:00
Refresh ( ) ;
2023-01-07 15:57:55 +00:00
//m_pTrace->GetTree()->expandAll();
theConf - > SetValue ( " Options/UseObjectTree " , m_pObjectTree - > isChecked ( ) ) ;
2022-03-26 09:09:24 +00:00
}
2021-10-15 16:39:43 +01:00
void CTraceView : : UpdateFilters ( )
{
2022-03-26 09:09:24 +00:00
m_bUpdatePending = false ;
2021-10-15 16:39:43 +01:00
quint32 cur_pid = m_pTracePid - > currentData ( ) . toUInt ( ) ;
2022-03-26 09:09:24 +00:00
QMap < quint32 , SProgInfo > pids = m_PidMap ;
2022-09-29 17:28:48 +01:00
foreach ( quint32 pid , pids . keys ( ) ) {
2022-03-26 09:09:24 +00:00
SProgInfo & Info = pids [ pid ] ;
2021-10-15 16:39:43 +01:00
if ( m_pTracePid - > findData ( pid ) = = - 1 )
m_pTracePid - > addItem ( tr ( " %1 (%2) " ) . arg ( Info . Name ) . arg ( pid ) , pid ) ;
if ( cur_pid ! = 0 & & cur_pid ! = pid )
continue ;
foreach ( quint32 tid , Info . Threads ) {
if ( m_pTraceTid - > findData ( tid ) = = - 1 )
m_pTraceTid - > addItem ( tr ( " %1 " ) . arg ( tid ) , tid ) ;
}
}
}
2023-01-12 22:10:50 +00:00
void CTraceView : : OnFilterChanged ( )
2021-10-15 16:39:43 +01:00
{
m_FullRefresh = true ;
}
void CTraceView : : OnSetPidFilter ( )
{
m_FilterPid = m_pTracePid - > currentData ( ) . toUInt ( ) ;
m_FilterTid = 0 ;
//m_pSortProxy->m_FilterPid = m_pTracePid->currentData().toUInt();
//m_pSortProxy->m_FilterTid = 0;
QTimer : : singleShot ( 100 , this , [ this ] ( ) {
m_pTraceTid - > clear ( ) ;
m_pTraceTid - > addItem ( tr ( " [All] " ) , 0 ) ;
UpdateFilters ( ) ;
} ) ;
//m_pSortProxy->setFilterKeyColumn(m_pSortProxy->filterKeyColumn());
m_FullRefresh = true ;
2023-01-07 15:57:55 +00:00
//if(!m_pMonitorMode->isChecked())
// m_pTrace->GetTree()->expandAll();
2021-10-15 16:39:43 +01:00
}
void CTraceView : : OnSetTidFilter ( )
{
2023-03-12 15:36:22 +00:00
//m_FilterTid = m_pTraceTid->currentData().toUInt();
m_FilterTid = m_pTraceTid - > currentText ( ) . toUInt ( ) ;
2021-10-15 16:39:43 +01:00
//m_pSortProxy->m_FilterTid = m_pTraceTid->currentData().toUInt();
//m_pSortProxy->setFilterKeyColumn(m_pSortProxy->filterKeyColumn());
m_FullRefresh = true ;
2023-01-07 15:57:55 +00:00
//if(!m_pMonitorMode->isChecked())
// m_pTrace->GetTree()->expandAll();
2021-10-15 16:39:43 +01:00
}
void CTraceView : : OnSetFilter ( )
{
2021-10-30 08:46:49 +01:00
m_FilterTypes . clear ( ) ;
for ( int i = 0 ; i < m_pTraceType - > count ( ) ; i + + ) {
if ( m_pTraceType - > itemData ( i , Qt : : CheckStateRole ) . toInt ( ) = = Qt : : Checked ) {
m_FilterTypes . append ( m_pTraceType - > itemData ( i , Qt : : UserRole + 1 ) . toUInt ( ) ) ;
}
}
2021-10-15 16:39:43 +01:00
m_FilterStatus = m_pTraceStatus - > currentData ( ) . toUInt ( ) ;
m_FullRefresh = true ;
2023-01-07 15:57:55 +00:00
//if(!m_pMonitorMode->isChecked())
// m_pTrace->GetTree()->expandAll();
2021-10-15 16:39:43 +01:00
}
2021-10-16 17:24:16 +01:00
void CTraceView : : SaveToFile ( )
{
QString Path = QFileDialog : : getSaveFileName ( this , tr ( " Save trace log to file " ) , " " , QString ( " Log files (*.log) " ) ) . replace ( " / " , " \\ " ) ;
2021-10-16 19:13:16 +01:00
if ( Path . isEmpty ( ) )
return ;
2021-10-16 17:24:16 +01:00
QFile File ( Path ) ;
if ( ! File . open ( QFile : : WriteOnly ) ) {
QMessageBox : : critical ( this , " Sandboxie-Plus " , tr ( " Failed to open log file for writing " ) ) ;
return ;
}
2022-05-15 11:27:33 +01:00
if ( m_pMonitorMode - > isChecked ( ) )
2021-10-16 17:24:16 +01:00
{
2022-05-15 11:27:33 +01:00
QList < QStringList > Rows = m_pMonitor - > DumpPanel ( ) ;
foreach ( const QStringList & Row , Rows )
File . write ( Row . join ( " \t " ) . toLatin1 ( ) + " \n " ) ;
}
else
{
2023-07-01 17:54:53 +01:00
SaveToFile ( & File ) ;
2021-10-16 17:24:16 +01:00
}
File . close ( ) ;
2022-07-09 10:46:07 +01:00
}
2023-07-01 17:54:53 +01:00
void CTraceView : : SaveToFileAsync ( const CSbieProgressPtr & pProgress , QVector < CTraceEntryPtr > ResourceLog , QIODevice * pFile )
{
pProgress - > ShowMessage ( tr ( " Saving TraceLog... " ) ) ;
QByteArray Unknown = " Unknown " ;
quint64 LastTimeStamp = 0 ;
QByteArray LastTimeStampStr ;
for ( int i = 0 ; i < ResourceLog . count ( ) & & ! pProgress - > IsCanceled ( ) ; i + + )
{
if ( i % 10000 = = 0 )
pProgress - > SetProgress ( 100 * i / ResourceLog . count ( ) ) ;
const CTraceEntryPtr & pEntry = ResourceLog . at ( i ) ;
if ( LastTimeStamp ! = pEntry - > GetTimeStamp ( ) ) {
LastTimeStamp = pEntry - > GetTimeStamp ( ) ;
LastTimeStampStr = QDateTime : : fromMSecsSinceEpoch ( pEntry - > GetTimeStamp ( ) ) . toString ( " hh:mm:ss.zzz " ) . toUtf8 ( ) ;
}
pFile - > write ( LastTimeStampStr ) ;
pFile - > write ( " \t " ) ;
QString Name = pEntry - > GetProcessName ( ) ;
pFile - > write ( Name . isEmpty ( ) ? Unknown : Name . toUtf8 ( ) ) ;
pFile - > write ( " \t " ) ;
pFile - > write ( QByteArray : : number ( pEntry - > GetProcessId ( ) ) ) ;
pFile - > write ( " \t " ) ;
pFile - > write ( QByteArray : : number ( pEntry - > GetThreadId ( ) ) ) ;
pFile - > write ( " \t " ) ;
pFile - > write ( pEntry - > GetTypeStr ( ) . toUtf8 ( ) ) ;
pFile - > write ( " \t " ) ;
pFile - > write ( pEntry - > GetStautsStr ( ) . toUtf8 ( ) ) ;
pFile - > write ( " \t " ) ;
pFile - > write ( pEntry - > GetName ( ) . toUtf8 ( ) ) ;
pFile - > write ( " \t " ) ;
pFile - > write ( pEntry - > GetMessage ( ) . toUtf8 ( ) ) ;
pFile - > write ( " \n " ) ;
}
pProgress - > Finish ( SB_OK ) ;
}
bool CTraceView : : SaveToFile ( QIODevice * pFile )
{
pFile - > write ( " Timestamp \t Process \t PID \t TID \t Type \t Status \t Name \t Message \n " ) ; // don't translate log
QVector < CTraceEntryPtr > ResourceLog = theAPI - > GetTrace ( ) ;
CSbieProgressPtr pProgress = CSbieProgressPtr ( new CSbieProgress ( ) ) ;
QtConcurrent : : run ( CTraceView : : SaveToFileAsync , pProgress , ResourceLog , pFile ) ;
theGUI - > AddAsyncOp ( pProgress , true ) ;
return ! pProgress - > IsCanceled ( ) ;
}
2022-07-09 10:46:07 +01:00
////////////////////////////////////////////////////////////////////////////////////////
// CTraceWindow
CTraceWindow : : CTraceWindow ( QWidget * parent )
: QDialog ( parent )
{
Qt : : WindowFlags flags = windowFlags ( ) ;
flags | = Qt : : CustomizeWindowHint ;
//flags &= ~Qt::WindowContextHelpButtonHint;
//flags &= ~Qt::WindowSystemMenuHint;
//flags &= ~Qt::WindowMinMaxButtonsHint;
//flags |= Qt::WindowMinimizeButtonHint;
//flags &= ~Qt::WindowCloseButtonHint;
flags & = ~ Qt : : WindowContextHelpButtonHint ;
//flags &= ~Qt::WindowSystemMenuHint;
setWindowFlags ( flags ) ;
this - > setWindowTitle ( tr ( " Sandboxie-Plus - Trace Monitor " ) ) ;
2022-07-11 18:30:09 +01:00
bool bAlwaysOnTop = theConf - > GetBool ( " Options/AlwaysOnTop " , false ) ;
this - > setWindowFlag ( Qt : : WindowStaysOnTopHint , bAlwaysOnTop ) ;
2022-07-09 10:46:07 +01:00
QGridLayout * pLayout = new QGridLayout ( ) ;
2022-09-29 17:28:48 +01:00
pLayout - > setContentsMargins ( 3 , 3 , 3 , 3 ) ;
2022-07-09 10:46:07 +01:00
pLayout - > addWidget ( new CTraceView ( true , this ) , 0 , 0 ) ;
this - > setLayout ( pLayout ) ;
restoreGeometry ( theConf - > GetBlob ( " TraceWindow/Window_Geometry " ) ) ;
}
CTraceWindow : : ~ CTraceWindow ( )
{
theConf - > SetBlob ( " TraceWindow/Window_Geometry " , saveGeometry ( ) ) ;
2022-07-11 18:30:09 +01:00
if ( ! theAPI ) theAPI - > EnableMonitor ( false ) ;
2022-07-09 10:46:07 +01:00
}
void CTraceWindow : : closeEvent ( QCloseEvent * e )
{
emit Closed ( ) ;
this - > deleteLater ( ) ;
2022-12-07 16:32:40 +00:00
}
2023-01-07 15:57:55 +00:00