My MFC Study Notes

来源:互联网 发布:爱思助手 mac 编辑:程序博客网 时间:2024/05/22 06:22

* Windows程序分为UI资源和程序代买两大部分,两部分最终以RC编译器整合为一个exe文件。

* Windows三大模块:GDI32.DLL, USER32.DLL和KERNEL32.DLL其对应的分别的Lib文件。

* Windows程序的进行依靠外部发生的事件来驱动,当事件发生后以消息的形式进入程序之中。消息是一种数据结构。这就是以消息为基础,以事件来驱动的工作方式。

* __stdcall, __pascal, __cdecl是函数调用规则,关系到常熟压到堆栈的顺序以及处理堆栈的责任归属。

* CreateThread产生一个worker thread,但它接触到消息时有了消息循环以抓取消息,就变为UI Thread

* MFC产生线程使用CWinThread对象来创建或者用afxBeginThrad,没有用CreateThread或_beginthreadex,因为他们在framework中做了必要的初始化动作。

* SDK窗口创建过程:

   1.注册窗口类。2.CreateWindow。3.UpdateWindow.4.ShowWindow。5.回调函数处理消息。

* 一个CstdioFile对象代表以CRuntime函数fopen打开的一个stream文件,stream文件有缓冲区,有文字模式和二进制模式。

* GDI:Graphics Device Interface。图形设备接口。主要负责系统和绘图程序之间的信息交换,处理所有Windows程序的图形输出。

* 进程正常结束时,系统通知该进程调用的所有Dll,但是用TerminateProcess结束一个进程,系统不会做这件事。

* MessageMap是一套宏,宏展开后是一套数据结构,再其中寻找处理程序的函数指针。没用虚函数的方法是因为子类中有父类副函数表的副本,这样占用空间很大,效率不高。

* HOOK更早一步抓取消息,并且可以抓取不属于你的消息。

* 如果想在进入OnDraw之前调整DC,可以改写虚函数OnPrePareDC

* DC就是Device Context,在Windows绘图之前一定要先获得一个DC,它可能代表一个窗口,一块内存,或者打印机等。

* 自定义Windows 消息:

  1.在头文件中什么DECLARE_MESSGE_MAP()

  2.在源文件中声明BEGIN_MESSAGE_MAP和END_MESSAGE_MAP,BEGIN_MESSAGE_MAP有两个参数,一个是消息映射表的类,一个是其父类。

* Exe执行过程:

  1.Shell调用CreateProcess激活App.exe

  2.系统产生一个进程核心对象计数值为1

  3.系统为此进程建立一个4GB的地址空间。

  4.加载器将必要的资源加载到上述空间中,如程序资料,Dll等

  5.系统为此进程建立一个主线程,这才是CPU的时间分配对象。

  6.系统调用Cruntime函数的Straup Code

  7.Stratup Code调用APp的WinMain函数。

  8.APP开始运行。

* 线程的执行过程

  1.配置“执行线程对象",其handle为CreateThread的返回值。

  2.设定计数值为1

  3.配置执行线程的Context

  4.保留执行线程的堆栈

  5.将Context中的堆栈指针缓存器css和指令指针缓存器IP设定妥当。

* 线程等级  0——31 低——高

等级代码优先权值idleIDLE_PRIORITY_CALSS4normalNORMAL_PRIORITY_CASS9前景7背景highHIGH_PRIORITY_CASS13realtimeREALTIME_PRIORITY_CASS24* property或者field就是成员变量,成员函数有一个隐藏的this指针

* 如果基类的指针指向子类的地址,那么该指针只能调用基类的函数。如果基类和子类有共同的函数,非virtual函数,那么调用成员函数时,视原始指针而定而不是指针实际所致的对象而定。

* 将子类对象强转为父类对象会造成对象的切割,但是子类对象指针强转为父类指针就不会有这种情况。

* Application object是一个全局对象,代表整个程序,其类型是CWinApp,MainFrameObject代表主窗口,基类是CFrameWnd。CWinApp代表程序本体。\

* 应用程序一定要修改虚函数InitInstance,因为它是CWinApp中的一个空函数,没有任何默认操作。

