策略为王源代码分析-股票列表(SListView.cpp)

Published

摘要:

1.窗口的初始化

2.窗口切换的处理

3.读取本地数据

4.发送请求数据

5.接收推送行情数据

6.画图

 

第一部分 视图的结构和数据初始化

SListView.cpp同时实现了两种功能,一个是推送行情,一个是请求行情。

int CSListView::OnCreate(LPCREATESTRUCT lpCreateStruct) 方法中,

1.初始化GridCtrl

2.准备底部标签

3.注册了窗口句柄,用于接收主动推送行情的行情数据

4.设置了一个定时器,每隔3秒主动向行情服务器请求一次行情数据。

int CSListView::OnCreate(LPCREATESTRUCT lpCreateStruct) 方法中的
{
        // Create GridCtrl  初始化GridCtrl
	CRect rect;
	GetClientRect(rect);
	if (!m_Grid.Create(rect, this, IDC_SLISTVIEW_GRID, WS_CHILD|WS_BORDER|WS_TABSTOP|WS_VISIBLE|WS_VSCROLL|WS_HSCROLL))
	{
		TRACE("CSListView::OnCreate(...), Create m_Grid Error.\n");
		return -1;
	}

	// fill it up with stuff
	m_Grid.SetEditable(FALSE);
	m_Grid.SetListMode(TRUE);
	m_Grid.SetHeaderSort(FALSE); 
	m_Grid.SetSingleRowSelection(FALSE);
	m_Grid.EnableDragAndDrop(TRUE);
	m_Grid.SetGridLines(GVL_NONE);
	m_Grid.EnableTitleTips(TRUE);
	m_Grid.SetRowResize(TRUE);
	m_Grid.SetColumnResize(TRUE);
        .....

         //底部标签
	CTSKTabManagerItem* pItem;
	CString	strName;

	strName.LoadString(IDS_SLISTVIEW_CLASS);
	pItem = new CTSKTabManagerItem();
	pItem->SetCaption(strName);
	AddItem(SL_WORKTAB_CLASS, pItem);

	strName.LoadString(IDS_SLISTVIEW_A);
	pItem = new CTSKTabManagerItem();
	pItem->SetCaption(strName);
	AddItem(SL_WORKTAB_A, pItem);

	//strName.LoadString(IDS_SLISTVIEW_MSMALL);
	strName = _T("中小板");
	pItem = new CTSKTabManagerItem();
	pItem->SetCaption("中小板");
	AddItem(SL_WORKTAB_MSMALL, pItem);
        .....

        主动推送行情,注册窗口句柄,在 \StkNet\Src\StkReceiver.cpp中实现
	AfxGetStkReceiver().AddRcvDataWnd(GetSafeHwnd());

	//行情列表刷新速度 
	SetTimer(SLV_TIMER_REFRESH, 20000, NULL);
}

	

 

推送行情

G:\stock\TskingVS2019\src\Client\StkUI\View\SListView.cpp

定义了一个接收数据的消息

BEGIN_MESSAGE_MAP(CSListView, CView)
	
	
	ON_MESSAGE(WM_APP_STKRECEIVER_DATA, OnStkReceiverData)
END_MESSAGE_MAP()

消息处理

LRESULT CSListView::OnStkReceiverData(WPARAM wParam, LPARAM lParam)
{
	PCOMMPACKET	pCommPacket	=	(PCOMMPACKET)lParam;

	switch(wParam)
	{
	case CStock::dataReport:
		if (pCommPacket && CStock::dataReport == pCommPacket->m_dwDataType 
			&& pCommPacket->m_dwCount > 0)
		{
			CStockContainer & container = AfxGetSListStockContainer();
			// 如果当前股票列表显示的日期不是今天的,返回,不做刷新
			DWORD	dwDate = -1;;
			AfxGetSListStockContainer().GetCurrentType(NULL,NULL,&dwDate);
			if (-1 != dwDate && dwDate != CSPTime(pCommPacket->m_pReport[0].m_time).ToStockTimeDay())
				return 0L;

			container.Lock();

			// 刷新数据
			BOOL	bNeedReSort	=	FALSE;
			for (DWORD i=0; i<pCommPacket->m_dwCount; i++)
			{
				int	id = 0;
				if (container.GetStockInfo(pCommPacket->m_pReport[i].m_szCode, NULL, &id))
				{
					CStockInfo	& info	=	container.ElementAt(id);
					UpdateStockInfoByREPORT(info, &(pCommPacket->m_pReport[i]));
					StockInfoChanged(id, info);
					bNeedReSort	=	TRUE;
				}
			}

			// 统计平均值刷新
			if (bNeedReSort)
			{
				container.SetAverage();
				//SetAverageItem(m_Grid, container, TRUE);
			}

			// 重新排序
			if (-1 != m_nColSort && bNeedReSort)
			{
				LPARAM	lParam	=	m_Grid.GetItemData(0, m_nColSort);
				if (lParam >= 0)
				{
					CStockContainer::m_pSortContainer	=	&container;
					container.m_nSortVariantID	=	lParam;
					container.m_bSortAscend		=	m_bSortAscend;
					m_Grid.SortItems(ItemCompareFunc, 0, TRUE);
				}
				m_Grid.Invalidate();
			}
			container.UnLock();
		}
		break;
	}
	return 0L;
}

 

WM_APP_STKRECEIVER_DATA 由以下几个地方发出:

1.通视驱动发出,

G:\stock\TskingVS2019\src\Client\StkNet\Src\StkReceiver.cpp

