ADO+virtual list的问题

法尔我给他 2009-10-01 06:26:25
在一个对话框中,使用CListCtrl来显示由ADOX的_RecordsetPtr得到的记录集,由于记录过多,想采用virtual list来显示,
现在 我知道需要为CListCtrl
1.添加LVS_OWNERDATA属性
2.添加消息:ON_NOTIFY(LVN_GETDISPINFO, IDC_LIST1, GetDispInfo)
3.执行 m_List.SetItemCount(nCount);
4.在void CDlgT::GetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)中进行添加需要显示的数据
大致是这个样子的
void CDlgT::GetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
LV_ITEM* pItem= &(pDispInfo)->item;
if (pItem->mask & LVIF_TEXT) {
switch(pItem->iSubItem)
{
case 0:
{
lstrcpy(pItem->pszText, "ceshi");
break;
}
case 1:
{
lstrcpy(pItem->pszText, "ceshi2");
break;
}
}
}
}

做完这些后,我发现,根本不执行 CDlgT::GetDispInfo()这个函数呀,

又试着在执行 m_List.SetItemCount(nCount);之前,添加nCount个 空 item,
并把CDlgT::GetDispInfo()改成这样:

void CDlgT::GetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
LV_ITEM* pItem= &(pDispInfo)->item;
if (pItem->mask & LVIF_TEXT) {
m_List.SetItemText(pItem->iItem,0,_T("ceshi"));
m_List.SetItemText(pItem->iItem,1,_T("ceshi2"));
}
}

这样是执行CDlgT::GetDispInfo()了,但是却一直在刷新,屏幕闪烁的频率和闪光灯一样(第一列不闪烁,其他列一直在闪烁),这是怎么回事呀?

...全文
312 35 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
35 条回复
切换为时间正序
请发表友善的回复…
发表回复
cct8 2009-12-27
  • 打赏
  • 举报
回复
::memset(&m_Personnel, NULL, sizeof(m_Personnel));

写出这样的语句,就是没理解memset是干啥的。

第二个参数,就是一个字节。你罗嗦点写0x00,不罗嗦就写0。写NULL是啥玩意?虽然NULL在Windows里面的定义就是0,但是传统上指的是指针。
fengbangyue 2009-10-19
  • 打赏
  • 举报
回复
我也遇见同样的问题
法尔我给他 2009-10-03
  • 打赏
  • 举报
回复
找到问题的原因了,原来我的CListCtrl根本就不是virtual list,
这样
UINT uListStyle = LVS_EX_FULLROWSELECT|LVS_EX_GRIDLINES|LVS_OWNERDATA;
m_Llist.SetExtendedStyle(uListStyle);
设置是无效的
我在资源文件总为我的CListCtrl添加了LVS_OWNERDATA属性才有效的,谢谢大大的回答,太感谢了!

我的数据太多了,一次全部提取,耗时:59秒,(40W记录),消耗内存 440M还要多,所以,我每次提取1000条(因为我的这些记录一般情况时不需要全看的),需要的item不在数组中,就重新从记录集总获取,速度不错,内存消耗也不大,当然了,一直向下翻,最终还是要达到那个440m的内存消耗的,不过从直观上来看,已经好多了!
谢谢!
waitlife 2009-10-03
  • 打赏
  • 举报
回复
不执行,肯定是你的消息响应函数问题啦,你看我的:
头文件中的声明:

afx_msg void OnLvnGetdispinfoCheckreclist(NMHDR *pNMHDR, LRESULT *pResult);

消息响应声明:

ON_NOTIFY(LVN_GETDISPINFO, IDC_CHECKRECLIST, &CCheckRecDlg::OnLvnGetdispinfoCheckreclist)
法尔我给他 2009-10-03
  • 打赏
  • 举报
回复
不好意思,昨天有事,今天才来得及测试,问题依旧呀,根本不执行GetDispInfo()这个函数呀,我在看看和例子有什么差别!
晕死了,这是什么问题呀?
那位大大知道原因,告诉小弟一下,谢谢!
法尔我给他 2009-10-03
  • 打赏
  • 举报