* 改变类的名称应该在CaminFrame::PreCreateWindow中进行

* SDK程序通过GetMessahe,DispatchMessage消息循环处理,MFC通过消息映射进行处理。在头文件汇总添加DELCARE_MESSAGE_MAP,在源文件中添加:BEGIN_MESSAGE_MAP和END_MESSAGE_MAP

* MFC消息分为三大类

  1.标准消息:以WM_xxx开头,除WM_COMMAND意外,以WM开头的消息。

   ON_WM_CHAR               Onchar函数处理

   ON_WM_CLOSE            OnClose函数处理

   2.命令消息:WM_COMMAND,可能来自菜单,快捷键或工具栏按钮。

   ON_COMAND(<id>,<memberFun>)

   3.Notification消息,由控件产生的消息

   ON_BN_CLICKED(<id>,<memberFun>)

* MFC程序的产生:

   1.Application Object产生,内存于是获得配置初值设定。

   2.AfxWinMain执行AfxWinInit后者又调用AfxInitThread把消息队列尽量加大到96

   3.AfxWinMain执行InitApplication,通常不需要修改。

   4.AfxWinMain执行Initinstance,这是CWinApp虚函数,必须修改它

   5.InitInstance new了一个CFrameWnd对象

   6.CFraneWnd构造函数执行Create产生窗口

   7.回到InitInstance执行showWindow

   8.执行UpdateWindow,发生WN_PAINT消息

   9.回到AfxWinmain执行Run进入消息循环。

  10.程序获得WM_PAINT消息,Run中GetMessage循环。、

  11.WM_PAINT由DispatchMessage发送到窗口函数DefWindowProc中。

  12.DefWindowProc传递到消息映射表中。

* MFC程序结束

  1.用户单击关闭产生WM_CLOSE消息

  2.CFrameWnd]交由默认处理程序

  3.调用DestroyWindow并发出WN_DESTORY

  4.WN_DESTORY调用PostQuitMessage并发出WM_QUIT

  5.WinApp::Run()收到WM_QUIT后结束消息循环调用ExitInstance

  6.回到AfxWinMain()执行AfxWinTerm结束程序。

* 由程序员设计却又Windows调用的函数称为CallBack函数。Windows不借助任何对象调用这个函数,也就不需要This指针,所以CallBack要么是全局的要么是static的

* CRunTimeClass是类型识别网链表中的元素,任何一个类只要声明DECLARE_DYANMIC或DECLARE_DYCREATE或DECLARE_SERIAL宏就有个静态的CRuntimeClass内嵌对象。

* DocumentFrame是View窗口的容器,程序每打开一个文件(数据)应有三个对象:一份Document对象,一份View对象,一份CMDIChildWnd对象,这三份对象有Document Tempalte管理。

* Seralize条件

  1.继承自Cobject,保证有RTTI,DYNAMIC Create等机能。

  2.类的声明必须有DELCARE_SERIAL宏,此宏有一个参数:类名称

  3.类的实现要有IMPLEMNT_SERIAL宏,此宏有三个参数:一个是类名称,一个是基类名称,一个是schme no

  4.改写Seialize虚函数,使他能适当地把成员变量写入文件中。

  5.为此类加一个default构造函数(无参)。因为一个对象来自文件,MFC要先创建它,而且是无任何参数下创建,然后才能读数据。

* Invalid(TRUE)产生WM_PATIN消息

* CDocTempalte,CDouemnt, CView, CFrameWnd的关系:

  1.CWinApp有一个指针对象,CDocmanage× m_pDocManager

  2.CDocManager有一个指针链表,CPtrList m_templateList用来维护一系列的Document Template应用程序在Initinstance中以AddDocTempalte将这些Document Template加入到CDocManager所维护的链表中

  3.CDocTempalte有三个成员变量,分别有Document, View, Frame的CRuntimeClass指针,另有一个m_nIdResource用来表示显示的UI对象。

  4.CDocument有一个成员变量CDoceTempalte× m_pDocTempalte会指其DocumentTempalte,另有一个成员变量CptrList m_viewList表示可以同时维护一系列的iews

  5.CFrameWnd有一个成员变量CView× m_pViewActive指向当前活动的View

  6CView有一个成员变量CDocument* m_pDocument指向相关的Docuemnt。