// 通视处理数据
LRESULT CStkReceiver::OnStkReceiverTsdata(
		UINT wFileType,				// 文件类型, 参见 StockDrv.H
		LONG lPara)
{
	int	i = 0;
	PBYTE pFileBuf = NULL;
	PBYTE pBuffx = NULL;
	PGZLXBINDATA pGZLX = NULL;
	RCV_DATA *	pHeader = (RCV_DATA *) lPara;

	// 检查参数
	ASSERT(pHeader && pHeader->m_pData);
	if (!pHeader || !pHeader->m_pData)
		return -1L;
	// ASSERT(pHeader->m_nPacketNum > 0 || pHeader->m_bDISK);
	if (pHeader->m_nPacketNum <= 0 && !pHeader->m_bDISK)
		return -1L;

	//	对于处理较慢的数据类型,建议将数据备份,另创建一线程处理
	switch(wFileType)
	{
	case RCV_REPORT:
		{
			// 检查参数
			//ASSERT(pHeader->m_nPacketNum > 0 && !pHeader->m_bDISK);
			if (pHeader->m_nPacketNum <= 0 || pHeader->m_bDISK)
				return -1L;
			
			// 分配备份数据存储空间
			COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataReport, pHeader->m_nPacketNum);
			if (NULL == pCommPacket)
				return -1L;

			// 修改AfxGetStockContainer()
			int nBufSize = pHeader->m_pReport[0].m_cbSize;
			PBYTE pBaseBuf = (PBYTE)&pHeader->m_pReport[0];
			for(i=0; i<pHeader->m_nPacketNum; i++)
			{
				PRCV_REPORT_STRUCTEx pReport = (PRCV_REPORT_STRUCTEx)(pBaseBuf + nBufSize*i);
				if (convert_TSREPORT_to_REPORT(pReport, &(pCommPacket->m_pReport[i])))
				{
					/* 以下代码转移到新建线程中执行 */
					// AfxGetStkReceiver().OnReceiveReport(&(pCommPacket->m_pReport[i]));
				}
			}
			
			{
				CSingleLock		lock(&g_mutexCommPacket,TRUE);
				g_aptrCommPacket.Add(pCommPacket);
			}

			/* 以下代码转移到新建线程中执行
			// 保存至本地文件
			AfxGetDB().StoreReport(pCommPacket->m_pReport, pCommPacket->m_dwCount, FALSE);

			// 通知各股票信息显示窗口,刷新行情数据
			for(i=0; i<m_awndRcvData.GetSize(); i++)
				::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataReport, (LPARAM)pCommPacket);

			// 释放备份数据存储空间
			FreeCommPacket(pCommPacket);
			*/ 
		}
		break;
	
	case RCV_FILEDATA:
		switch(pHeader->m_wDataType)
		{
		case FILE_HISTORY_EX:						// 补日线数据
			{
				RCV_HISTORY_STRUCTEx * pDay = pHeader->m_pDay;
				int	nPacketNum	=	pHeader->m_nPacketNum;
				CKData	kdata(CKData::ktypeDay);
				CString	sCode;
				DWORD	dwMarket = CStock::marketUnknown;

				// 读取文件
				if (pHeader->m_bDISK)
				{
					pDay	=	NULL;
					CFile	file;
					if (file.Open(pHeader->m_File.m_szFileName,CFile::modeRead))
					{
						ASSERT(file.GetLength() == pHeader->m_File.m_dwLen);
						nPacketNum	=	(int)(file.GetLength()/sizeof(RCV_HISTORY_STRUCTEx));
						if (nPacketNum > 0)
							pDay = new RCV_HISTORY_STRUCTEx[nPacketNum];
						if (pDay)
							file.Read(pDay, sizeof(RCV_HISTORY_STRUCTEx)*nPacketNum);
						file.Close();
					}
				}

				// 分配备份数据存储空间
				COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataK, nPacketNum);
				if (NULL == pCommPacket)
					return -1L;

				// 处理数据
				DWORD	dwCount	=	0;
				for(i=0; i<nPacketNum && pDay; i++)
				{
					if (pDay[i].m_head.m_dwHeadTag == EKE_HEAD_TAG)
					{
						if (kdata.GetSize() > 0)
						{
							kdata.AutoSetKType();
							UpdateStockContainerByKData(AfxGetStockContainer(), sCode, kdata);
							AfxGetDB().InstallKData(kdata);
							kdata.RemoveAll();
						}
						sCode		=	pDay[i].m_head.m_szLabel;
						dwMarket	=	TSMarket_to_Market(pDay[i].m_head.m_wMarket);
						continue;
					}
					convert_TSHISTORY_to_KDATA(dwMarket, sCode, &(pDay[i]), &(pCommPacket->m_pKdata[dwCount]));
					kdata.Add(pCommPacket->m_pKdata[dwCount]);
					dwCount	++;
				}
				pCommPacket->m_dwCount	=	dwCount;
				if (pHeader->m_bDISK && pDay)
					delete [] pDay;
				if (kdata.GetSize() > 0)
				{
					kdata.AutoSetKType();
					UpdateStockContainerByKData(AfxGetStockContainer(), sCode, kdata);
					AfxGetDB().InstallKData(kdata);
				}

				// 通知各股票信息显示窗口,刷新行情数据
				for(i=0; i<m_awndRcvData.GetSize(); i++)
					::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataK, (LPARAM)pCommPacket);
	
				// 释放备份数据存储空间
				FreeCommPacket(pCommPacket);
			}
			break;

		case FILE_MINUTE_EX:						// 补分钟线数据
		case 83:	// 补分笔数据
			{
				RCV_MINUTE_STRUCTEx * pMin = pHeader->m_pMinute;
				ASSERT(pMin->m_head.m_dwHeadTag == EKE_HEAD_TAG);
				CMinute	minute;
				BOOL bAutoSaveKDataMin = AfxGetProfile().GetAutoSaveKDataMin(); // 作为分钟K线保存
				CString	sCode;
				DWORD	dwMarket = CStock::marketUnknown;
				
				// 分配备份数据存储空间
				COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataMinute, pHeader->m_nPacketNum);
				if (NULL == pCommPacket)
					return -1L;

				DWORD	dwCount	=	0;
				for(i=0; i<pHeader->m_nPacketNum; i++)
				{
					if (pMin[i].m_head.m_dwHeadTag == EKE_HEAD_TAG)
					{
						if (minute.GetSize() > 0)
						{
							CKData	kdMin(CKData::ktypeMin5);
							if (minute.ToKData(kdMin))
								AfxGetDB().InstallKData(kdMin);
							minute.RemoveAll();
						}
						sCode		=	pMin[i].m_head.m_szLabel;
						dwMarket	=	TSMarket_to_Market(pMin[i].m_head.m_wMarket);
						continue;
					}
					convert_TSMINUTE_to_MINUTE(dwMarket, sCode, &(pMin[i]), &(pCommPacket->m_pMinute[dwCount]));
					if (bAutoSaveKDataMin)
						minute.Add(pCommPacket->m_pMinute[dwCount]);
					dwCount	++;
				}
				pCommPacket->m_dwCount	=	dwCount;
				if (minute.GetSize() > 0)
				{
					CKData	kdMin(CKData::ktypeMin5);
					if (minute.ToKData(kdMin))
						AfxGetDB().InstallKData(kdMin);
				}

				// 保存至本地文件
				AfxGetDB().StoreMinute(pCommPacket->m_pMinute, pCommPacket->m_dwCount);

				// 通知各股票信息显示窗口,刷新行情数据
				for(i=0; i<m_awndRcvData.GetSize(); i++)
					::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataMinute, (LPARAM)pCommPacket);

				// 释放备份数据存储空间
				FreeCommPacket(pCommPacket);
			}
			break;

		case FILE_POWER_EX:							// 补充除权数据
			{
				RCV_POWER_STRUCTEx * pPower = pHeader->m_pPower;
				int	nPacketNum	=	pHeader->m_nPacketNum;
				CDRData	drdata;
				CString	sCode;
				DWORD	dwMarket = CStock::marketUnknown;

				if (pHeader->m_bDISK)
				{
					pPower	=	NULL;
					CFile	file;
					if (file.Open(pHeader->m_File.m_szFileName,CFile::modeRead))
					{
						ASSERT(file.GetLength() == pHeader->m_File.m_dwLen);
						nPacketNum	=	(int)(file.GetLength()/sizeof(RCV_POWER_STRUCTEx));
						if (nPacketNum > 0)
							pPower = new RCV_POWER_STRUCTEx[nPacketNum];
						if (pPower)
							file.Read(pPower, sizeof(RCV_POWER_STRUCTEx)*nPacketNum);
						file.Close();
					}
				}

				// 分配备份数据存储空间
				COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataDR, nPacketNum);
				if (NULL == pCommPacket)
					return -1L;

				DWORD	dwCount	=	0;
				for(i=0; i<nPacketNum && pPower; i++)
				{
					if (pPower[i].m_head.m_dwHeadTag == EKE_HEAD_TAG)
					{
						if (drdata.GetSize() > 0)
							AfxGetDB().InstallDRData(drdata);
						drdata.RemoveAll();
						sCode		=	pPower[i].m_head.m_szLabel;
						dwMarket	=	TSMarket_to_Market(pPower[i].m_head.m_wMarket);
						continue;
					}
					convert_TSPOWER_to_DRDATA(dwMarket, sCode, &(pPower[i]), &(pCommPacket->m_pDrdata[dwCount]));
					drdata.Add(pCommPacket->m_pDrdata[dwCount]);
					dwCount	++;
				}
				if (pHeader->m_bDISK && pPower)
					delete [] pPower;
				if (drdata.GetSize() > 0)
					AfxGetDB().InstallDRData(drdata);
				pCommPacket->m_dwCount	=	dwCount;

				// 通知各股票信息显示窗口,刷新行情数据
				for(i=0; i<m_awndRcvData.GetSize(); i++)
					::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataDR, (LPARAM)pCommPacket);

				// 释放备份数据存储空间
				FreeCommPacket(pCommPacket);
			}
			break;

		case FILE_MULTISORT_EX:					// 综合排名
			{
				RCV_MULTISORT_STRUCTEx * pMultisort = pHeader->m_pMultisort;
				
				// 分配备份数据存储空间
				COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataMultisort, pHeader->m_nPacketNum);
				if (NULL == pCommPacket)
					return -1L;

				for(i=0; i<pHeader->m_nPacketNum; i++)
				{
					convert_TSMULTISORT_to_MULTISORT(&(pMultisort[i]), &(pCommPacket->m_pMultisort[i]));
				}
				pCommPacket->m_dwCount	=	pHeader->m_nPacketNum;

				// 通知各股票信息显示窗口,刷新行情数据
				for(i=0; i<m_awndRcvData.GetSize(); i++)
					::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataMultisort, (LPARAM)pCommPacket);

				// 释放备份数据存储空间
				FreeCommPacket(pCommPacket);
			}
			break;

		case FILE_BASE_EX:						// 钱龙兼容基本资料文件,m_szFileName仅包含文件名
			{
				if (pHeader->m_bDISK)
				{
					AfxGetDB().InstallBaseText(pHeader->m_File.m_szFileName, AfxGetFileTitle(pHeader->m_File.m_szFileName));
				}
				else
				{
					AfxGetDB().InstallBaseText(LPCTSTR(pHeader->m_pData), pHeader->m_File.m_dwLen, AfxGetFileTitle(pHeader->m_File.m_szFileName));
				}

				// 分配备份数据存储空间
				COMMPACKET * pCommPacket = AllocCommPacket(CStock::dataCode, 1);
				if (NULL == pCommPacket)
					return -1L;
				CString	sTemp	=	AfxGetFileTitleNoExt(pHeader->m_File.m_szFileName);
				strncpy(pCommPacket->m_pStockcode[0].m_szCode, sTemp, min(sizeof(pCommPacket->m_pStockcode[0].m_szCode)-1,sTemp.GetLength()));
				pCommPacket->m_pStockcode[0].m_dwMarket	=	CStock::marketUnknown;

				// 通知各股票信息显示窗口,刷新行情数据
				for(i=0; i<m_awndRcvData.GetSize(); i++)
					::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataBasetext, (LPARAM)pCommPacket);

				// 释放备份数据存储空间
				FreeCommPacket(pCommPacket);
			}
			break;

		case FILE_NEWS_EX:						// 新闻类,其类型由m_szFileName中子目录名来定
			if (pHeader->m_bDISK)
			{
				AfxGetDB().InstallNewsText(pHeader->m_File.m_szFileName, AfxGetFileTitle(pHeader->m_File.m_szFileName));
			}
			else
			{
				AfxGetDB().InstallNewsText(LPCTSTR(pHeader->m_pData), pHeader->m_File.m_dwLen, AfxGetFileTitle(pHeader->m_File.m_szFileName));
			}
			// 通知各股票信息显示窗口,刷新行情数据
			for(i=0; i<m_awndRcvData.GetSize(); i++)
				::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA, CStock::dataNews, NULL);
			break;

		case FILE_HTML_EX:						// HTML文件,m_szFileName为URL
			AfxGetProfile().AddHTMLURLToExplorer(pHeader->m_File.m_szFileName);
			break;

		case FILE_SOFTWARE_EX:
			TRACE("%s\n",pHeader->m_File.m_szFileName);
			TRACE("%d\n",pHeader->m_File.m_dwLen);
			pBuffx = (PBYTE)pHeader->m_pData;
			break;

		case FILE_SHAZQDATA_EX:		//h上海国债净价交易
			break;
		case FILE_TYPE_RES:
			TRACE("MSG: CStkReceiver::OnStkDataOK, Replaced data \n");
			break;
		default:
			TRACE("Msg: CStkReceiver::OnStkReceiverData,Unkonw data type\n");
		}
		break;
	}

	return 0L;
}