回复
不好意思,昨天有事,今天才来得及测试,问题依旧呀,根本不执行GetDispInfo()这个函数呀,我在看看和例子有什么差别!
晕死了,这是什么问题呀?
那位大大知道原因,告诉小弟一下,谢谢!
法尔我给他 2009-10-02
  • 打赏
  • 举报
回复
一会我测试一下,吃个饭先,谢谢大大们的回答!
waitlife 2009-10-02
  • 打赏
  • 举报
回复
你退出查询的时候,就可以释放内存啦,如果你查一次,读一次,刷新速度会比较慢。
而且,现在电脑的内存基本上都有1G,数据量大的时候,查询占100M内存也没什么,况且查询完就释放了
waitlife 2009-10-02
  • 打赏
  • 举报
回复
我测试的时候,240202条记录,19.7秒,内存占用85M(启动时有50M),看你的需求啦
MoXiaoRab 2009-10-02
  • 打赏
  • 举报
回复
数据是一次性读进去的,但是每次显示都是List向主窗体请求数据进行显示的
法尔我给他 2009-10-02
  • 打赏
  • 举报
回复
看到了,这也太强了吧,一次读入,是不是对内存需要太大了,我的access数据库是100M(40W条记录),如果全读入,内存消耗太大了吧!
waitlife 2009-10-02
  • 打赏
  • 举报
回复
你看代码就知道啦,肯定是一次性把所有记录都读入结构体中(你原来的方法是看一个查一个)。
法尔我给他 2009-10-02
  • 打赏
  • 举报
回复
[Quote=引用 14 楼 tr0j4n 的回复:]
是自动填充的没错。
你说
===========
大致是这个样子的
void CDlgT::GetDispInfo(NMHDR* pNMHDR, LRESULT* pResult)
{
LV_DISPINFO* pDispInfo = (LV_DISPINFO*)pNMHDR;
LV_ITEM* pItem= &(pDispInfo)->item;
if (pItem->mask & LVIF_TEXT) {
switch(pItem->iSubItem)
{
case 0:
                          {
lstrcpy(pItem->pszText, "ceshi");
break;
                          }
case 1:
                          {
lstrcpy(pItem->pszText, "ceshi2");
break;
                          }
}
}
}

====================================
你是从数据库里面批量取了一批数据吧?是不是该处理下LVN_ODCACHEHINT
[/Quote]

关于LVN_ODCACHEHINT
的处理,还不会,看了MSDN上的解释很不明白,正在看,大大有什么例子吗?
谢谢
法尔我给他 2009-10-02
  • 打赏
  • 举报
回复
非常感谢,是一次将Recordset的记录都读到结构体中,还是一次读取一个,或者数个?

我还没来得及看,希望大大能告诉我一下,现在就开始看了

太谢谢了!
waitlife 2009-10-02
  • 打赏
  • 举报
回复
希望对你有帮助。
waitlife 2009-10-02
  • 打赏
  • 举报
回复
显示部分:

void CPersonnelDlg::OnLvnGetdispinfoPersonnellist(NMHDR *pNMHDR, LRESULT *pResult)
{
NMLVDISPINFO *pDispInfo = reinterpret_cast<NMLVDISPINFO*>(pNMHDR);
// TODO: 在此添加控件通知处理程序代码
LV_ITEM* pItem = &(pDispInfo)->item;
INT_PTR iItem = pItem->iItem;

if (pItem->mask & LVIF_TEXT)
{
CString straccid, strpname, strsex, strppwd, strcardno, strdeptname, strstationname, straccstatelegal, straccstate, strcrtime, strmotime, strnote, strpertype, strduty, stridentityno, strnname;
::memset(&m_Personnel, NULL, sizeof(m_Personnel));
INT_PTR iCount = m_PersonnelArray.GetCount();
if(iItem > iCount)
{
CString strMsg;
strMsg = _T("列表控件超过结构体范围");

BCGP_MSGBOXPARAMS params;
params.lpszCaption = g_strCap;
params.lpszText = strMsg;
params.dwStyle = MB_OK | MB_ICONERROR;
params.bUseNativeControls = FALSE;
params.bUseNativeCaption = params.bUseNativeControls;
int result = BCGPMessageBoxIndirect(¶ms);
//AfxMessageBox(_T("\n超过结构体范围!"));
g_TraceDbgMsg(CAT_SYS, KQMJSYS_STATUS_ERROR, g_Login.oprid, g_Login.oprname, _T("账户管理-刷新界面失败,超过结构体范围"));
return;
}
m_Personnel = m_PersonnelArray.GetAt(iItem);
switch(pItem->iSubItem)
{
case 0://账号
{
straccid.Format(_T("%ld"), m_Personnel.accid);
_tcscpy_s(pItem->pszText, pItem->cchTextMax, straccid);
}
break;
case 1://姓名
{
strpname = m_Personnel.pname;
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strpname);
}
break;
case 2://性别
{
if(m_Personnel.sex == 0)
strsex = _T("男");
else
strsex = _T("女");
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strsex);
}
break;
case 3://密码
{
strppwd = m_Personnel.ppwd;
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strppwd);
}
break;
case 4://卡号
{
strcardno.Format(_T("%08X"), m_Personnel.cardno);
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strcardno);
}
break;
case 5://身份
{
strstationname = m_Personnel.stationname;
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strstationname);
}
break;
case 6://部门
{
strdeptname = m_Personnel.deptname;
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strdeptname);
}
break;
case 7://工号
{
stridentityno = m_Personnel.identityno;
_tcscpy_s(pItem->pszText, pItem->cchTextMax, stridentityno);
}
break;
case 8://有效标志
{
if(m_Personnel.accstatelegal == 0)
straccstatelegal = _T("无效");
else
straccstatelegal = _T("有效");
_tcscpy_s(pItem->pszText, pItem->cchTextMax, straccstatelegal);
}
break;
case 9://账户状态
{
switch(m_Personnel.accstate)
{
case 0:
straccstate = _T("正常");
break;
case 1:
straccstate = _T("挂失");
break;
case 2:
straccstate = _T("停用");
break;
case 3:
straccstate = _T("撤户");
break;
default:
straccstate = _T("不详");
break;
}
_tcscpy_s(pItem->pszText, pItem->cchTextMax, straccstate);
}
break;
case 10://人员类型
{
switch(m_Personnel.pertype)
{
case 0:
strpertype = _T("人事");
break;
case 1:
strpertype = _T("教务");
break;
case 2:
strpertype = _T("人事教务");
break;
case 3:
strpertype = _T("不考勤");
break;
default:
strpertype = _T("不详");
break;
}
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strpertype);
}
break;
case 11://担任职务
{
strduty = m_Personnel.duty;
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strduty);
}
break;
case 12://建立时间
{
strcrtime = m_Personnel.crtime.Format(_T("%Y-%m-%d %H:%M:%S"));
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strcrtime);
}
break;
case 13://更新时间
{
strmotime = m_Personnel.motime.Format(_T("%Y-%m-%d %H:%M:%S"));
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strmotime);
}
break;
case 14://民族
{
strnname = m_Personnel.nname;
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strnname);
}
break;
case 15://备注
{
strnote = m_Personnel.note;
_tcscpy_s(pItem->pszText, pItem->cchTextMax, strnote);
}
break;
}
}
//if (pItem->mask & LVIF_IMAGE) //valid image?
//{
// pItem->iImage = m_Items[iItem].m_iImage;
//}
*pResult = 0;
}
waitlife 2009-10-02
  • 打赏
  • 举报
回复
以下是关键性代码,这是我的一个项目中的实际应用:

struct tagPERSONNEL_FIND
{
long accid;
TCHAR pname[32];
int sex;
TCHAR ppwd[32];
unsigned long cardno;
int cardtype;
int stationid;
TCHAR stationname[32];
int bldept;
TCHAR deptname[32];
TCHAR identityno[32];
int accstatelegal;
int accstate;
int pertype;
TCHAR duty[256];
COleDateTime crtime;
COleDateTime motime;
COleDateTime leavetime;
TCHAR note[256];
int nid;
int epnid;
TCHAR nname[32];
};
CArray<struct tagPERSONNEL_FIND, struct tagPERSONNEL_FIND> m_PersonnelArray;
void CPersonnelDlg::GetSQLToVirtualList()
{
c_Progress.SetPos(0);
if(c_PersonnelList.GetItemCount() > 0)
{
c_PersonnelList.DeleteAllItems();
this->UpdateWindow();
}
c_PersonnelSum.SetWindowText(_T("请稍候,正在统计系统账户记录数量..."));
long lRecordCount = 0;
if(g_ADO.ExecRs(strSQLCount))
{
if(!g_ADO.m_pRecordSet->adoBOF && !g_ADO.m_pRecordSet->adoEOF)
{
lRecordCount = g_ADO.VarToLong(g_ADO.m_pRecordSet->GetCollect("RecordCount"));
}
if(g_ADO.m_pRecordSet != NULL)
{
g_ADO.m_pRecordSet->Close();
g_ADO.m_pRecordSet.Detach();
}
}
if (lRecordCount == 0)
{
c_PersonnelSum.SetWindowText(_T("查询完成,没有任何记录..."));
CString strMsg;
strMsg = _T("您的查询请求成功地完成,但没有任何记录返回!");

BCGP_MSGBOXPARAMS params;
params.lpszCaption = g_strCap;
params.lpszText = strMsg;
params.dwStyle = MB_OK | MB_ICONINFORMATION;
params.bUseNativeControls = FALSE;
params.bUseNativeCaption = params.bUseNativeControls;
int result = BCGPMessageBoxIndirect(¶ms);
//AfxMessageBox(strMsg);
c_Personnel_Refresh.EnableWindow(TRUE);
c_Cancel.EnableWindow(TRUE);
return;
}
c_PersonnelSum.FlashWindow(TRUE);
c_PersonnelSum.SetWindowText(_T("请稍候,您的查询请求已经开始处理,可能时间较长(取决于您的查询记录数量),请耐心等候..."));

// 取得当前 CPU frequency
LARGE_INTEGER m_liPerfFreq = {0};
QueryPerformanceFrequency(&m_liPerfFreq);
// 取得执行前的时间
LARGE_INTEGER m_liPerfStart = {0};
QueryPerformanceCounter(&m_liPerfStart);

g_TraceDbgMsg(CAT_SYS, KQMJSYS_STATUS_WARNING, g_Login.oprid, g_Login.oprname, _T("账户管理-界面更新 UpdatePersonnelUIThread 线程启动"));
DWORD lpThreadId = 0;
pUpdatePersonnelUIThread = CreateThread(NULL, 0, &UpdatePersonnelUIThread, this, 0, &lpThreadId);
ProcessSQLToArray(lRecordCount);
c_PersonnelSum.FlashWindow(FALSE);
c_PersonnelSum.SetWindowText(_T("请稍候,将数据库正常刷卡流水信息转储至结构体完成,正在刷新列表控件并显示结果..."));
int iPersonnelArrayCount = m_PersonnelArray.GetCount();
c_PersonnelList.SetItemCountEx(iPersonnelArrayCount);
c_PersonnelList.Invalidate();

c_Personnel_Refresh.EnableWindow(TRUE);
c_Personnel_Add.EnableWindow(TRUE);
c_Personnel_Del.EnableWindow(TRUE);
c_Personnel_Modi.EnableWindow(TRUE);
c_Personnel_Print.EnableWindow(TRUE);
c_Personnel_Export.EnableWindow(TRUE);
c_OK.EnableWindow(TRUE);
c_Cancel.EnableWindow(TRUE);

// 取得执行后的时间
LARGE_INTEGER liPerfNow = {0};
QueryPerformanceCounter(&liPerfNow);
// 计算出花费的时间 (millisecond)
long decodeDulation = (((liPerfNow.QuadPart - m_liPerfStart.QuadPart) * 1000)/m_liPerfFreq.QuadPart);

CString strPersonnelArrayCount, strcounttime;
strPersonnelArrayCount.Format(_T("%d"), iPersonnelArrayCount);
strcounttime.Format(_T("%.1f 秒"), (float)decodeDulation/1000.0);
g_TraceDbgMsg(CAT_OPERATOR, KQMJSYS_STATUS_INFORMATION, g_Login.oprid, g_Login.oprname, _T("账户管理-查询账户信息完成"));
g_TraceOprLog(g_Login.oprid, 1, g_Login.nowloginipaddr, 46, _T("查询账户信息"));
c_PersonnelSum.SetWindowText(_T("查询完成,累计记录:") + strPersonnelArrayCount + _T(",查询耗时:") + strcounttime);
}
void CPersonnelDlg::ProcessSQLToArray(long lRecordCount)
{
c_PersonnelSum.SetWindowText(_T("请稍候,正在将数据库账户信息转储至结构体..."));
if(g_ADO.ExecRs(strSQL))
{
m_PersonnelArray.RemoveAll();
m_PersonnelArray.FreeExtra();
CString strAccid, strPName;
long lStep = 0;
m_iCurProcessStep = 0;
MSG message;
BeginWaitCursor();
while(!g_ADO.m_pRecordSet->adoBOF && !g_ADO.m_pRecordSet->adoEOF)
{
::memset(&m_Personnel, NULL, sizeof(m_Personnel));
m_Personnel.accid = g_ADO.VarToLong(g_ADO.m_pRecordSet->GetCollect("ACCID"));
_tcscpy_s(m_Personnel.pname, g_ADO.VarToCString(g_ADO.m_pRecordSet->GetCollect("PNAME")));
m_Personnel.sex = g_ADO.VarToShortInt(g_ADO.m_pRecordSet->GetCollect("SEX"));
_tcscpy_s(m_Personnel.ppwd, g_ADO.VarToCString(g_ADO.m_pRecordSet->GetCollect("PPWD")));
m_Personnel.cardno = g_ADO.VarToLong(g_ADO.m_pRecordSet->GetCollect("CARDNO"));
m_Personnel.cardtype = g_ADO.VarToShortInt(g_ADO.m_pRecordSet->GetCollect("CARDTYPE"));
m_Personnel.stationid = g_ADO.VarToShortInt(g_ADO.m_pRecordSet->GetCollect("STATIONID"));
_tcscpy_s(m_Personnel.stationname, g_ADO.VarToCString(g_ADO.m_pRecordSet->GetCollect("STATIONNAME")));
m_Personnel.bldept = g_ADO.VarToShortInt(g_ADO.m_pRecordSet->GetCollect("BLDEPT"));
_tcscpy_s(m_Personnel.deptname, g_ADO.VarToCString(g_ADO.m_pRecordSet->GetCollect("DEPTNAME")));
_tcscpy_s(m_Personnel.identityno, g_ADO.VarToCString(g_ADO.m_pRecordSet->GetCollect("IDENTITYNO")));
m_Personnel.accstatelegal = g_ADO.VarToShortInt(g_ADO.m_pRecordSet->GetCollect("ACCSTATELEGAL"));
m_Personnel.accstate = g_ADO.VarToShortInt(g_ADO.m_pRecordSet->GetCollect("ACCSTATE"));
m_Personnel.pertype = g_ADO.VarToShortInt(g_ADO.m_pRecordSet->GetCollect("PERTYPE"));
_tcscpy_s(m_Personnel.duty, g_ADO.VarToCString(g_ADO.m_pRecordSet->GetCollect("DUTY")));
m_Personnel.crtime = g_ADO.VarToDate(g_ADO.m_pRecordSet->GetCollect("CRTIME"));
m_Personnel.motime = g_ADO.VarToDate(g_ADO.m_pRecordSet->GetCollect("MOTIME"));
m_Personnel.leavetime = g_ADO.VarToDate(g_ADO.m_pRecordSet->GetCollect("LEAVETIME"));
_tcscpy_s(m_Personnel.note, g_ADO.VarToCString(g_ADO.m_pRecordSet->GetCollect("NOTE")));
m_Personnel.nid = g_ADO.VarToShortInt(g_ADO.m_pRecordSet->GetCollect("NID"));
m_Personnel.epnid = g_ADO.VarToShortInt(g_ADO.m_pRecordSet->GetCollect("EPNID"));
_tcscpy_s(m_Personnel.nname, g_ADO.VarToCString(g_ADO.m_pRecordSet->GetCollect("NNAME")));
m_PersonnelArray.Add(m_Personnel);
m_iCurProcessStep = (int)((++lStep*100)/lRecordCount);
// 直接更新太费时,转用线程更新
//c_Progress.SetPos(g_lCurProcessStep);
// 转发消息,立即同步更新进度条
if(::PeekMessage(&message, NULL, 0, 0, PM_REMOVE))
{
::TranslateMessage(&message);
::DispatchMessage(&message);
}
BeginWaitCursor();
g_ADO.m_pRecordSet->MoveNext();
}
EndWaitCursor();
if(g_ADO.m_pRecordSet != NULL)
{
g_ADO.m_pRecordSet->Close();
g_ADO.m_pRecordSet.Detach();
}
}
}
法尔我给他 2009-10-02
  • 打赏
  • 举报