* Frmaework先调用ON_PAINT,ON_PAINT再调用OnDraw


* 进程/线程同步

临界区(Critical Section)
保证在某一时刻只有一个线程能访问数据的简便办法。在任意时刻只允许一个线程对共享资源进行访问。如果有多个线程试图同时访问临界区,那么在有一个线程进 入后其他所有试图访问此临界区的线程将被挂起,并一直持续到进入临界区的线程离开。临界区在被释放后,其他线程可以继续抢占,并以此达到用原子方式操作共 享资源的目的。临界区包含两个操作原语: EnterCriticalSection() 进入临界区 LeaveCriticalSection() 离开临界区
EnterCriticalSection()语句执行后代码将进入临界区以后无论发生什么,必须确保与之匹配的 LeaveCriticalSection()都能够被执行到。否则临界区保护的共享资源将永远不会被释放。在使用临界区时,一般不允许其运行时间过长, 只要进入临界区的线程还没有离开,其他所有试图进入此临界区的线程都会被挂起而进入到等待状态,并会在一定程度上影响。程序的运行性能。尤其需要注意的是 不要将等待用户输入或是其他一些外界干预的操作包含到临界区。如果进入了临界区却一直没有释放,同样也会引起其他线程的长时间等待。换句话说,在执行了 EnterCriticalSection()语句进入临界区后无论发生什么,必须确保与之匹配的LeaveCriticalSection()都能够被 执行到。可以通过添加结构化异常处理代码来确保LeaveCriticalSection()语句的执行。虽然临界区同步速度很快,但却只能用来同步本进 程内的线程,而不可用来同步多个进程中的线程。MFC为临界区提供有一个CCriticalSection类,使用该类进行线程同步处理是非常 简单的。只需在线程函数中用CCriticalSection类成员函数Lock()和UnLock()标定出被保护代码片段即可。Lock()后代码用 到的资源自动被视为临界区内的资源被保护。UnLock后别的线程才能访问这些资源。

互斥(Mutex)是一种用途非常广泛的内核对象。能够保证多个线程对同一共享资源的互斥访问。同临界区有些类似,只有拥有互斥对象的线程才具有访问资源 的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交 出,以便其他线程在获得后得以访问资源。与其他几种内核对象不同,互斥对象在操作系统中拥有特殊代码,并由操作系统来管理,操作系统甚至还允许其进行一些 其他内核对象所不能进行的非常规操作。 互斥量跟临界区很相似,只有拥有互斥对象的线程才具有访问资源的权限,由于互斥对象只有一个,因此就决定了任何情况下此共享资源都不会同时被多个线程所访 问。当前占据资源的线程在任务处理完后应将拥有的互斥对象交出,以便其他线程在获得后得以访问资源。互斥量比临界区复杂。因为使用互斥不仅仅能够在同一应 用程序不同线程中实现资源的安全共享,而且可以在不同应用程序的线程之间实现对资源的安全共享。以互斥内核对象来保持线程同步可能用到的函数主要有CreateMutex()、OpenMutex()、ReleaseMutex()、 WaitForSingleObject()和WaitForMultipleObjects()等。在使用互斥对象前,首先要通过 CreateMutex()或OpenMutex()创建或打开一个互斥对象。

信号量(Semaphores)信号量对象对线程的同步方式与前面几种方法不同,信号允许多个线程同时使用共享资源,这与操作系统中的PV操作相同。它指出了同时访问共享资源的线程最大 数目。它允许多个线程在同一时刻访问同一资源,但是需要限制在同一时刻访问此资源的最大线程数目。在用CreateSemaphore()创建信号量时即 要同时指出允许的最大资源计数和当前可用资源计数。一般是将当前可用资源计数设置为最大资源计数,每增加一个线程对共享资源的访问,当前可用资源计数就会 减1,只要当前可用资源计数是大于0的,就可以发出信号量信号。但是当前可用计数减小到0时则说明当前占用资源的线程数已经达到了所允许的最大数目,不能 在允许其他线程的进入,此时的信号量信号将无法发出。线程在处理完共享资源后,应在离开的同时通过ReleaseSemaphore()函数将当前可用资 源计数加1。在任何时候当前可用资源计数决不可能大于最大资源计数。使用WaitForSingleObject检测当前是否还有可用的信号量,如果有,Semaphores>0,则执行下一条指令,计数减1.如果没有则等待。直到有Thread执行ReleaseSemaphore(hSema, 1, count)