2.void CStkReceiver::NotifyWndRcvData(WPARAM wParam, LPARAM lParam)发出

G:\stock\TskingVS2019\src\Client\StkNet\Src\StkReceiver.cpp

void CStkReceiver::NotifyWndRcvData(WPARAM wParam, LPARAM lParam)
{
	for(int i=0; i<m_awndRcvData.GetSize(); i++)
		::SendMessage(m_awndRcvData[i], WM_APP_STKRECEIVER_DATA , wParam, lParam);
}

前者是通视驱动发送过来的消息处理后再发送给各窗口,后者是请求行情服务器得到数据后发送给各窗口。

step 1:各视图向CStkReceiver注册自己的窗口句柄

step 2:各视图定义自己的消息及处理函数。ON_MESSAGE(WM_APP_STKRECEIVER_DATA, OnStkReceiverData)

step 3:CStkReceiver向通视注册,用OnStkReceiverTsdata()函数处理通视驱动发送过来的数据,处理后转发各个已注册窗口。

gSTOCKDLL.Stock_Init(m_hWnd,WM_APP_STKRECEIVER_TSDATA,RCV_WORK_SENDMSG)

行情服务器也注册了

LRESULT CStkReceiver::OnStkReceiverConnectserver(WPARAM wParam, LPARAM lParam)
{
	if (gNetTSDLL.LoadDriver("NetTS.dll")
		&& gNetTSDLL.Stock_Init(m_hWnd,WM_APP_STKRECEIVER_TSDATA,RCV_WORK_SENDMSG) > 0)
		return TRUE;
	return FALSE;
}