回复
[Quote=引用 17 楼 waitlife 的回复:]
给个简单的思路你:
1、先定义一个结构体,即你要在CListCtrl中显示的内容;
2、在按下查询按钮时,首先判断数据库记录,为空时直接返回,不为空时,将数据库记录存入刚才定义的结构体;
3、c_List.SetItemCountEx(您的记录数量);
4、c_List.Invalidate();
5、响应ON_NOTIFY(LVN_GETDISPINFO, IDC_LLIST, &CPersonnelDlg::OnLvnGetdispinfolist)消息函数;
6、在CPersonnelDlg::OnLvnGetdispinfolist函数中用你的结构体来刷新CListCtrl。

我一直都是这么做的,相比一般情况,在 Release 模式下,Phonem X4 9550、4G DDR2、143900条记录约为 10 秒,Intel Pentium4 2.4C、1G DDR、143900条记录约为 18 秒。
[/Quote]

大大能把代码给我看看嘛?谢谢,我用ClistCtrl的自动显示400000记录需要2分37秒!太慢了!

我在找找和我看到的代码的区别,谢谢各位大大的回答!
waitlife 2009-10-02
  • 打赏
  • 举报
回复
给个简单的思路你:
1、先定义一个结构体,即你要在CListCtrl中显示的内容;
2、在按下查询按钮时,首先判断数据库记录,为空时直接返回,不为空时,将数据库记录存入刚才定义的结构体;
3、c_List.SetItemCountEx(您的记录数量);
4、c_List.Invalidate();
5、响应ON_NOTIFY(LVN_GETDISPINFO, IDC_LLIST, &CPersonnelDlg::OnLvnGetdispinfolist)消息函数;
6、在CPersonnelDlg::OnLvnGetdispinfolist函数中用你的结构体来刷新CListCtrl。