事件(Event)事件对象也可以通过通知操作的方式来保持线程的同步。并且可以实现不同进程中的线程同步操作。信号量包含的几个操作原语:CreateEvent() 创建一个信号OpenEvent() 打开一个事件SetEvent() 回置事件WaitForSingleObject() 等待一个事件WaitForMultipleObjects() 等待多个事件使用临界区只能同步同一进程中的线程,而使用事件内核对象则可以对进程外的线程进行同步,其前提是得到对此事件对象的访问权。可以通过OpenEvent()函数获取得到

Mutex是互斥的,一个线程拥有这个Mutex其他线程只能等待,知道该线程使用ReleaseMutex释放。Event不是互斥的,两个线程可以同时使用SetEvent置为有信号(每次触发后,必有一个或多个处于等待状态下的线程变成可调度状态。),从而两个线程可以同时执行。Semaphores是并发执行的个数,一般搭配着其他同步方法是用。

1. 互斥量与临界区的作用非常相似,但互斥量是可以命名的,也就是说它可以跨越进程使用。所以创建互斥量需要的资源更多,所以如果只为了在进程内部是用的话使 用临界区会带来速度上的优势并能够减少资源占用量。因为互斥量是跨进程的互斥量一旦被创建,就可以通过名字打开它。
2. 互斥量(Mutex),信号灯(Semaphore),事件(Event)都可以被跨越进程使用来进行同步数据操作,而其他的对象与数据同步操作无关,但对于进程和线程来讲,如果进程和线程在运行状态则为无信号状态,在退出后为有信号状态。所以可以使用WaitForSingleObject来等待进程和 线程退出。
3. 通过互斥量可以指定资源被独占的方式使用,但如果有下面一种情况通过互斥量就无法处理,比如现在一位用户购买了一份三个并发访问许可的数据库系统,可以根 据用户购买的访问许可数量来决定有多少个线程/进程能同时进行数据库操作,这时候如果利用互斥量就没有办法完成这个要求,信号灯对象可以说是一种资源计数 器。

* WaitForSingleObject

WaitForSingleObject(g_event,INFINITE);     For(;;)        {         ………….        }

在这个线程函数中只有设置g_event为有信号状态时才执行下面的for循环 ,因为g_event是全局变量,所以我们可以在别的线程中通过g_event. SetEvent控制这个线程。因为g_event是全局对象,所以在一个线程中调用了SetEvent,其他线程的WaitForSingleObject也会执行,除非在这个线程退出之前执行了ResetEvent,其他线程才会继续等待。一个Event被创建以后,可以用OpenEvent()API来获得它的Handle,用CloseHandle() 来关闭它,用SetEvent()或PulseEvent()来设置它使其有信号,用ResetEvent() 来使其无信号,用WaitForSingleObject()或WaitForMultipleObjects()来等待其变为有信号.

PulseEvent()是一个比较有意思的使用方法,正如这个API的名字,它使一个Event 对象的状态发生一次脉冲变化,从无信号变成有信号再变成无信号,而整个操作是原子的.
对自动复位的Event对象,它仅释放第一个等到该事件的thread(如果有),而对于人工复位的Event对象,它释放所有等待的thread.

这里有两个API函数用来修改事件对象的信号状态:SetEvent和ResetEvent。前者把事件对象设为”有信号”状态,而后者正好相反。 

* catch(...)捕获所有throw抛出的异常

* __uuidof的作用

用来获取某种结构、接口及其指针、引用、变量 所关联的GUID,类似于某些语言中获取类型 typeof 这样的操作。