G:\stock\TskingVS2019\src\Client\NetTS\TSWnd.cpp

当队列有数据时,主动发送消息

// 分发数据线程,行情服务器主动推送线程 2019/06/08 by freeman
UINT TSDispatchThreadMain(LPVOID pParam)
{
	while(TRUE)
	{
		UINT nMsgType = 0;
		PRCV_DATA pRCV_DATA = NULL;
		if (CTSCache::GetInstance().PopPacket(nMsgType, pRCV_DATA))
		{
			ASSERT(pRCV_DATA && pRCV_DATA->m_pData);
			if (pRCV_DATA && pRCV_DATA->m_pData)
				CTSWnd::GetInstance().SendMessage(nMsgType, (LPARAM)pRCV_DATA);

 

 

请求行情

调用链条:ListView.cpp里面的定时器发起SendRequestQuote(FALSE)->调用StkNet项目里面的

AfxGetStkReceiver().RequestStockData(CStock::dataReport, stocks.GetData(), stocks.GetSize(), 0, 0);

->调用StkNet项目里面的gNetTSDLL.RequestStockData(nDataTypeTS, pStocks, nSize, nKType, nDataCount);

->痛过gNetTSDLL全局变量调用NetTS项目里面的函数

int WINAPI RequestStockData( int nDataType, TW_STOCK * pStocks, int nSize, int nKType, int nDataCount )

->调用NetTS项目里面的CTWSocket::GetInstance().RequestStockData( nDataType, pStocks, nSize, nKType, nDataCount );

在这个函数里面,构造发送Askbuffer的二进制字节流发送给行情服务器。

具体实现过程如下:

step 1:SListView.cpp的定时器,每隔3秒定时向行情服务器发起一次请求行情数据

void CSListView::OnTimer(UINT nIDEvent) 
{
	if (SLV_TIMER_REFRESH == nIDEvent)
	{
		SendRequestQuote(FALSE);
	}
	
	CView::OnTimer(nIDEvent);
}

 

step 2:执行SendRequestQuote(FALSE);发起请求行情

SendRequestQuote(FALSE),当bForced=FALSE时,会判断是否是在交易时间内,如是则发起请求,否则不发起请求。

请求

用AfxGetStkReceiver().RequestStockData(CStock::dataReport, stocks.GetData(), stocks.GetSize(), 0, 0);发起向行情服务器的主动请求

//发球请求行情
void CSListView::SendRequestQuote(BOOL bForced)
{
	if (!AfxGetStkReceiver().NetEngineIsWorking())
		return;

	static int nLastStart = -1;
	static int nLastCount = -1;

	BOOL bQuoteTipSended = FALSE;

	//判断目前是否在交易时间内
	BOOL bInTrade = CSPTime::InTradeTime(CSPTime::GetCurrentTime().GetTime(), 900);
	CView* pView = AfxGetStaticDoc()->GetActiveView();

	//如果是强制刷新. 则发起请求沪、深指数数据2019/06/06 by freeman
	if (bForced)
	{
		CStockContainer stocks;
		CStockInfo info;
		if (AfxGetStockContainer().GetStockInfo(STKLIB_CODE_MAIN, &info))
			stocks.Add(info);
		if (AfxGetStockContainer().GetStockInfo(STKLIB_CODE_MAINSZN, &info))
			stocks.Add(info);
		if (stocks.GetSize() > 0)
			AfxGetStkReceiver().RequestStockData(CStock::dataReport, stocks.GetData(), stocks.GetSize(), 0, 0);//发起请求行情:请求沪、深指数
	}

	//如果是强制刷新或者股票列表窗口处于激活状态,则发起请求当前窗口股票列表的数据
	if (bForced || pView == this)
	{
		CCellRange cr = m_Grid.GetVisibleNonFixedCellRange();//非固定
		int nStart = cr.GetMinRow() - 1;
		int nCount = cr.GetMaxRow() - cr.GetMinRow() + 1;
		CStockContainer& container = AfxGetSListStockContainer();
		container.Lock();
		if (nStart >= 0 && nCount > 0 && container.GetSize() > 0 && (bInTrade || bForced || nLastStart != nStart || nLastCount != nCount))
		{
			nLastStart = nStart;
			nLastCount = nCount;
			//从AfxGetSListStockContainer()中找出当前窗口的股票列表,并发送请求
			AfxGetStkReceiver().RequestStockData(CStock::dataReport, container.GetData()+nStart, min(nCount,container.GetSize()-nStart), 0, 0);//发起请求行情:请求当前窗口的股票列表
			bQuoteTipSended = (0 == nStart && nCount >= 9);
		}
		container.UnLock();
	}


	//如果目前是在交易时间内。2019/06/06 by freeman
	if (bInTrade)
	{
		// 预警系统所需股票行情刷新
		CAlarmCondContainer& conds = AfxGetProfile().GetAlarmCondContainer();
		CStockContainer stocks;
		stocks.SetSize(0, conds.GetSize() + 5);
		for (int i = 0; i < conds.GetSize(); i++)
		{
			ALARMCOND cond = conds.GetAt(i);
			CStockInfo info;
			if (AfxGetStockContainer().GetStockInfo(cond.m_szCode, &info))
				stocks.Add(info);
		}
		CStockInfo infoMain, infoMainSzn;
		if (AfxGetStockContainer().GetStockInfo(STKLIB_CODE_MAIN, &infoMain)
			&& AfxGetStockContainer().GetStockInfo(STKLIB_CODE_MAINSZN, &infoMainSzn))
		{
			stocks.Add(infoMain);
			stocks.Add(infoMainSzn);
		}

		//  行情QuoteTip 需要行情数据
		if (!bQuoteTipSended)
		{
			CStockContainer & container = AfxGetSListStockContainer();
			container.Lock();
			for (int i=0; i<9 && i<container.GetSize(); i++)
				stocks.Add(container.ElementAt(i));
			container.UnLock();
		}

		if (stocks.GetSize() > 0)
			AfxGetStkReceiver().RequestStockData(CStock::dataReport, stocks.GetData(), stocks.GetSize(), 0, 0);
	}
}

 

第二部分 切换到股票列表视图窗口

窗口激活时调用

void CSListView::OnActivateView(BOOL bActivate, CView* pActivateView, CView* pDeactiveView) 
{
	TRACE(_T("CSListView::OnActivateView %d\n"), bActivate);

	if (bActivate && m_bShouldUpdate)
	{
		OnUpdate(NULL, UPDATE_HINT_SLISTVIEW, NULL);//更新数据
		m_bShouldUpdate	= FALSE;
	}

	CView::OnActivateView(bActivate, pActivateView, pDeactiveView);
}

 

//数据更新 2019/06/08 by freeman
void CSListView::OnUpdate(CView* pSender, LPARAM lHint, CObject* pHint) 
{
	TRACE(_T("CSListView::OnUpdate %d %d %d\n"), pSender, lHint, pHint);
	if (UPDATE_HINT_SLISTVIEW != lHint)
		return;
	if (IsWindowVisible())
		SetFocus();
	int	nColumnCount	=	m_Grid.GetColumnCount();
	if (nColumnCount <= 0)
		return;

	if (GetParentFrame()->GetSafeHwnd() != AfxGetMainFrame()->GetActiveFrame()->GetSafeHwnd())
	{
		m_bShouldUpdate	=	TRUE;
		return;
	}
	m_bShouldUpdate	=	FALSE;

	CStockContainer & container = AfxGetSListStockContainer();
	container.Lock();

	m_Grid.DeleteNonFixedRows();

	// Progress
	CMainFrame	* pMainFrame = AfxGetMainFrame();
	if (pMainFrame)
	{
		pMainFrame->ShowProgressBar();
		pMainFrame->SetProgress(0);
		pMainFrame->SetMessageText(IDS_MAINFRAME_WAITING);
	}

	CUIntArray	anParams;
	anParams.SetSize(0, nColumnCount);
	for (int nCol=0; nCol < nColumnCount; nCol ++)
	{
		LPARAM	lParam	=	m_Grid.GetItemData(0, nCol);//获取第0行的数据
		anParams.Add(lParam);
	}

	CRect rectClient;
	GetClientRect(&rectClient);
	int nPageCount = 1 + rectClient.Height() / abs(m_Grid.GetFixedRowHeight()) + 1;

	//股票容器
	for (int i=0; i<container.GetSize(); i++)
	{
		CStockInfo & info = container.GetStockInfoByID(i);

		//插入一行,第0列为股票名称  2019/06/08 by freeman
		int nRow = m_Grid.InsertRow(info.GetStockName());
		m_Grid.SetItemData(nRow, 0, i);

		//显示除股票名称外的其他列信息
		for (int nCol=0; nCol<anParams.GetSize(); nCol++)
		{
			m_Grid.SetItemFormat(nRow, nCol, DT_CENTER|DT_VCENTER|DT_SINGLELINE);
			m_Grid.SetItemText(nRow, nCol, AfxGetVariantDispString(anParams[nCol], info, &container));
			m_Grid.SetItemBkColour(nRow, nCol, AfxGetProfile().GetColor(CColorClass::clrSListBK));
			m_Grid.SetItemFgColour(nRow, nCol, AfxGetVariantColor(anParams[nCol], info));
			if (anParams[nCol] == SLH_DATE)
				m_Grid.SetColumnWidth(nCol, 80);
		}

		if (i == nPageCount+5)
		{
			m_Grid.Invalidate();
			MSG		msg;
			while (::PeekMessage(&msg, NULL, NULL, NULL, PM_NOREMOVE))
				AfxGetApp()->PumpMessage();
		}
		if (pMainFrame)
			pMainFrame->SetProgress(100*i/container.GetSize());
	}

	// 统计列表的平均值
	//SetAverageItem(m_Grid, container, FALSE);

	// Sort If Needed
	if (-1 != m_nColSort)
	{
		LPARAM	lParam	=	m_Grid.GetItemData(0, m_nColSort);
		if (lParam >= 0)
		{
			CStockContainer::m_pSortContainer	=	&container;
			container.m_nSortVariantID	=	lParam;
			container.m_bSortAscend		=	m_bSortAscend;
			m_Grid.SortItems(ItemCompareFunc, 0, TRUE);
		}
	}

	if (pMainFrame)
	{
		pMainFrame->SetProgress(100);
		pMainFrame->HideProgressBar();
		pMainFrame->SetMessageText(IDS_MAINFRAME_FINISHED);
		pMainFrame->SetMessageText(IDS_HELPTIP_SLIST);
	}

	if (m_bFirstUpdate)
	{
		m_Grid.AutoSizeColumns();
	}
	else
	{
		m_Grid.SetColumnWidth(0, 80);
		m_Grid.SetColumnWidth(1, 80);
	}

	m_bFirstUpdate = FALSE;
	m_Grid.Invalidate();

	SendRequestQuote(TRUE);

	container.UnLock();
}

 

//股票数据发生变化 2019/06/08 by freeman
void CSListView::StockInfoChanged(LONG infoid, CStockInfo & info)
{
	CStockContainer & container = AfxGetSListStockContainer();

	container.Lock();

	// update grid value
	for (int nRow=m_Grid.GetRowCount()-1; nRow >= 0; nRow--)
	{
		LPARAM	id	=	m_Grid.GetItemData(nRow,0);
		if (id == infoid)
		{
			for (int nCol=0; nCol<m_Grid.GetColumnCount(); nCol++)
			{
				LPARAM lParam = m_Grid.GetItemData(0,nCol);
				m_Grid.SetItemText(nRow, nCol, AfxGetVariantDispString(lParam, info, &container));
				m_Grid.SetItemFgColour(nRow, nCol, AfxGetVariantColor(lParam, info));
			}
			m_Grid.RedrawRow(nRow);//重画一行 2019/06/08 by freeman
			break;
		}
	}

	container.UnLock();
}

 

第三部分 股票列表显示

 

股票列表指标项目定义

 

G:\stock\TskingVS2019\src\Client\StkLib\Include\Express.h

#if	!defined( __STKLIB_EXPRESS_H__ )
#define	__STKLIB_EXPRESS_H__

/***
	定义股票列表项的唯一标识.SListView 股票列表项定义 2019/06/08 by freeman
*/
	//	股票
#define	SLH_NONE					0
#define	SLH_MIN						1
#define	SLH_CODE					1	//	股票代码
#define	SLH_NAME					2	//	股票名称
	//	技术数据
#define	SLH_DATE					3	//	日期
#define	SLH_LASTCLOSE				4	//	昨收
#define	SLH_OPEN					5	//	今开
#define	SLH_CLOSE					6	//	收盘价
#define	SLH_HIGH					7	//	最高价
#define	SLH_LOW						8	//	最低价
#define	SLH_AVERAGE					9	//	均价
#define	SLH_DIFF					10	//	涨跌
#define	SLH_DIFFPERCENT				11	//	涨幅%
#define	SLH_SCOPE					12	//	震幅%
#define	SLH_VOLUME					13	//	成交量

 

G:\stock\TskingVS2019\src\Client\StkLib\Src\Profile.cpp

//设置股票列表缺省列的函数
void CStProfile::SetSListColumnsDefault( )
{
	// All Order
	m_anSListColumnsOrder.RemoveAll();
#ifdef CLKLAN_ENGLISH_US
	for( UINT i=SLH_MIN; i<=SLH_MAX; i++ )
	{
		m_anSListColumnsOrder.Add( i );
	}
#else
	m_anSListColumnsOrder.Add( SLH_NAME );
	m_anSListColumnsOrder.Add( SLH_CODE );
	for( UINT i=SLH_MIN; i<=SLH_MAX; i++ )
	{
		if( SLH_NAME != i && SLH_CODE != i )
			m_anSListColumnsOrder.Add( i );
	}
#endif
	for( int k=0; k<m_aSListColumnsUser.GetSize(); k++ )
	{
		m_anSListColumnsOrder.Add( m_aSListColumnsUser.ElementAt(k).m_nID );
	}

	// Show
	m_anSListColumnsShow.RemoveAll( );
	m_anSListColumnsShow.Copy( m_anSListColumnsOrder );
}

CSPDWordArray & CStProfile::GetSListColumnsShow( )
{
	if( m_anSListColumnsShow.GetSize() == 0 )
		SetSListColumnsDefault( );

	// Remove some dulplicate
	for( int i=m_anSListColumnsShow.GetSize()-1; i>=0; i-- )
	{
		for( int k=0; k<i; k++ )
		{
			if( m_anSListColumnsShow[i] == m_anSListColumnsShow[k] )
			{
				m_anSListColumnsShow.RemoveAt( i );
				SP_ASSERT( FALSE );
				break;
			}
		}
	}

	return m_anSListColumnsShow;
}

CSPDWordArray & CStProfile::GetSListColumnsOrder( )
{
	int	nRealCount = SLH_MAX-SLH_MIN+1 + m_aSListColumnsUser.GetSize();
	if( m_anSListColumnsOrder.GetSize() != nRealCount )
		SetSListColumnsDefault( );

	// Remove some dulplicate
	for( int i=m_anSListColumnsOrder.GetSize()-1; i>=0; i-- )
	{
		for( int k=0; k<i; k++ )
		{
			if( m_anSListColumnsOrder[i] == m_anSListColumnsOrder[k] )
			{
				m_anSListColumnsOrder.RemoveAt( i );
				SP_ASSERT( FALSE );
				break;
			}
		}
	}

	return m_anSListColumnsOrder;
}

CIndexContainer &CStProfile::GetSListColumnsUser( )
{
	return m_aSListColumnsUser;
}

窗口标题更新

ON_MESSAGE(WM_USER_GETVIEWTITLE, OnGetViewTitle)

G:\stock\TskingVS2019\src\Client\StkUI\StaticDoc.h

#define	WM_USER_GETVIEWTITLE		(WM_USER + 1)

发出消息

G:\stock\TskingVS2019\src\Client\StkUI\ChildFrm.cpp

void CChildFrame::OnUpdateFrameTitle(BOOL bAddToTitle)
{
	GetMDIFrame()->OnUpdateFrameTitle(bAddToTitle);

	if ((GetStyle() & FWS_ADDTOTITLE) == 0)
		return;     // leave child window alone!

	CView * pView = GetActiveView( );
	if (bAddToTitle)
	{
		TCHAR szText[256+_MAX_PATH];
		memset( szText, 0, sizeof(szText) );

		if (pView == NULL)
			lstrcpyn(szText, m_strTitle, 256+MAX_PATH);
		else
			pView->SendMessage( WM_USER_GETVIEWTITLE, 255+MAX_PATH, long(szText) );

		// set title if changed, but don't remove completely
		AfxSetWindowText(m_hWnd, szText);
	}
}

 

 

 

G:\stock\TskingVS2019\src\Client\StkLib\Include\Express.h

/***
	定义股票列表项的唯一标识.SListView 股票列表项定义 2019/06/08 by freeman
*/
	//	股票
#define	SLH_NONE					0
#define	SLH_MIN	 					1
#define	SLH_NO					    1	//	股票序号,extend by freeman
#define	SLH_CODE					2	//	股票代码
#define	SLH_NAME					3	//	股票名称
	//	技术数据
#define	SLH_DATE					4	//	日期
#define	SLH_LASTCLOSE				5	//	昨收
#define	SLH_OPEN					6	//	今开
#define	SLH_CLOSE					7	//	收盘价
#define	SLH_HIGH					8	//	最高价
#define	SLH_LOW						9	//	最低价
#define	SLH_AVERAGE					10	//	均价
#define	SLH_DIFF					11	//	涨跌
#define	SLH_DIFFPERCENT				12	//	涨幅%
#define	SLH_SCOPE					13	//	震幅%
#define	SLH_VOLUME					14	//	成交量
#define	SLH_AMOUNT					15	//	成交额
#define	SLH_VOLUP					16	//	外盘
#define	SLH_VOLDOWN					17	//	内盘
#define	SLH_DIFFPERCENT_MIN5		18	//	五分钟涨幅%
#define	SLH_SELLBUYRATIO			19	//	委比%
#define	SLH_SELLBUYDIFF				20	//	委量差
#define	SLH_BUYPRICE3				21	//	买价三
#define	SLH_BUYPRICE2				22	//	买价二
#define	SLH_BUYPRICE1				23	//	买价一
#define	SLH_SELLPRICE1				24	//	卖价一
#define	SLH_SELLPRICE2				25	//	卖价二
#define	SLH_SELLPRICE3				26	//	卖价三
#define	SLH_BUYVOLUME3				27	//	买量三
#define	SLH_BUYVOLUME2				28	//	买量二
#define	SLH_BUYVOLUME1				29	//	买量一
#define	SLH_SELLVOLUME1				30	//	卖量一
#define	SLH_SELLVOLUME2				31	//	卖量二
#define	SLH_SELLVOLUME3				32	//	卖量三

	//	主要基本指标
#define	SLH_REPORTTYPE				33	//	报表类型
#define	SLH_PE						34	//	市盈率
#define	SLH_PNETASSET				35	//	市净率
#define	SLH_PMAININCOME				36	//	市销率
#define	SLH_RATIO_PCASH				37	//	价格净现金比
#define	SLH_RATIO_CURRENCY			38	//	流通率
#define	SLH_RATIO_CHANGEHAND		39	//	换手率
#define	SLH_RATIO_VOLUME			40	//	量比
#define	SLH_RS						41	//	相对强度
#define	SLH_MARKETVALUE				42	//	总市值
#define	SLH_MARKETVALUEA			43	//	A股市值
#define	SLH_MARKETVALUEB			44	//	B股市值
	// ★偿债能力
#define	SLH_RATIO_LIQUIDITY			45	//	流动比率
#define	SLH_RATIO_QUICK				46	//	速动比率
#define	SLH_VELOCITY_RECEIVABLES	47	//	应收帐款周率
	// ★经营能力
#define	SLH_VELOCITY_MERCHANDISE	48	// 存货周转率
#define	SLH_MAIN_INCOME				49	// 主营业务收入
#define	SLH_CASH_PS					50	// 每股净现金流量
	// ★盈利能力
#define	SLH_PROFIT_MARGIN			51	// 主营业务利润率
#define	SLH_NETASSET_YIELD			52	// 净资产收益率
	// ★资本结构
#define	SLH_DATE_BEGIN				53	//	上市日期
#define	SLH_SHARE_COUNT_TOTAL		54	//	总股本
#define	SLH_SHARE_COUNT_A			55	//	流通A股
#define	SLH_SHARE_COUNT_B			56	//	流通B股
#define	SLH_SHARE_COUNT_H			57	//	流通H股
#define	SLH_SHARE_COUNT_NATIONAL	58	//	国有股
#define	SLH_SHARE_COUNT_CORP		59	//	法人股
#define	SLH_PROFIT_PSUD				60	// 每股未分配利润
#define	SLH_ASSET					61	// 总资产
#define	SLH_RATIO_HOLDERRIGHT		62	// 股东权益比率
#define	SLH_RATIO_LONGDEBT			63	// 长期负债率
#define	SLH_RATIO_DEBT				64	// 资产负债率
	// ★投资收益能力
#define	SLH_NETASSET_PS				65	// 每股净资产
#define	SLH_NETASSET_PS_REGULATE	66	// 调整每股净资产
#define	SLH_EPS						67	// 每股收益
#define	SLH_EPS_DEDUCT				68	// 扣除后每股收益
#define	SLH_NET_PROFIT				69	// 净利润
#define	SLH_MAIN_PROFIT				70	// 主营业务利润
#define	SLH_TOTAL_PROFIT			71	// 利润总额
#define	SLH_PROFIT_INC				72	// 主营利润增长率
#define	SLH_INCOME_INC				73	// 主营收入增长率
#define	SLH_ASSET_INC				74	// 总资产增长率
#define	SLH_ANNGAINS_AVERAGE		75	// 年平均收益率
#define	SLH_ANNGAINS_STDDEV			76	// 收益标准差
#define	SLH_BETA					77	// β值
#define	SLH_SHARP					78	// 夏普风险指数
#define	SLH_TRADE					79	// 行业
#define	SLH_PROVINCE				80	// 省份
#define	SLH_MAX						80

#define	SLH_INVALID					(UINT(-1))
#define	SLH_USERDEFINE_BEGIN		10000

#define	SLH_WIDTH_MIN				60


#define	SLMAX_SLHTEXTLEN			256