一、启动过程简介
主程序启动原理1:获取版本号
主程序启动原理2:加载配置文件,设置主目录 E:\Stock\Tsking\src\StkUI\StkUI.cpp
主程序启动原理3:根据加载的配置文件,设置主目录 AfxGetProfile().GetWorkDirectory()==E:\Stock\Tsking\src\Bin
程序启动原理4:设置注册表
程序启动原理5:Empty Temp Directory
程序启动原理:6,设置网络数据库
程序启动原理:7,设置本地数据库目录、设置主目录、指定使用SelfDB接口
程序启动原理:9 .从磁盘加载本地数据,写入到容器。
程序启动原理:10.启动“连接行情服务器”对话框,下载数据(从数据服务器下载,写入本地磁盘文件)
程序启动原理:11.下载数据后,本地磁盘数据有更新,便用LoadProgram((HWND)pMainFrame,0,1000);再次重新装载数据到容器。
二、启动代码如下:
BOOL CStkUIApp::InitInstance()
{
// Standard initialization
// If you are not using these features and wish to reduce the size
// of your final executable, you should remove from the following
// the specific initialization routines you do not need.
AfxSocketInit(); //网路请求socket
AfxOleInit(); //表格控件?
// 程序只能启动一个实例
m_pMutex = CreateMutex(NULL, FALSE, _T("TsKing"));
if (m_pMutex != NULL)
{
if (GetLastError() == ERROR_ALREADY_EXISTS)
{
CloseHandle(m_pMutex);
m_pMutex = NULL;
AfxMessageBox("程序已经运行!"); //add by freeman 2019/06/08
return FALSE;
}
}
//step 1:主程序启动原理:获取版本号
GetVersionNumber();
//step 2:读取配置文件,获取配置参数
//程序启动原理:1.StkUI.cpp中,加载配置文件,在E:\Stock\Tsking\src\StkLib\Src\Profile.cpp,配置文件名为:profile.set
AfxGetProfile().LoadProfile();
//step 3:设置主目录
::SetCurrentDirectory(AfxGetProfile().GetWorkDirectory()); //AfxGetProfile().GetWorkDirectory()==E:\Stock\Tsking\src\Bin
AfxGetSView().Load(AfxGetProfile().GetSViewFile()); //AfxGetProfile().GetSViewFile()==E:\Stock\Tsking\src\Bin\profile.dat
// 修改注册表,保存路径和版本。Change the registry key under which our settings are stored.
// TODO: You should modify this string to be something appropriate
// such as the name of your company or organization.
/**********设置注册表********
* 前面定义的常量
* TCHAR szRegKeyCompany[] = _T("Tsking Stock Software");
* TCHAR szRegKeyApp[] = _T("Tsking");
AfxGetProfile().GetWorkDirectory()=G:\stock\TskingVS2019\src\Client\Bin\
AfxGetProfile().GetVersion()=3.10
***********************************/
//程序启动原理:3.设置注册表
// SetRegistryKey( szRegKeyCompany );
AfxRegSetValue(szRegKeyCompany, szRegKeyApp, "Install", "Path", AfxGetProfile().GetWorkDirectory());
AfxRegSetValue(szRegKeyCompany, szRegKeyApp, "Install", "Version", AfxGetProfile().GetVersion());
// 修改INI文件名
BOOL bEnable = AfxEnableMemoryTracking(FALSE);
if (m_pszProfileName) free((void*)m_pszProfileName);
m_pszProfileName = _tcsdup(AfxGetProfile().GetWorkDirectory() + m_pszExeName + ".ini");
AfxEnableMemoryTracking(bEnable);
LoadStdProfileSettings(64); // Load standard INI file options (including MRU)
// Register the application's document templates. Document templates
// serve as the connection between documents, frame windows and views.
// Enable DDE Execute open
EnableShellOpen();
AfxUnregisterShellFileTypes();
// AfxRegisterShellFileTypes(FALSE);
AfxGetProfile().LoadProfile();
//程序启动原理:4.Empty Temp Directory
{
CNetDatabase netdb;
if (netdb.SetRootPath(AfxGetProfile().GetSelfDBPath(), IStStore::dbtypeSelfDB))
netdb.EmptyTempDirectory();
}
////AfxGetNetDB()是一个静态的CNetDatabase类,* g_pdb指向此类,
//程序启动原理:5,设置网络数据库
AfxSetDB(&AfxGetNetDB());
//*/
//程序启动原理:6,设置本地数据库目录、设置主目录、指定使用钱龙接口
// Load Data
/*功能:从物理盘读取数据到本地数据库
1.根据设定的主目录,如果不存在子目录,则创建子目录
2.指定主用哪个自有数据库,这里指定的是selfdb。可选用selfdb,qianlong、shenglong中的哪个为主,取数时的时候,只有指定的IStStore::dbtypeSelfDB取不到数时,才会用循环从其他几个注册了的数据库接口来获取数据
3.注册qianlong的实现,取数时的时候,只有指定的数据接口(如:IStStore::dbtypeSelfDB)取不到数时,才会用循环从其他几个注册了的数据库接口来获取数据
*/
char szErr[1024];
if (!AfxInitializeDB(szErr, sizeof(szErr)))
{
AfxMessageBox(szErr, MB_OK | MB_ICONINFORMATION);
return FALSE;
}
// 程序启动原理:7:初始化股票容器,把从本地数据库的数据填充到股票数组容器中
AfxGetStockContainer().Load(&AfxGetDB(), NULL, NULL); // 这里只读取证券代码表、财务。//调用OnDataChanged(),该函数里面设置财务数据、技术指标、股票均价等。
AfxGetDomainContainer().Load(AfxGetProfile().GetDomainFile()); // 读取板块 ;AfxGetProfile().GetDomainFile()=GetWorkDirectory() + "data\\domain.dat" by freeman
AfxGetGroupContainer().Load(AfxGetProfile().GetGroupFile()); // 读取自选板块。实际是自选股和省份板块。AfxGetProfile().GetGroupFile()=GetWorkDirectory() + "data\\group.dat" by freeman
// 分析标准外壳命令、DDE、打开文件操作的命令行
CCommandLineInfo cmdInfo;
ParseCommandLine(cmdInfo);
#ifdef _TSK_MAINFRAME
// 注册应用程序的文档模板。文档模板
// 将用作文档、框架窗口和视图之间的连接
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CStaticDoc),
RUNTIME_CLASS(CMainFrame), // 主 SDI 框架窗口
RUNTIME_CLASS(CSListView));
if (!pDocTemplate)
return FALSE;
AddDocTemplate(pDocTemplate);
// 调度在命令行中指定的命令。如果
// 用 /RegServer、/Register、/Unregserver 或 /Unregister 启动应用程序,则返回 FALSE。
if (!ProcessShellCommand(cmdInfo))
return FALSE;
#else
// Create MainFrame and Static Doc and child frames and static views
CStaticDoc* pStaticDoc = CStaticDoc::OpenDocumentFile(NULL);
if (pStaticDoc == NULL)
{
AfxMessageBox(IDS_STATICDOC_FAILED, MB_OK | MB_ICONINFORMATION);
return FALSE;
}
pStaticDoc->m_bAutoDelete = FALSE;
CString strAppTitle;
strAppTitle.LoadString(AFX_IDS_APP_TITLE);
pStaticDoc->SetTitle(strAppTitle);
SetStaticDoc(pStaticDoc);
// Create MainFrame
CMainFrame* pMainFrame = CMainFrame::CreateNewFrame();
//SendMessage(AfxGetMainFrame()->GetSafeHwnd(), WM_INPUTLANGCHANGEREQUEST, 1, 0x04090409);
if (pMainFrame == NULL)
{
AfxMessageBox(IDS_STATICDOC_FAILED, MB_OK | MB_ICONINFORMATION);
delete pStaticDoc;
SetStaticDoc(NULL);
return FALSE;
}
SetMainWnd(pMainFrame);
#endif
// 股票行情接收
AfxGetStkReceiver().CreateReceiver(m_pMainWnd);
AfxGetStkReceiver().NetEngineSetAutoReport(FALSE);
//程序启动原理:8,Show Startup Window 启动连接行情服务器对话框 by freeman
//CStartupDlg startup;
//CBitmap bmp;
//bmp.LoadBitmap(IDB_DLGLEFTLOGO);
//startup.SetBitmap((HBITMAP)bmp.GetSafeHandle());
//startup.SetLoadProgramFunc(LoadProgram);
//if (IDOK != startup.DoModal())
//{
// pMainFrame->SendMessage(WM_CLOSE);
// return FALSE;
//}
LoadProgram((HWND)pMainFrame, 0, 1000);
//Show Splash Window
/*CSplashDlg splash;
CBitmap bmp;
bmp.LoadBitmap(IDB_SPLASH);
splash.SetBitmap(HBITMAP(bmp.GetSafeHandle()));
splash.Create(NULL, 0, AfxGetSView().GetU(), AfxGetSView().GetS(), AfxGetVersionString(), FALSE, 0, STKLIB_MAX_PROGRESS);
splash.ShowWindow(SW_SHOW);
splash.UpdateWindow();
LoadProgram(splash.GetSafeHwnd(), 0, STKLIB_MAX_PROGRESS);
splash.DestroyWindow();*/
//
// //////////////////////////////////////////////////////////////
// Open Simulation Strategy
if (GetFirstStrategyPosition() == NULL)
{
if (OpenLastOpenedStrategy() == 0)
OpenDefaultStrategy();
}
//if (!AfxGetStaticDoc()->GetViewIfExist(RUNTIME_CLASS(CSListView)))
// AfxGetStaticDoc()->ShowStaticView(RUNTIME_CLASS(CSListView), TRUE);
//AfxSwitchToStaticView(RUNTIME_CLASS(CSListView));
AfxGetProfile().SetCurrentStock(STKLIB_CODE_SZZS, FALSE);
//装载键盘精灵数据
((CMainFrame*)m_pMainWnd)->m_SearchBox.InitStocks(TRUE, TRUE, TRUE);
((CMainFrame*)m_pMainWnd)->m_SearchBox.SetCurrentWindowText();
//*//////////////////////////////////////////////////////////////
// Dispatch commands specified on the command line
if (CCommandLineInfo::AppUnregister == cmdInfo.m_nShellCommand)
{
AfxUnregisterShellFileTypes();
if (!cmdInfo.m_bRunEmbedded)
AfxMessageBox(AFX_IDP_UNREG_DONE, MB_OK | MB_ICONINFORMATION);
if (m_pCmdInfo == NULL)
{
m_pCmdInfo = new CCommandLineInfo;
m_pCmdInfo->m_nShellCommand = CCommandLineInfo::AppUnregister;
}
return FALSE;
}
// Dispatch commands specified on the command line
if (cmdInfo.m_nShellCommand == CCommandLineInfo::FileOpen)
{
OpenStrategyFile(cmdInfo.m_strFileName);
AfxSwitchToStaticView(RUNTIME_CLASS(CSimuView));
}
// 启动通视接收系统,不显示选择接收系统对话框
//AfxGetStkReceiver().EngineBeginWorking(FALSE);
// The one and only window has been initialized, so show and update it.
// Load Last WindowPlacement
WINDOWPLACEMENT wp;
if (AfxGetProfile().GetWindowPlacement(&wp))
m_pMainWnd->SetWindowPlacement(&wp);
else
m_pMainWnd->ShowWindow(SW_SHOWMAXIMIZED);
// Enable drag/drop open
//m_pMainWnd->DragAcceptFiles();
// Check New Version
m_pMainWnd->SetTimer(TIMER_AUTOUPDATE, 5000, NULL); //检查版本升级的定时器 by freeman
return TRUE;
}
三、启动过程详细介绍
1.读取设置文件
//step 2:读取配置文件,获取配置参数
//程序启动原理:1.StkUI.cpp中,加载配置文件,在E:\Stock\Tsking\src\StkLib\Src\Profile.cpp,配置文件名为:profile.set
AfxGetProfile().LoadProfile();
//step 3:设置主目录
::SetCurrentDirectory(AfxGetProfile().GetWorkDirectory()); //AfxGetProfile().GetWorkDirectory()==E:\Stock\Tsking\src\Bin
AfxGetSView().Load(AfxGetProfile().GetSViewFile()); //AfxGetProfile().GetSViewFile()==E:\Stock\Tsking\src\Bin\profile.dat
2.初始化本地数据库,主要功能:负责把物理盘上的文件读取到本地数据库中
char szErr[1024];
if (!AfxInitializeDB(szErr, sizeof(szErr)))
{
AfxMessageBox(szErr, MB_OK | MB_ICONINFORMATION);
return FALSE;
}
/*功能:
1.根据设定的主目录,如果不存在子目录,则创建子目录
2.指定主用哪个自有数据库,这里指定的是selfdb。可选用selfdb,qianlong、shenglong中的哪个为主,取数时的时候,只有指定的IStStore::dbtypeSelfDB取不到数时,才会用循环从其他几个注册了的数据库接口来获取数据
3.注册qianlong的实现,取数时的时候,只有指定的数据接口(如:IStStore::dbtypeSelfDB)取不到数时,才会用循环从其他几个注册了的数据库接口来获取数据
*/
CStDatabase & AfxGetDB()
{
if( g_pdb ) return *g_pdb;
static CStDatabase g_stdatabase;
return g_stdatabase;
}
G:\stock\TskingVS2019\src\Client\StkUI\StkUI.cpp
调用链条:StkUI.cpp调用StkLib.cpp中的AfxInitializeDB( LPSTR lpErr, UINT nMaxSize )初始化本地数据库->设置一个主用数据接口->注册其他数据接口
从本地数据库加载一些数据时,首先用主用接口获取,如没有则循环调用其它接口获取,如果其中某个接口能返回所需要的数据,则中断循环。
但加载另一些数据时,则直接循环获取,如果其中某个接口能返回所需要的数据,则中断循环。如加载分时线。所以自己写的数据接口要注册
/*功能:
1.根据设定的主目录,如果不存在子目录,则创建子目录
2.指定主用哪个自有数据库,这里指定的是selfdb。可选用selfdb,qianlong、shenglong中的哪个为主,取数时的时候,只有指定的IStStore::dbtypeSelfDB取不到数时,才会用循环从其他几个注册了的数据库接口来获取数据
3.注册qianlong的实现,取数时的时候,只有指定的数据接口(如:IStStore::dbtypeSelfDB)取不到数时,才会用循环从其他几个注册了的数据库接口来获取数据
*/
char szErr[1024];
if (!AfxInitializeDB(szErr, sizeof(szErr)))
{
AfxMessageBox(szErr, MB_OK | MB_ICONINFORMATION);
return FALSE;
}
G:\stock\TskingVS2019\src\Client\StkLib\Src\AfxCore.cpp
//////////////////////////////////////////////////////////////////////////////////
// database functions 功能://设置本地数据库目录、设置主目录、指定使用钱龙接口
BOOL AfxInitializeDB( LPSTR lpErr, UINT nMaxSize )
{
if( lpErr && nMaxSize > 0 )
memset( lpErr, 0, nMaxSize );
//根据设定的主目录,如果不存在子目录,则创建子目录
if( !CStDatabase::CreateSelfDB( AfxGetProfile().GetSelfDBPath() ) )
{
if( lpErr ) strncpy( lpErr, db_errcreateselfdb, min(nMaxSize-1,strlen(db_errcreateselfdb)) );
return FALSE;
}
//指定主用哪个自有数据库,这里指定的是selfdb。可选用selfdb,qianlong、shenglong中的哪个为主,取数时的时候,只有指定的IStStore::dbtypeSelfDB取不到数时,才会用循环从其他几个注册了的数据库接口来获取数据
if( !AfxGetDB().SetRootPath( AfxGetProfile().GetSelfDBPath(), IStStore::dbtypeSelfDB ) )
{
if( lpErr ) strncpy( lpErr, db_errrootpath, min(nMaxSize-1,strlen(db_errrootpath)) );
return FALSE;
}
//注册qianlong的实现,取数时的时候,只有指定的数据接口(如:IStStore::dbtypeSelfDB)取不到数时,才会用循环从其他几个注册了的数据库接口来获取数据
CSPString strQianlongPath = AfxGetProfile().GetQianlongPath();
if( strQianlongPath.GetLength() > 0
&& !AfxGetDB().AddAssistantRootPath( strQianlongPath, IStStore::dbtypeQianlong ) )
{
if( lpErr ) strncpy( lpErr, db_errqianlongpath, min(nMaxSize-1,strlen(db_errqianlongpath)) );
return FALSE;
}
return TRUE;
}
2.从本地加载股票代码表、财务数据、K线数据等
// 初始化,装在本地盘上的股票数据 2019/06/09 by freeman
AfxGetStockContainer().Load(&AfxGetDB(), NULL, NULL); // 读取证券信息,代码表、财务、除权、K线数据、计算其他指标等
AfxGetDomainContainer().Load(AfxGetProfile().GetDomainFile()); // 读取板块
AfxGetGroupContainer().Load(AfxGetProfile().GetGroupFile()); // 读取自选板块
2.1 AfxGetStockContainer().Load(&AfxGetDB(), NULL, NULL);
step 2.1.1 从本地文件读取股票代码表,添加到全局变量
//加载代码表
int nLen = pDB->LoadCodetable( *this );
step 2.1.2 建立一张股票代码的哈希索引表
格式:股票代码,股票位置
SetMap( );
//加载代码表、加载财务数据 by freeman
BOOL CStockContainer::Load( CStDatabase * pDB, PROGRESS_CALLBACK fnCallback, void *cookie, int nProgStart, int nProgEnd )
{
CSPMutex::Scoped l(m_mutex);
SP_ASSERT( nProgStart <= nProgEnd );
SP_ASSERT( pDB );
if( nProgStart > nProgEnd || NULL == pDB )
return FALSE;
Clear( );
//加载代码表
int nLen = pDB->LoadCodetable( *this );
SetMap( );
//加载财务数据
pDB->LoadBasetable( *this );
// pDB->LoadKDataCache( *this, fnCallback, cookie, nProgStart, nProgEnd );
m_nType = typeAll;
m_dwDate = -1;
//数据发生变化,函数里面设置财务数据、技术指标、股票均价等。
OnDataChanged( );
return TRUE;
}
//数据发生变化,函数里面设置财务数据、技术指标、股票均价等。
void CStockContainer::OnDataChanged( )
{
CSPMutex::Scoped l(m_mutex);
DWORD dateLatest = m_dwDate;
if( -1 == dateLatest )
AfxGetStockContainer().GetLatestTechDate( &dateLatest );
for( int i=0; i<GetSize(); i++ )
{
CStockInfo & info = ElementAt(i);
//设置财务数据
info.StatBaseIndex( dateLatest );
//设置技术指标
info.StatTechIndex( dateLatest );
}
//设置股票均价
SetAverage( );
}
3.加载完成后计算相关技术指标
4.启动行情刷新程序