假定c++中,有结构体s

struct s {int i;};
可以通过下面的__declspec 给这个结构 关联一个GUID
struct __declspec( uuid("93A1665E-C9FA-4147-AC3A-3CC855281AF8") ) s;
以后程序中使用该结构
s a, *b, &c;
通过__uuidof(s); __uuidof(a); __uuidof(b); __uuid(c); 都能得到结构s关联的GUID
("93A1665E-C9FA-4147-AC3A-3CC855281AF8")

*使Dialog 中的控件最大化,占满Dialog 的空间

在Onsize的消息函数中添加如下代码:

void CThreeView::OnSize(UINT nType, int cx, int cy) {CFormView::OnSize(nType, cx, cy);// TODO: Add your message handler code hereif (GetSafeHwnd()){if (m_treeCtrl.GetSafeHwnd()){CRect rect(0, 0, cx, cy);m_treeCtrl.MoveWindow(&rect);}}}

* 窗口不响应回车,ESC和Alt+F4的关闭消息:

1添加PreTranslateMessage的虚函数

2添加如下代码:

if (pMsg->wParam == VK_ESCAPE || pMsg->wParam == VK_RETURN) //屏蔽回车和ESC return TRUE;if (pMsg->message == WM_SYSKEYDOWN && pMsg->wParam == VK_F4) //屏蔽ALT+F4return TRUE;

* 使窗口不在任务栏显示

在OnInitDialog中添加如下代码:

ModifyStyleEx(WS_EX_APPWINDOW,WS_EX_TOOLWINDOW,1);//任务栏隐藏
*VC6中加载GIF图片

1. 新建项目:在VC6中用MFC新建一个基于对话框的GifDemo应用程式,接受任何缺省选项即可;

2.在项目中插入文档:把PictureEx.h,PictureEx.cpp文档copy 到项目文档夹下,Project->Add to Project->Files中选上PictureEx.h,PictureEx.cpp, Insert;

3.加入图片控件:从对话框控件中把Picture Control(图片控件)拖入主对话框中,修改其属性:ID:IDC_GIF,TYPE:Rectangle,其余接受缺省选项。再在ClassWiard中为IDF_GIF加入CSatic控制变量m_GifPic, 注意看一下,GifDemoDlg.h中是否加上了#include "PictureEx.h"(由ClassWiard加入)。然后将CSatic m_GifPic;更改成CPictureEx m_GifPic;

4.加载动画文档:先将要加载的动画文档放到 res 资源文档夹下,再将其Import进项目中,由于MFC只支持256BMP文档的图片,因此,我们要新建一个图片类型:"GIF",我在这里将我网站的宣传图片roaring.gif放进去 ,并将其ID修改成:IDR_GIFROARING。

import(导入)gif动画的周详过程:
在resourceview窗口中,单击鼠标右键,在出现的环境菜单中选择“import...”命令,会出现“import resource”选择文档对话框,文档类型选择“任何文档(*.*)”,open as 选项为"auto",再选择动画文档所在目录,选上要载入的动画文档 roaring.gif,再单击 import,由于gif动画类型不是vc默认的文档类型,这时会出现"custom resource type"对话框,键入“"gif"”,再单击ok,然后再修改其id。

5.在程式的适当位置添入加载代码: 这里,我们在CGifDemoDlg::OnInitDialog()函数中加入如下代码:

// TODO: Add extra initialization hereif (m_GifPic.Load(MAKEINTRESOURCE(IDR_GIFROARING),_T("Gif")))m_GifPic.Draw();  
但是由于我们的软件要写入硬件设备中,对文件大小有严格要求,几个软件间共用同一个GIF,必须采用动态调用GIF文件加载进MFC中。
查看PictureEx类,会发现类中有多个load函数,其中一个可以Load文件名,但是调用过程不是简单的Load,在Draw的样子显示GIF动画。试验多次后来发现需要在Load文件之前 Static::Create一下。 源码如下:
 m_GifPic.Create(NULL,WS_CHILD | WS_VISIBLE |SS_ENHMETAFILE,CRect(50,50,100,100),this,1234); m_GifPic.Load(_T("c://1.gif"));  m_GifPic.Draw();//

* 使用CSplitterWnd分割窗口后,分割窗口得到控件的方法

分割的某个view是个窗口,在这个窗口上关联了一个TreeCtrl的控件变量m_treeCtrl,如果在这个窗口使用这个控件最好在OnSize中使用,在Oncreate中使用这个控件会是NULL

* 解决使用CSplitterWnd分割窗口后,窗口中的控件随着滚动条拖动,会出现空白的情况

1.在OnInitialUpdate中添加如下代码:

void CThreeView::OnInitialUpdate() {CFormView::OnInitialUpdate();// TODO: Add your specialized code here and/or call the base classInitializeFlatSB(this->m_hWnd);  FlatSB_ShowScrollBar(this->m_hWnd, SB_BOTH, FALSE); //m_nMapMode = -1;  不能用这种方法禁止滚动条,否则得到窗口的Szie会有问题。}

2.在控件的Styles中选中Scroll的属性。

* 单文档中重载OnFilenOpen指定打开文件类型,继续调用Doc类中的OnOpenDocument

在MainFrame或App类中添加OnFileOpen的Message响应函数,不要在Doc类或View中添加

这样在OnOpenDocument可以调用ShowWindow函数,可以发送消息

void CBMPEditToolApp::OnFileOpen() {// TODO: Add your command handler code hereCString strFilter = "BMP File (*.bmp)|*.bmp||";CFileDialog dlg(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_OVERWRITEPROMPT, strFilter, NULL);if (dlg.DoModal() == IDOK){POSITION pos = GetFirstDocTemplatePosition();if(pos){CDocTemplate* pTemplate = GetNextDocTemplate(pos);ASSERT(pTemplate != NULL);ASSERT_KINDOF(CDocTemplate, pTemplate);pTemplate->OpenDocumentFile(dlg.GetPathName());}}}
*窗口重绘和檫除背景

擦除背景不是调用OnPaint。擦除背景和重绘窗口不是一回事。
重绘窗口过程中是否要擦除背景,决定于Invalidate()的参数值。
如果是TRUE,重绘窗口过程分两步1。调OnEraseBkgnd,2。调OnPaint。
否则就只有调OnPaint一步。

* 修改窗口的类名。

在CMainFrame::PreCreateWindow的函数中添加如下代码:

WNDCLASS wndcls;memset(&wndcls, 0, sizeof(WNDCLASS));wndcls.lpfnWndProc = ::DefWindowProc;wndcls.hInstance = AfxGetInstanceHandle();wndcls.hCursor = AfxGetApp()->LoadCursorW(IDC_ARROW);wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;wndcls.lpszClassName = MAIN_WND_CLASS;BOOL bRes = AfxRegisterClass(&wndcls);if (bRes){cs.lpszClass = _T("your class name");}return CFrameWnd::PreCreateWindow(cs);
主要要有AfxRegisterClass(&wndcls);和cs.lpszClass = MAIN_WND_CLASS;,否则会注册失败,单独写cs.lpszClass = MAIN_WND_CLASS;会运行错误,采用这样的方法不必修改RC文件。

* MFC单文档程序不现实隐藏窗口,程序后台运行。

在App的InitInstance()函数中,注销下面两行代码:

// m_pMainWnd->ShowWindow(SW_SHOW);// m_pMainWnd->UpdateWindow();
添加:
m_pMainWnd->ShowWindow(SW_HIDE);
*MFC自定义消息映射的方法
1.自定义个一个消息,如:

#define WM_SCANBUTTON_SHOWWM_USER + 4000
2.头文件中对声明一个该消息的相应函数:

CMainFrame();afx_msg LRESULT OnOpenScanButtonWnd(WPARAM wp, LPARAM lp);DECLARE_DYNCREATE(CMainFrame)
3.cpp文件中添加映射:

BEGIN_MESSAGE_MAP(CMainFrame, CFrameWnd)ON_WM_CREATE()ON_MESSAGE(WM_SCANBUTTON_SHOW, OnOpenScanButtonWnd)END_MESSAGE_MAP()

实现该函数:

LRESULT CMainFrame::OnOpenScanButtonWnd(WPARAM wp, LPARAM lp){AfxMessageBox(_T("aaaaaaaaaaaaaaaa"));return 1L;}
在其他地方发送这个消息,就可以了,函数就会响应

m_pMainWnd->PostMessage(WM_SCANBUTTON_SHOW);

*CMap用CString做key时可能会报错,应该使用CMap<CString, LPCTSTR, CString, CString>

* 使用CFileDialog选择文件是,如果多选且选择文件比较多,GetNextPathName可能不能反悔所有的文件路径,解决方法如下:

CFileDialog dlgFile(TRUE, NULL, NULL, OFN_HIDEREADONLY | OFN_FILEMUSTEXIST | OFN_ALLOWMULTISELECT, _T("Txt Files (*.txt)|*.txt||"), NULL);DWORD MAXFILE = 2000 * 256;dlgFile.m_ofn.nMaxFile = MAXFILE;TCHAR* pc = new TCHAR[MAXFILE];dlgFile.m_ofn.lpstrFile = pc;dlgFile.m_ofn.lpstrFile[0] = NULL;if (dlgFile.DoModal() == IDOK){if ((*m_pFileList).GetCount() > 0) (*m_pFileList).RemoveAll();POSITION pos = dlgFile.GetStartPosition();while (pos){CString strFileName = dlgFile.GetNextPathName(pos);ASSERT(NULL != m_pFileList);(*m_pFileList).AddTail(strFileName);}ASSERT(NULL != m_pConvertUtility);m_pConvertUtility->ConvertTxtToExcel(m_pFileList);}delete []pc;

* 写Unicode字串到txt

CFileException fileException;if(!mFile.Open(_T("1.txt"),CFile::modeCreate|CFile::modeNoTruncate|CFile::modeWrite|CFile::typeBinary)){AfxMessageBox(_T("文件路径找不到,打开文件失败"));return bRes;}TCHAR d = '\t';CString cs = _T("角度");cs+=d;cs+=_T("权值\r\n");mFile.Write(&UNICODE_TXT_FLG,2);  mFile.Write(cs.GetBuffer(10),cs.GetLength()*2);cs = _T("IDbb");cs+=d;cs+=_T("aa\r\n");mFile.Write(cs.GetBuffer(10),cs.GetLength()*2);mFile.Close();

* MFC单文档的初始化顺序:

1.App类中的全局对象,主要是theApp的构造函数。

2.InitInstance()函数。

* 修改exe图标和程序左上角图标

1.在res文件夹下直接替换原来的ico文件,再rebuild,exe文件图标替换。应为windows缩略图的原因,可能需要重启exploer。

2.在PreCreateWindow函数中,添加wndcls.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);如:

WNDCLASS wndcls;  memset(&wndcls, 0, sizeof(WNDCLASS));  wndcls.lpfnWndProc = ::DefWindowProc;  wndcls.hInstance = AfxGetInstanceHandle();  wndcls.hCursor = AfxGetApp()->LoadCursorW(IDC_ARROW);  wndcls.style = CS_DBLCLKS | CS_HREDRAW | CS_VREDRAW;  wndcls.lpszClassName = APP_CLASS_NAME;  wndcls.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);BOOL bRes = AfxRegisterClass(&wndcls);  if (bRes)  {  cs.lpszClass = APP_CLASS_NAME;  }  
*初始化时居中显示窗口

在app的InitInstance()函数中:

int sW = GetSystemMetrics(SM_CXSCREEN); //Get screent weightint sH = GetSystemMetrics(SM_CYSCREEN); //Get screent heightint w = (int)(sW / 2);   int h = (int)(sH / 2);MoveWindow(AfxGetMainWnd()->m_hWnd, (int)((sW - w) / 2),  (int)((sH - h) / 2), w, h, TRUE);

* 多文档程序启动时,不打开文档

在App类的InitInstance函数中,在ParseCommandLine(cmdInfo);之后添加

cmdInfo.m_nShellCommand = CCommandLineInfo::FileNothing;


0 0