我一直都是这么做的,相比一般情况,在 Release 模式下,Phonem X4 9550、4G DDR2、143900条记录约为 10 秒,Intel Pentium4 2.4C、1G DDR、143900条记录约为 18 秒。
MoXiaoRab 2009-10-02
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 wwwcs59 的回复:]
在CListCtrl刚开始需要显示数据的时候也是触发 LVN_GETDISPINFO消息来获得需要显示的数据的,而我这个根本就不显示任何数据,
我试出来的一种方法就是先填充好多空行,然后在GetDispInfo()中刷新需要显示的数据,这不是太奇怪了吗!
我看别的例子都是自动就显示出来的,怎么到了我这就不对了呢?

是不是我哪里弄错了,或者是设置错了?

非常感谢
[/Quote]
填充空行是看你的代码怎么搞的了。应该是自动显示的。
你对比下你的代码和别人的代码有什么区别,改改。
加载更多回复(14)

16,548

社区成员

发帖
与我相关
我的任务
社区描述
VC/MFC相关问题讨论
社区管理员
  • 基础类社区
  • AIGC Browser
  • encoderlee
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

        VC/MFC社区版块或许是CSDN最“古老”的版块了,记忆之中,与CSDN的年龄几乎差不多。随着时间的推移,MFC技术渐渐的偏离了开发主流,若干年之后的今天,当我们面对着微软的这个经典之笔,内心充满着敬意,那些曾经的记忆,可以说代表着二十年前曾经的辉煌……
        向经典致敬,或许是老一代程序员内心里面难以释怀的感受。互联网大行其道的今天,我们期待着MFC技术能够恢复其曾经的辉煌,或许这个期待会永远成为一种“梦想”,或许一切皆有可能……
        我们希望这个版块可以很好的适配Web时代,期待更好的互联网技术能够使得MFC技术框架得以重现活力,……

试试用AI创作助手写篇文章吧