ice197983

VC常见入门问题总结

1:fatal error C1010: unexpected end of file while looking for precompiled header directive该如何解决
    如果发生错误的文件是由其他的C代码文件添加进入当前工程而引起的,则Alt+F7进入当前工程的Settings,选择C/C++选项卡,从Category组合框中选中Precompiled Headers,选择Not Using Precompiled headers。确定
    如果发生错误的文件原本是该工程中的,则检查该文件头部有没有#include "stdafx.h"语句,没有的话添加。
    如果还不行,也有可能是定义的类或结构体等最后忘了加分号,注意一下。
2:fatal error RC1015: cannot open include file 'afxres.h'.该如何解决
    #include "afxres.h"语句是在.rc文件中的,而afxres.h文件在VC的安装目录中的.\VC98\MFC\INCLUDE目录中,所以着重查一下Tools菜单中Options对话框中的Directories中的包括文件的路径是否正确,是否在VC的安装路径中,不是的话,改过来,如果这方面没问题,则到其他机器中拷贝afxres.h到相应的目录中
3:Dll分配的内存块,应用程序释放,结果报异常。
    用GlobalAlloc()代替new, 用GlobalFree() 代替delete就不会出错了
    其实还有一个办法,就是把dll的Settings的C/C++选项卡的Code Generation的Use Run-time liberary改成Debug Multithreaded DLL,在Release版本中改成Multithreaded DLL,就可以直接使用new和delete了,没问题
    比较规范点的做法一般是DLL分配的内存由DLL释放。在DLL中加一个函数释放内存不是更好吗。
4:发现打印预览的图形明显比屏幕显示图形小,怎么办?
    这多半是CDC映射模式的选择引起的,缺省状态下,选择的是MM_TEXT模式,MM_TEXT以设备的像素点为单位,而不同设备的像素点的大小不同,打印机的分辨率比显示器要高很多,所以导致同样图形在打印时候变小。解决之道是统一使用其他定长的映射模式,比如MM_HIMETRIC等等(CDC::SetMapMode()改变映射模式)
5:CString、char*、string、int、_bstr_t、CTime、COleDateTime等等的相互转换,如何判断一个字符串是一个浮点数?

#include<string>
using namespace std;
#include <COMDEF.H>

{
CString strCString="ABC";
char strchar[256],*pstr;

pstr=(LPSTR)(LPCTSTR)strCString; //CString---->char*
strcpy(strchar,(LPSTR)(LPCTSTR)strCString); //CString---->char[]

_bstr_t strbstr=pstr; //char*---->_bstr_t
WCHAR *strWCHAR=strbstr; //b_str_t--->UNICODE

strbstr=strWCHAR;
pstr=strbstr; //UNICODE---->char*

strCString="10";
int istr=atoi((LPSTR)(LPCTSTR)strCString); //CString、char[]、char*------>int
strCString.Format("%d",istr); //int----->CString
sprintf(strchar,"%d",istr); //int----->char[]

pstr=new char[256]; //字符串申请空间
strcpy(pstr,"ABC"); //字符串赋值
delete []pstr; //字符串释放

string strstring="ABC";
pstr=(char*)strstring.c_str(); //string---->char*

strCString="2003-10-27 6:24:37"; //CString--->COleDateTime
COleVariant vtime(strCString);
vtime.ChangeType(VT_DATE);
COleDateTime time4=vtime;



COleDateTime time1(1977,4,16,2,2,2); //COleDataTime--->CTime
SYSTEMTIME systime;
VariantTimeToSystemTime(time1, &systime);
CTime tm(systime);

time_t time2=tm.GetTime(); //CTime--->time_t
COleDateTime time3(time2); //time_t--->COleDateTime

//判断字符串是否是某种类型
CString sValue("123.1");
COleVariant vValue(sValue);

BOOL bStrIsFloat = (SUCCEEDED(VariantChangeType(&vValue, &vValue, 0, VT_R8)) && sValue.Find('.') != -1);
if(bStrIsFloat)
{
AfxMessageBox("浮点");
}
}

6:如何建立一个UNICODE应用程序?

建立一个应用程序,打开Alt+F7 settings选项,选择C/C++选项卡,在Preprocessor definenation中加上_UNICODE,在Link选项卡中,在Category选择框中选择Output,在Entry-point symbol编辑框中,添加wWinMainCRTStartup确定。

注意调试UNICODE程序时,需要在安装时VC选择所有选项,否则会缺少动态库和相应的.lib文件


7:ADO操作数据库表,更新出现问题
在打开数据库前,添加如下语句试一下pRecordSet->CursorLocation = adUseClient;

IntefOptics.lib(IntefOptics.dll) : error LNK2005: "public: bool __thiscall CIntvnOptics::NewtonRingsCreate(int,double,int,int,int,int)" (?NewtonRingsCreate@CIntvnOptics@@QAE_NHNHHHH@Z) already defined in IntvnOptics.obj

这类错误是什么导致? 不要说rebuild all, 因为我已经做了不下百次了
LIB里面有和IntvnOptics.cpp里面一样的函数实现。估计你在编译库的时候将NewtonRingsCreate的实现写在头文件里面,然后你使用库的那个CPP文件include了那个头文件。
ll.h
//
void func()
{
//...
}
ll.cpp
#include "ll.h"
以上两个文件编译成lib,使用库的时候
ul.cpp
#include "ll.h"
这时候ul里面也连接了func,lib里面也有func。
解决方法:
1、实现放到CPP里面
或者
2、使用inline修饰
inline void func()
{
//...
}
大家分享一下如何用MFC做实际项目的经验
希望大家一起分享一下如何用MFC做实际项目的经验,中秋凑个热闹。呵呵。
1、界面风格和控件
2、消息循环、多线程、数据库相关
3、数据结构和算法相关
4、其他

初学MFC的时候,大家最先接触的就是界面吧。
虽然说界面不是特别重要,但确实让人头痛。
弄来弄去,大多是控件搞不定。(在这个版这样的问题好多:))
至于消息循环机制,我觉得我们用到不多。
MFC都帮我们做好了。不过有一些注意事项。
上一次我做多线程的时候死锁了,阿行大哥就是用消息机制帮我解决的。
另外,rtdb大哥还赠了我1000分,使我能够继续提问。(太感谢了)
这个版好心人不少。感谢之余,我在这里也和大家分享一下。

1、
刚开始的时候,为了把界面做得好看,
我在www.codeproject.com和www.codeguru.com上面找到了一些好用的控件。
如ButtonST、TruecolorToolbar、GradientProgressCtrl、CoolToolbar等
控件。可能大家也用过。它们都很好用,而且有演示程序下载,边学边用,
用对比法和增减法,这两个方法学MFC挺管用的。
后来在www.vkbase.com看到了一个换肤的ActiveX控件,很好用。
做出来的界面特好看。大家去下载看看。
可惜对组合框等控件支持不够。(可能是小弟不会:))

后来做数据库的时候,我公司的高手说选择越简单的控件越好。
我刚开始用DataGrid,直接绑定数据库。
他们建议用原始的Grid控件,独立于具体的数据库。
Grid控件没有(也不需要!)编辑和组合框支持。编辑的时候跳出对话框来解决。

现在我做的一个项目的界面是单文档多个formview切换。(这个界面挺古老的吧)
曾经用过属性页控件,现在用了Tab控件和对话框嵌入,感觉这个挺好的。

遇到控件问题的时候,我常看msdn,上面的实例真好。
现在有问题来msdn,我都是先搜索一下,其实大多数问题都已经有帖子解答。
这样可以节省自己和别人的时间。
记住每个控件都是一个窗口是有帮助的。

2、
多线程的时候尤其要注意一个问题:MFC帮我们开的线程是界面线程。
如果我们以后再开的线程是工作者线程,那么界面主线程不能阻塞。
如果界面主线程阻塞了,相当于消息泵阻塞了。
我上次犯的错误就是让其他工作者线程发一个消息来让主线程解除阻塞。结果死锁。
解决方法是将其他线程改为界面线程。
还有一个问题,创建新线程的时候要小心,它有一个CRuntimeClass*的参数。
它要CreateObject()创建一个新的线程,然后把线程指针返回。
CSerialPortEx* g_pPort = new CSerialPortEx;
CRuntimeClass* IOClass  = g_pPort->GetRuntimeClass();
g_pPort = (CSerialPortEx*)AfxBeginThread(IOClass);
上面的代码将使g_pPort无法访问CSerialPortEx内部的变量。
通过增加一个临时变量来解决。如下:
CSerialPortEx* TempPort = new CSerialPortEx;
CRuntimeClass* IOClass  = TempPort->GetRuntimeClass();
g_pPort = (CSerialPortEx*)AfxBeginThread(IOClass);
delete TempPort;
类似的场合不少。如在用对话框嵌入标签的时候再一次创建了对话框。
出现了实际上存在两个对话框,无法访问原来对话框上的控件。
现在我一看到CRuntimeClass*就会特别小心。
我在解决这些问题的时候用的方法是调试和增减法。并不是从书上得来。
如果有差错,请各位指出来。呵呵。
还有一个问题是进度条对话框上的控件不能及时显示,(消息循环暂时受阻塞)
阿行大哥用在用户代码中加入消息循环解决。也可以开一个线程。

数据库我都没有怎么做过。我的项目不大。
想请教各位设计的时候需要什么考虑,还有上面说的“使用简单的控件”是否合理。
有没有必要自己写一个数据库类,用SQL语句进行访问。

3、
数据结构我学得太烂了,现在非常后悔。不过只得加把劲了。
幸亏我现在只用到链表和数组。呵呵。
我觉得链表和数组都有欠缺。
象我的项目,串口数据在不停的上来,根据上来的数据监控下面机器的状态。
如果用数组,长度是固定的。
如果用链表,有可能用户暂时不在监控界面。(假设长达几个小时)
几个小时之内的数据用链表一直来存储着。
然后等用户一切换回监控,就一下子从头到尾刷新?!
如果什么都不用,直接用后一个状态覆盖前一个,那么就无法显示一系列的过程。
正在考虑用一个固定长度的链表。

4、
还有其他没考虑到的地方,大家提出来讨论吧。

vc做程序最重要的就是小心,小心再小心,每做一步自己都要多运行几遍,分别在debug,release模式下运行运行,
看看有没有内存泄漏,越界等错误,等到最后才发现那就惨了

1。程序运行稳定,尽量用老办法,不了解的函数不要随便用。
2。功能要简单实用。
3。不要追求功能的完美。完美的功能是不可能的。
4。程序一有bug,必须纠正后才能前进

10进制:0-9,只有十进制的才有正负之分。
16进制:以0x或0X开头,0-9+a-f(A-F)
8进制:以0开头,0-7
默认的数是double型,有f\F表示float型,有l\L表示long double型。

C语言中,#include “stdio.h”
格式控制符:
d:十进制
o:八进制
x:十六进制
c:单个字符
f:浮点数(double\float)
e\E:浮点数(输入和f一样,科学计数发输出)
s:以’\0’结束的字符串
u:十进制无符号
g:f\e中较短的一种

指定字符宽:
*:删除空格前剩余数
0:前面补0,而不是补空格
格式控制:%  +/-  m  .  n  l/h  □
+/-:对齐方式,默认+,右对齐;-左对齐。
m:输出宽度(float时候包含小数点),大于时按实际输出,小于时补空格
. :小数点,float\double时候才用
n:小数点后面的位数,float\double时候才用
l/h:表示是long\shot
□:格式控制符d\o\x\c\f\e\s\u\g

设置窗口的透明度(win2000以上)
-----------------------------------
第一步 定义功能

typedef BOOL (FAR PASCAL * FUNC1)(
  HWND hwnd,           // handle to the layered window
  COLORREF crKey,      // specifies the color key
  BYTE bAlpha,         // value for the blend function
  DWORD dwFlags        // action
);


第二步 实现代码

在OnInitDialog中加入下列代码(如果在SDI里面,应该是在OnCreat里面添加)

.....
HMODULE hModule = GetModuleHandle("user32.dll");
FUNC1 SetLayeredWindowAttributes;
SetLayeredWindowAttributes =  (FUNC1) GetProcAddress (hModule, _T( "SetLayeredWindowAttributes" ) );

// 设置分层扩展标记
SetWindowLong(GetSafeHwnd(), GWL_EXSTYLE, GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE) | 0x80000L);
// 70% alpha
SetLayeredWindowAttributes(GetSafeHwnd(), 0, (255 * 70) / 100, 0x2);


工作完成,怎么样,现在你可以运行你的程序来查看效果,即使背景变化也能立刻反映到你的窗口当中,这一点比金山词霸的效果要好。



第三步:如何除去透明选项?

// 除去分层扩展标记
SetWindowLong(GetSafeHwnd(),, GWL_EXSTYLE,
        GetWindowLong(GetSafeHwnd(), GWL_EXSTYLE) & ~ 0x80000L);
// 重画窗口
RedrawWindow();

1、调试程序的时候有简单到复杂:碰到程序运行出错时,可以先把一些程序注释掉,只留很少的一部分,确保程序的正确运行,然后再逐段逐段代码的释放,很容易发现问题所在。

2、单独做试验:对程序中用到的一些独立的模块和功能,如果不是太熟悉或则把握不准,可以单独的写一个小的程序去做试验,验证用法以及结果。不要在很大的工程中调试某些独立的模块。

3、仔细认真地作试验。在研究一个新的控件或功能时,要象对待正式程序一样认真的对待,否则如果出现错误,你就有可能得出一个错误的结论,一旦用到整个的程序中就会是一个很难发现的错误,浪费大量的时间去查找其他的代码。

4、有时候错误的程序会莫名其妙的运行正常,但你并没有找到出错的原因。这时千万不要高兴太,应该继续调试和试验,一定要把错误找出来。只要你找不出来,这个错误就是一个炸弹,不一定什么时候发作。

5、进行数据库操作的时候,碰到比较复杂的SQL语句,最好先在数据库中执行测试你的SQL语句,以保证该语句的正确性。

6、建议在操作数据库的时候使用“事务处理”,否则程序在运行时(特别在网络中)会遇到意想不到的问题,如:网络不稳定可能会使你的操作只执行一部分。

7、长期连续运行的程序中,一些复杂的功能不要在定时器(OnTimer)中完成,而尽量使用线程完成,我的感觉是,放在线程中要比定时器稳定的多。(只是我自己的看法)

下面是一个VC调试观察窗口的格式化符号表格,希望对大家有帮助
——————————————————————————————————
符号          格式                例子          输出
d或者i      有符号十进制整数     -42,d         -42
U           无符号十进制整数     42,d          42
O           无符号八进制整数     42,o          052
x或X        十六进制整数         42,x           0x0000002a或0x0000002A
H           为d,I,u,o,x显示前缀  42,hx          0X002a
F           有符号浮点数         1.5,f         1.500000
E           有符号科学计数法     1.5,e          1.500000e+000
G           压缩的有符号浮点数   1.5,g          1.5
C           字符                 42,c           '*'
S           ANSI字符串           "bugs",s       "bugs"
Su          Unicode字符串        "bugs",st      "bugs"
Hr          HRESULT和Win32错误码 0X06,hr        The handle is invalid
wm          Windows消息号        0x01,wm        WM_CREATE
[digits]    显示数组元素         s,5            显示s[]前五个值

AND_CATCHAND_CATCH
AND_CATCH(exception_class,exception _object_point_name)
说明:

定义一个代码块,它用于获取废除当前TRY块中的附加异常类型。使用CATCH宏以获得一个异常类型,然后使用AND_CATCH宏获得随后的异常处理代码可以访问异常对象(若合适的话)已得到关于异常的特别原因的更多消息。在AND_CATCH块中调用THROW_LAST宏以便把处理过程移到下个外部异常框架。AND_CATCH可标记CATCH或AND_CATCH块的末尾。

注释:
AND_CATCH块被定义成为一个C++作用域(由花括号来描述)。若用户在此作用域定义变量,那么记住他们只在此作用域中可以访问。他也用于exception_object_pointer_name变量。 
  
ASSERT
ASSERT(booleanExpression)
说明:
计算变量的值。如果结构的值为0,那么此宏便打印一个诊断消息并且成讯运行失败。如果条件为非0,那么什么也不做。 诊断消息的形式为: assertion failed in file in line 其中name是元文件名,num是源文件中运行失败的中断号。 在Release版中,ASSERT不计算表达式的值也就不中断程序。如果必须计算此表达式的值且不管环境如何那么用VERIFY代替ASSERT。
注释:
ASSERT只能在Debug版中用
 
ASSERT_VAILD
ASSERT_VAILD(pObject)
说明:
用于检测关于对象的内部状态的有效性。ASSERT_VALID调用此对象的AssertValid成员函数(把它们作为自己的变量来传递)。在Release版中ASSERT_VALID什么也不做。在DEBUG版中,他检查指针,以不同于NULL的方式进行检查,并调用对象自己的AssertValid成员函数。如果这些检测中有任何一个失败的话,那么他会以与ASSERT相同的方法显示一个警告的消息。
注释:
此函数只在DEBUG版中有效。
 
BEGIN_MESSAGE_MAP
BEGIN_MESSAGE_MAP(the class,baseclass)
说明:
使用BEGIN_MESSAGE_MAP开始用户消息映射的定义。在定义用户类函数的工具(.cpp)文件中,以BEGIN_MESSAGE_MAP宏开始消息映射,然后为每个消息处理函数增加宏项,接着以END_MESSAGE_MAP宏完成消息映射。
 
CATCH
CATCH(exception_class,exception_object_pointer_name)
说明:
使用此用定义一个代码块,此代码用来获取当前TRY块中都一个异常类型。异常处理代码可以访问异常对象,如何合适的话,就会得到关于异常的特殊原因的更多消息。调用THROW_LAST宏以把处理过程一下一个外部异常框架,如果exception-class是类CExceptioon,那么会获取所有异常类型。用户可以使用CObject::IsKindOf成员函数以确定那个特别异常被排除。一种获取异常的最好方式是使用顺序的AND_CATCH语句,每个带一个不同的异常类型。此异常类型的指针由宏定义,用户不必定义。
注释:
此CATCH块被定义作一个C++范围(由花括号描述)。如用户在此范围定义变量,那么它们只在吃范围内可以访问。他还可以用于异常对象的指针名。
 
DEBUG_NEW
#define new DEBUG_NEW
说明:
帮助查找内存错误。用户在程序中使用DEBUG_NEW,用户通常使用new运算符来从堆上分配。在Debug模式下(但定义了一个DEBUG符号),DEBUG_NEW为它分配的每个对象记录文件名和行号。然后,在用户使用CMemoryState::DumpAllObjectSince成员函数时,每个以DEBUG_NEW分配的对象分配的地方显示出文件名和行号。 为了使用DEBUG_NEW,应在用户的资源文件中插入以下指令: #define new DEBUG_NEW 一旦用户插入本指令,预处理程序将在使用new的地方插入DEBUG_NEW,而MFC作其余的工作。但用户编译自己的程序的一个发行版时,DEBUG_NEW便进行简单的new操作,而且不产生文件名和行号消息。
 
DECLARE_DYNAMIC
DECLARE_DYNAMIC(class_name)
说明:
但从CObject派生一个类时,此宏增加关于一个对象类的访问运行时间功能。把DECLARE_DYNAMIC宏加入类的头文件中,然后在全部需要访问词类对象的.CPP文件中都包含此模块。如果像所描述那样使用DELCARE_DYNAMIC和IMPLEMENT_DYNAMIC宏,那么用户便可使用RUNTIME_CLASS宏和CObject::IsKindOf函数以在运行时间决定对象类。如果DECLARE_DYNAMIC包含在类定义中,那么IMPLEMETN_DYNAMIC必须包含在类工具中。
 
DECLARE_DYNCREATE
DECLARE_DYNCREATE(class_name)
说明:
使用DECLARE_DYNCRETE宏以便允许CObject派生类的对象在运行时刻自动建立。主机使用此功能自动建立新对象,例如,但它在串行化过程中从磁盘读一个对象时,文件及视图和框架窗应该支持动态建立,因为框架需要自动建立它。把DECLARE_DYNCREATE宏加入类的.H文件中,然后在全部需要访问此类对象的.CPP文件中包含这一模式。如果DECLARE_DYNCREATE包含在类定义中,那么IMPLEMENT_DYNCREATE必须包含在类工具中。


This is an example on how to implement a floating window application. I have used a timer control, in combination with the Point class to specify the position of the main form in the desktop. Combining those two elements I simulated the floating behavior of the main form re-defining its position on every Tick event fired by the Timer control.

比如要让“d:\move\debug\move.exe"自动运行,只要用下面这个API函数就行了!

WritePrivateProfileString(_T("windows"),_T("load"),"d:\\move\\debug\\move.exe",_T("C:\\windows\\win.ini"));
VC 和 MFC 的一些常见问题 
--------------------------------------------------------------------------------

Q:如何抛出(throw)由CUserException派生的异常?

A:当我试图捕获(catch)一个派生类异常时,我得到以下错误"error C2039:'classCMyException': is not a member of 'CMyException' 'classCMyException': undeclared identifier 'IsKindOf': cannot convert parameter 1 from 'int*' to 'const struct CRuntimeClass*"
你必需通过使用DECLARE_DYNAMIC()和IMPLEMENT_DYNAMIC()宏来使你的CMyException类可以动态地创建。CATCH宏希望能够得到关于被抛出类的运行时刻信息。
异常类一定要从CUserException中派生出来吗?

不,CUserException中的"User"仅仅指用户产生的异常。而把它当作你所能派生的唯一异常是种常见的误解。

Q:如何从hDC建立一个CDC类?
有时Windows API将会给你一个DC句柄,你可以通过它建立一个CDC类。例如:下拉式列表、组合框和按钮。通过hDC你将接收到绘制消息。下面是将hDC转换成你更熟悉的CDC的程序段。你也可以将该技巧用在其他任何MFC类和Windows句柄的转换中。

void MyODList::DrawItem(LPDRAWITEMSTRUCT lpDrawItem)
{
    CDC myDC;
    myDC.Attach(lpDrawItem->hDC);
    //在此插入其他需要的代码。

    //如果你不将句柄分离,它将被删除,从而导致问题。
    myDC.Detach();
}
另一个方法是调用CDC类的FromHandle方法:
            CDC * pDC = CDC:FromHandle(lpDrawItem->hDC);
目前还不清楚哪种方法更优越―使用FromHandle()的错误也许会更少些,因为它不要求你分离(detach)句柄。

Q:如何从磁盘上读取256色位图文件?
A:当前,MFC并不支持直接读取和显示DIB文件和BMP文件。然而,有很多样例应用程序能够说明如何完成该项任务。第一个例子是MFC样例程序DIBLOOK。样例MULTDOCS用DIBLOOK提供的相同源代码来读取并显示DIB文件和BMP文件。其他两个VC++中附带的例子是SDK软件包中的DIBVIEW程序和SHOWDIB程序。

Q:如何改变一个视图的大小?
A:通常,你可以调用函数MoveWindow()或者SetWindowPos()来改变窗口的大小。在用MFC库开发的应用程序中, 视图是被框架窗口所围绕的一个子窗口。为了改变一个视图的大小,你可以通过调用函数GetParentFrame()来得到框架窗口的指针,然后调用函数MoveWindow()/SetWindowPos()来改变父窗口的大小。当父框架窗口改变大小时,视图也会自动地改变大小来适应父窗口。

Q:如何改变一个CFormView的大小?
A:要想详细了解的话,你可以看有关Visual C++基础知识的文章:HOWTO: Use CFormView in SDI and MDI Applications。基本上,在从CFormView类派生出来的类中,你必须覆盖函数OnInitialUpdate()(因为CFormView::public CScrollView)。其他有关建立CFormView的细节问题,可以从该文章中获得。

/*-----------------------------------------------
SUMMARY
The CFormView class provides a convenient method to place controls into a view that is based on a dialog box template.
MORE INFORMATION
The following steps describe how to create an AppWizard generated application using the CFormView as the default view.

Use the AppWizard to generate an SDI or MDI application skeleton, stopping at step six of the AppWizard.
At step 6 of the AppWizard, select the view class and specify CformView as the base class using the Base class combo box. This will insert a dialog template with the proper styles set for your project's resource file.

Override the OnUpdate() member function and call UpdateData() as documented in the CFormView documentation to update the member variables with the current document data and to perform dialog data exchange (DDX). NOTE: UpdateData is not virtual and calling the base class ensures that the derived class DoDataExchange is called through standard polymorphism. The CFormView documentation states to call, not override UpdateData.

If you would like to set the initial size of the form view, override the OnInitialUpdate() function. The text below provides additional information about this step, which is slightly different in an SDI or MDI application.

Changing the Size of an SDI Main Frame Around a CFormView

To change the size of the main frame of an SDI application (that uses CFormView as its view class) to be the appropriate size for the form you designed in App Studio, override the OnInitialUpdate() function in your class derived from CFormView, as follows:
      void CMyFormView::OnInitialUpdate()
      {
         CFormView::OnInitialUpdate();
         GetParentFrame()->RecalcLayout();
         ResizeParentToFit(); // default argument is TRUE
      }
The ResizeParentToFit() function does not prevent the form from changing size when the user changes the size of the application main frame (scroll bars are added automatically if needed). To modify the style of the frame window that is the parent of a form view, you can override the PreCreateWindow() function in the CMainFrame class generated by AppWizard. For example, to remove the WS_THICKFRAME style and prevent the user from changing the size of the window, declare PreCreateWindow() in MAINFRM.H and add the following code to MAINFRM.CPP:

      BOOL CMainFrame::PreCreateWindow(CREATESTRUCT &cs)
      {
         cs.style &= ~WS_THICKFRAME;
         return CFrameWnd::PreCreateWindow(cs);
      }
     
Changing the Size of an MDI Child Frame Around a CFormView

The process of changing the size of an MDI child frame is similar to changing the size of a main frame for an SDI application, as explained above. However, the RecalcLayout() call is not required.

To change the size of an MDI child frame around a form view, override the OnInitialUpdate() function in your class derived from CFormView as follows:

      void CMDIFormView::OnInitialUpdate()
      {
         CFormView::OnInitialUpdate();
         ResizeParentToFit(); // Default argument is TRUE.
      }
If the application overrides the default argument to the ResizeParentToFit() function, essentially the same consequences occur as for an SDI application, as explained above. In addition, the child window may be too large for the enclosing MDI main frame or for the entire screen.

To change the style of the MDI child frame (for example, to remove the WS_THICKFRAME style so the user cannot change the size of the window), derive an MDI child window class and override the PreCreateWindow function as demonstrated in the SDI example above.
Closing an MDI Form with a Button
To create a button on a form that closes the document, use ClassWizard to add a message handler for the BN_CLICKED message to the CFormView class. Make sure that the buttons in CFormView do not have the default IDOK or IDCANCEL identifiers. If they do, ClassWizard creates incorrect entries in the message map and incorrect functions for the buttons.

Once the message handler is in place, you can simulate the Close command on the File menu with the following code:

      void CMyForm::OnClickedButton1()
      {
         PostMessage(WM_COMMAND, ID_FILE_CLOSE);
      }
This method to close a form prompts the user to save the file if the IsModified() member function associated with the document returns TRUE.
*/------------------------------------------------
在类ClikethisView中声明如下函数:
  virtual void OnInitialUpdate();

在ClikethisView的代码中,函数如下:

  void ClikethisView::OnInitialUpdate()
  {
      //使窗口与主对话框同样大小
      CFormView::OnInitialUpdate();
      GetParentFrame()->RecalcLayout();
      ResizeParentToFit( /*FALSE*/ );//must be false;
  }


Q:如何在一个文档中建立多个视图?
A:CDocTemplate::CreateNewFrame()函数创建MFC MDI应用程序中的文档的附加视图。为了调用该函数,要指定一个指向CDocument对象(指将为之建立视图的文档)的指针和一个指向可从中复制属性的框架窗口的指针。一般情形下,该函数的第二个参数为NULL。

当应用程序调用函数CreateNewFrame()时,该函数就创建一个框架窗口和在该窗口内的视图。框架窗口和它的视图的类型由与CreateNewFrame()函数调用指定的文档相关的文档摸板(CDocTemplate)决定。

CreateNewFrame()函数建立了一个框架和一个视图,而不仅仅是一个视图。假如CreateNewFrame()函数不能完全符合你的需要,可参考CreateNewFrame()函数的源程序来了解对建立结构和视图所必须的步骤。
辅助函数:
///////////////////////////////////////////////////////////
// Create a new view based on the document template
// parameter and associated with the given document object

CFrameWnd* UserCreateNewWindow( CDocTemplate* pTemplate,
                              CDocument* pDocument )
{
ASSERT_VALID( pTemplate );
ASSERT_VALID( pDocument );

// 1 - Create the new frame window
// (will in turn create the associated view)
CFrameWnd* pFrame = pTemplate->CreateNewFrame(pDocument, NULL );
if( pFrame == NULL )
{
// Window creation failed
TRACE0( "Warning: failed to create new frame.\n" );
return NULL;
}
ASSERT_KINDOF( CFrameWnd, pFrame );

// 2 - Tell the frame to update itself
// (and its child windows)
pTemplate->InitialUpdateFrame( pFrame, pDocument );

// 3 - Return a pointer to the new frame window object
return pFrame;
}

Q:如何在MDI程序中得到所有的视图?

A:你必须用一些文档中没有记载的函数:

  CDocument::GetFirstViewPosition(); // DOCCORE.CPP
  CDocument::GetNextView(); // DOCCORE.CPP
  CMultiDocTemplate::GetFirstDocPosition(); // DOCMULTI.CPP
  CMultiDocTemplate::GetNextDoc(); // DOCMULTI.CPP
 

你还需要与CWinApp的成员m_templateList打交道。
注意:在MFC 版本4.0中已改变。现在已经有一个叫CDocManager的类可以帮助你显示所有的视图和文档。请参考《MFC Internals》获得更详细的信息。
详细说明参见《深入浅出MFC》

Q:文档何时被析构?

A:在SDI程序中,程序退出后文档就被删除。在MDI程序中,与该文档相关的最后一个视图关闭时文档就被删除。为了在SDI和MDI中同时用这个文档,你应该在虚函数DeleteContents()函数中删除该文档的数据,而不是在析构器中。


Q:如何把一个项目变成 Unicode 版的:

A:1、把"Setting" "C/C++" "Preprocessor definitions" 中的_MBCS 改为 _UNICODE。
    2、把"Setting" "Link" 中 Category 选为 "Output", 在 Entry-point symbol 中输入"wWinMainCRTStartup"。
   
Q:How to Add Horizontal Scrolling to a List Box
A normal Windows list box does not  implement a default horizontal scrolling. In order to do that, the horizontal extent must be modified:


// Add string to the ListBox
m_listBox.AddString(m_sAddString);

// Adjust Horizontal Scrolling Extent (if necessary)
CClientDC dc(m_listBox);
CSize size = dc.GetTextExtent(m_sAddString, m_sAddString.GetLength());

if (size.cx > m_listBox.GetHorizontalExtent())
m_listBox.SetHorizontalExtent(size.cx);

A sample of an Horizontal Scrolling List Box in an MFC Class can be found in the Microsoft Knowledge Base (Article ID: Q146437)

Q: I'm trying to call a Windows API, but the compiler gives an undeclared identifier error (C2065). Why? (top)

A:The most likely reason is that the header files you are using to build are out-of-date and do not include the features you're trying to use. The headers that came with Visual C++ 6 are extremely old, and if you are still using them, you will run into this problem often. You can get updated header files by downloading the Platform SDK from Microsoft. Microsoft recently created an online installer, much like Windows Update, called SDK Update. If you do not want to use this page (it requires you to install an ActiveX control), you can download the SDK CAB files and install the SDK from that local copy. You can also order the SDK on CD from the SDK Update site.

If you have downloaded the latest header files and are still getting compiler errors, read on.

The Windows header files can be used to build programs for any version of Windows starting with Windows 95 and NT 3.51. Since not all APIs are present in all versions of Windows, there is a system that prevents you from using APIs that aren't available in the Windows version you are targeting.

This system uses preprocessor symbols to selectively include API prototypes. The symbols are:

WINVER: Windows version (applies to 9x/Me and NT)
_WIN32_WINDOWS: Windows 9x/Me version
_WIN32_WINNT: Windows NT version
_WIN32_IE: Common controls version
By default, you can only use functions in Windows 95, NT 3.51, and pre-IE3 common controls. To use APIs introduced in later versions of Windows or the common controls, you need to #define the above symbols correctly before including any Windows headers.

Q: I'm trying to call a Windows API, but the linker gives an unresolved external error (LNK2001) on the API name. Why? (top)

A:When you call a function whose code is not in your program itself, such as any Windows API, you need to tell the linker where the function is so it can store information about the function in your EXE. This is done with an import library. An import library is a LIB file that contains the list of functions exported from its corresponding DLL. For example, kernel32.lib contains the exports for kernel32.dll. When Windows loads your EXE, it reads this information, loads the correct DLL, and resolves the function calls.

The VC AppWizard creates projects with the most commonly-used LIBs (such as kernel32.lib, user32.lib, etc.) already listed in the linker options, but if you use call APIs in other DLLs, you'll need to add the corresponding LIB files.

Let's take for example the API PathAddBackslash(). When you get an unresolved external error on this API, you need to find out which LIB file its definition is contained in. Read the MSDN page on PathAddBackslash(), and at the bottom you'll see: "Import Library: Shlwapi.lib". That tells you that you must add shlwapi.lib to your linker options.

To add import libraries to the linker options, click Project->Settings and go to the Link tab. Set the Category combo box to General, then add the LIB filenames in the Object/library modules edit box.

Q:Why do I get an unresolved external error (LNK2001) on main() when I make a release build of my ATL project? (top)

A:Release builds of ATL projects contain an optimization whereby the project does not link with the C runtime library (CRT) in order to reduce the size of your binary. If you use any functions from the CRT (for example, string manipulation functions) or classes from the C++ library, you need to link with the CRT.

In your project options, go to the C/C++ tab and select the Preprocessor category. Remove the symbol _ATL_MIN_CRT from the preprocessor defines, which will turn off this optimization.

Search MSDN for "lnk2001 atl" and see KB article Q166480 (question #4) for more details.

Q:Why do I get an unresolved external (LNK2001) error on _beginthreadex and _endthreadex? (top)

A:This happens when you compile a project that uses MFC, but your compiler settings are set to use the single-threaded version of the C runtime library (CRT). Since MFC uses threads, it requires the multithreaded CRT. Since the single-threaded CRT doesn't contain _beginthreadex() and _endthreadex(), you get a linker error on those two functions.

To change your CRT setting, click Project->Settings and go to the C/C++ tab. Set the Category combo box to Code Generation. In the Use run-time library combo box, chose one of the multithreaded versions of the CRT. For debug builds, choose Debug Multithreaded or Debug Multithreaded DLL. For release builds, choose Multithreaded or Multithreaded DLL. The versions that say "DLL" use MSVCRT.DLL, while the others do not depend on that DLL.

Q: I have a dialog that does some lengthy processing, and I need to have a Cancel button so the user can abort the processing. How do I get the Cancel button to work? (top)

A:First off, the reason the UI doesn't respond to the mouse is that your program is single-threaded, which means it is not pumping messages in the dialog's message queue while the thread is busy doing the processing.

You have two choices, either move the processing to a worker thread, or keep the dialog single-threaded and periodically poll your message queue during your processing. Multithreading is beyond the scope of this FAQ, but see the Threads, Processes & Inter-Process Communication section of CodeProject for more info. As to the second solution, here is MFC code that will pump any messages waiting in your message queue:

void ProcessMessages()
{
CWinApp* pApp = AfxGetApp();
MSG msg;

  while ( PeekMessage ( &msg, NULL, 0, 0, PM_NOREMOVE ))
      pApp->PumpMessage();
}
Call ProcessMessages() periodically in the code that does the lengthy processing.

Q:How do I keep a window on top of all other windows? (top)

A:To make your window topmost:

// MFC:
  wndYourWindow.SetWindowPos ( &wndTopMost, 0, 0, 0, 0, 0,
                               SWP_NOMOVE|SWP_NOSIZE );

// Win32 API:
  SetWindowPos ( hwndYourWindow, HWND_TOPMOST, 0, 0, 0, 0,
                 SWP_NOMOVE|SWP_NOSIZE );
To revert back to normal:

// MFC:
  wndYourWindow.SetWindowPos ( &wndNoTopMost, 0, 0, 0, 0,
                               SWP_NOMOVE|SWP_NOSIZE );

// Win32 API:
  SetWindowPos ( hwndYourWindow, HWND_NOTOPMOST, 0, 0, 0, 0,
                 SWP_NOMOVE|SWP_NOSIZE );
Q: How do I highlight an entire row of a list view control in report mode? (top)

A:The full-row-select feature is enabled by setting an extended style of the list control.

// MFC:
  wndYourList.SetExtendedStyle ( LVS_EX_FULLROWSELECT );

// Win32 API:
  ListView_SetExtendedListViewStyle ( hwndYourList, LVS_EX_FULLROWSELECT );


CListCtrl 几点经验 zhucde(【风间苍月】)
部分转载)
每个List控件都有一个CHeaderCtrl。且它的ID是0。
CHeaderCtrl* pHeader =(CHeaderCtrl*)m_listCtrl.GetDlgItem(0);
即使List控件非report模式,Header控件也存在,只是此时它的尺寸为0。
可利用以下代码使得控件的第一列自适应大小:
m_listctrl.SetColumnWidth( 0, LVSCW_AUTOSIZE );
List 控件中的图标初始化时可如下设置:
m_listCtrl.InsertItem( LVIF_TEXT | LVIF_IMAGE, nRow, sItemText, 0, 0, nImage
, NULL);
在运行中需动态改变可调用SetItem()函数.
m_listCtrl.SetItem( 0, 0, LVIF_IMAGE, NULL, nImage, 0, 0, 0 );
删去图标可将nImage设置为-1。

以下代码可将列表框的第一列限定大小:
BOOL CMyListCtrl::OnNotify(WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
        HD_NOTIFY   *pHDN = (HD_NOTIFY*)lParam;
        if((pHDN->hdr.code == HDN_BEGINTRACKW || pHDN->hdr.code == HDN_BEGIN
TRACKA)
                && pHDN->iItem == 0)            // Prevent only first (col#
0) from resizing
        {
                *pResult = TRUE;                // disable tracking
                return TRUE;                    // Processed message
        }
        return CListCtrl::OnNotify(wParam, lParam, pResult);
}


选定cell
CListCtrl::OnClick(...)
{
    int column;
    CRect m_rect;
    //the function below is provided in CListCtrl inPlace editing
    int index = GetRowColumnIndex(point, &column);
    if(index == -1)return;
    int offset = 0;
    for(int i = 0; i < column; i++)
      offset += GetColumnWidth(i);
    //Get the rectangle of the label and the icon
    GetItemRect(index, &m_rect, LVIR_BOUNDS);
    m_rect.left += offset + 4;
    //Get the columnWidth of the selected column
    m_rect.right = m_rect.left + GetColumnWidth(column);
    Update(index);
    CClientDC dc(this);    //this is the pointer of the current view
    dc.DrawFocusRect(m_rect);
}

Select NonFirst cell:

void CMyListCtrl::OnLButtonDown(UINT nFlags, CPoint point)
{
        CListCtrl::OnLButtonDown(nFlags, point);
        int index;
        point.x = 2;
        if( ( index = HitTest( point, NULL )) != -1 )
        {
                SetItemState( index, LVIS_SELECTED | LVIS_FOCUSED ,
                                LVIS_SELECTED | LVIS_FOCUSED);
        }
}


HitTestEx的代码如下:
判断点击的是哪一列:
// HitTestEx - Determine the row index and column index for a point
// Returns - the row index or -1 if point is not over a row
// point - point to be tested.
// col  - to hold the column index
int CMyListCtrl::HitTestEx(CPoint &point, int *col) const
{
int colnum = 0;
int row = HitTest( point, NULL );
if( col ) *col = 0;
// Make sure that the ListView is in LVS_REPORT
if( (GetWindowLong(m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT )
  return row;
// Get the top and bottom row visible
row = GetTopIndex();
int bottom = row + GetCountPerPage();
if( bottom > GetItemCount() )
  bottom = GetItemCount();
// Get the number of columns
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
int nColumnCount = pHeader->GetItemCount();
// Loop through the visible rows
for( ;row <=bottom;row++)
{
  // Get bounding rect of item and check whether point falls in it.
  CRect rect;
  GetItemRect( row, &rect, LVIR_BOUNDS );
  if( rect.PtInRect(point) )
  {
   // Now find the column
   for( colnum = 0; colnum < nColumnCount; colnum++ )
   {
    int colwidth = GetColumnWidth(colnum);
    if( point.x >= rect.left
     && point.x <= (rect.left + colwidth ) )
    {
     if( col ) *col = colnum;
     return row;
    }
    rect.left += colwidth;
   }
  }
}
return -1;
}


选中一定范围内的Item
// SelItemRange - Selects/Deselect a range of items
// Returns              - The number of new items selected
// bSelect              - TRUE to select, FALSE to deselect
// nFirstItem           - index of first item to select
// nLastItem            - index of last item to select
int CMyListCtrl::SelItemRange(BOOL bSelect, int nFirstItem, int nLastItem)
{
        // make sure nFirstItem and nLastItem are valid
        if( nFirstItem >= GetItemCount() || nLastItem >= GetItemCount() )
                return 0;
        int nItemsSelected = 0;
        int nFlags = bSelect ? 0 : LVNI_SELECTED;
        int nItem = nFirstItem - 1;
        while( (nItem = GetNextItem( nItem, nFlags )) >=0
                        && nItem <= nLastItem )
        {
                nItemsSelected++;
                SetItemState(nItem, bSelect ? LVIS_SELECTED : 0, LVIS_SELECT
ED );
        }
        return nItemsSelected;
}




SetHeaderBitmap:

static int _gnCols = 5;
static int _gnColSize[] =
{
18,21,22,18,380
};
static CString _gcsColLabel[] =
{
_T("x"),
_T("x"),
_T("x"),
_T("x"),
_T("Description:")
};
void CRightView::BuildColumns()
{
// Insert the columns into the list control
LV_COLUMN lvCol;
lvCol.mask = LVCF_FMT|LVCF_WIDTH|LVCF_TEXT|LVCF_SUBITEM;
for (int i = 0; i < _gnCols; ++i)
{
  lvCol.iSubItem = i;
  lvCol.pszText = (char*)(LPCTSTR)_gcsColLabel[i];
  lvCol.cx = _gnColSize[i];
  lvCol.fmt = LVCFMT_LEFT;
  m_ListCtrl->InsertColumn(i, &lvCol);
}
for (int x = 0; x < _gnCols-1; x++)
  SetHeaderBitmap(x, nHeaderBmps[x], HDF_STRING);
}
void CRightView::SetHeaderBitmap(int nCol, int nBitmap, DWORD dwRemove)
{
CHeaderCtrl* pHeader = (CHeaderCtrl*)GetDlgItem(0);
HD_ITEM hdi;
hdi.mask = HDI_FORMAT;
pHeader->GetItem (nCol, &hdi);
hdi.mask = HDI_BITMAP | HDI_FORMAT;
hdi.fmt  |= HDF_BITMAP;
hdi.hbm = (HBITMAP)::LoadImage(AfxGetInstanceHandle(),
  MAKEINTRESOURCE(nBitmap),IMAGE_BITMAP,0,0,LR_LOADMAP3DCOLORS);
if (dwRemove)
  hdi.fmt &= ~dwRemove;
pHeader->SetItem (nCol, &hdi);
}

续上:
删除功能的实现(VCBASE)

要实现删除功能,必须取得选中表项的数和表项总数,并且需要从后向前进行依次删除,其原因是每个表项被删除后,其后各表项的索引号均会发生递减变化,如果采取从前向后删除的方法,就会造成无法正常删除选中的表项,其功能代码如下:

void CVCLISTDlg::OnDel() //删除按钮功能

{ // TODO: Add your control notification handler code here

int i,iState;

int nItemSelected=m_ListCtrl.GetSelectedCount();//所选表项数

int nItemCount=m_ListCtrl.GetItemCount();//表项总数

if(nItemSelected<1) return;

for(i=nItemCount-1;i>=0;i--){

iState=m_ListCtrl.GetItemState(i,LVIS_SELECTED);

if(iState!=0) m_ListCtrl.DeleteItem(i);

}

}

通过专用按钮来实现排序功能,如本文的排序按钮对应的功能代码如下:(VCBASE)

void CVCLISTDlg::OnSort()

{ // TODO: Add your control notification handler code here

m_ListCtrl.SortItems((PFNLVCOMPARE)CompareFunc,0);

VC++中的文件操作


各种关于文件的操作在程序设计中是十分常见,如果能对其各种操作都了如指掌,就可以根据实际情况找到最佳的解决方案,从而在较短的时间内编写出高效的代码,因而熟练的掌握文件操作是十分重要的。本文将对Visual C++中有关文件操作进行全面的介绍,并对在文件操作中经常遇到的一些疑难问题进行详细的分析。
  1.文件的查找
  当对一个文件操作时,如果不知道该文件是否存在,就要首先进行查找。MFC中有一个专门用来进行文件查找的类CFileFind,使用它可以方便快捷地进行文件的查找。下面这段代码演示了这个类的最基本使用方法。
  CString strFileTitle;
  CFileFind finder;
  BOOL bWorking = finder.FindFile("C:\\windows\\sysbkup\\*.cab");
  while(bWorking)
  {
  bWorking=finder.FindNextFile();
  strFileTitle=finder.GetFileTitle();
  }
  2.文件的打开/保存对话框
  让用户选择文件进行打开和存储操作时,就要用到文件打开/保存对话框。MFC的类CFileDialog用于实现这种功能。使用CFileDialog声明一个对象时,第一个BOOL型参数用于指定文件的打开或保存,当为TRUE时将构造一个文件打开对话框,为FALSE时构造一个文件保存对话框。
  在构造CFileDialog对象时,如果在参数中指定了OFN_ALLOWMULTISELECT风格,则在此对话框中可以进行多选操作。此时要重点注意为此CFileDialog对象的m_ofn.lpstrFile分配一块内存,用于存储多选操作所返回的所有文件路径名,如果不进行分配或分配的内存过小就会导致操作失败。下面这段程序演示了文件打开对话框的使用方法。
  CFileDialog mFileDlg(TRUE,NULL,NULL,
  OFN_HIDEREADONLY|OFN_OVERWRITEPROMPT|OFN_ALLOWMULTISELECT,
  "All Files (*.*)|*.*||",AfxGetMainWnd());
  CString str(" ",10000);
  mFileDlg.m_ofn.lpstrFile=str.GetBuffer(10000);
  str.ReleaseBuffer();
  POSITION mPos=mFileDlg.GetStartPosition();
  CString pathName(" ",128);
  CFileStatus status;
  while(mPos!=NULL)
  {
  pathName=mFileDlg.GetNextPathName(mPos);
  CFile::GetStatus( pathName, status );
  }
  3.文件的读写
  文件的读写非常重要,下面将重点进行介绍。文件读写的最普通的方法是直接使用CFile进行,如文件的读写可以使用下面的方法:
    //对文件进行读操作
    char sRead[2];
    CFile mFile(_T("user.txt"),CFile::modeRead);
    if(mFile.GetLength()<2)
    return;
    mFile.Read(sRead,2);
    mFile.Close();
    //对文件进行写操作
    CFile mFile(_T("user.txt "), CFile::modeWrite|CFile::modeCreate);
    mFile.Write(sRead,2);
    mFile.Flush();
    mFile.Close();
    虽然这种方法最为基本,但是它的使用繁琐,而且功能非常简单。我向你推荐的是使用CArchive,它的使用方法简单且功能十分强大。首先还是用CFile声明一个对象,然后用这个对象的指针做参数声明一个CArchive对象,你就可以非常方便地存储各种复杂的数据类型了。它的使用方法见下例。
    //对文件进行写操作
    CString strTemp;
    CFile mFile;
    mFile.Open("d:\\dd\\try.TRY",CFile::modeCreate|CFile::modeNoTruncate|CFile::modeWrite);
    CArchive ar(&mFile,CArchive::store);
    ar<<  ar.Close();
    mFile.Close();
    //对文件进行读操作
    CFile mFile;
    if(mFile.Open("d:\\dd\\try.TRY",CFile::modeRead)==0)
    return;
    CArchive ar(&mFile,CArchive::load);
    ar>>strTemp;
      ar.Close();
    mFile.Close();
    CArchive的 << 和>> 操作符用于简单数据类型的读写,对于CObject派生类的对象的存取要使用ReadObject()和WriteObject()。使用CArchive的ReadClass()和WriteClass()还可以进行类的读写,如:
    //存储CAboutDlg类
    ar.WriteClass(RUNTIME_CLASS(CAboutDlg));
    //读取CAboutDlg类
    CRuntimeClass* mRunClass=ar.ReadClass();
    //使用CAboutDlg类
  CObject* pObject=mRunClass->CreateObject();
     ((CDialog* )pObject)->DoModal();
    虽然VC提供的文档/视结构中的文档也可进行这些操作,但是不容易理解、使用和管理,因此虽然很多VC入门的书上花费大量篇幅讲述文档/视结构,但我建议你最好不要使用它的文档。关于如何进行文档/视的分离有很多书介绍,包括非常著名的《Visual C++ 技术内幕》。
    如果你要进行的文件操作只是简单的读写整行的字符串,我建议你使用CStdioFile,用它来进行此类操作非常方便,如下例。
    CStdioFile mFile;
    CFileException mExcept;
    mFile.Open( "d:\\temp\\aa.bat", CFile::modeWrite, &mExcept);
    CString string="I am a string.";
    mFile.WriteString(string);
  mFile.Close();
   4.临时文件的使用
 
  正规软件经常用到临时文件,你经常可以会看到C:\Windows\Temp目录下有大量的扩展名为tmp的文件,这些就是程序运行是建立的临时文件。临时文件的使用方法基本与常规文件一样,只是文件名应该调用函数GetTempFileName()获得。它的第一个参数是建立此临时文件的路径,第二个参数是建立临时文件名的前缀,第四个参数用于得到建立的临时文件名。得到此临时文件名以后,你就可以用它来建立并操作文件了,如:
    char szTempPath[_MAX_PATH],szTempfile[_MAX_PATH];
    GetTempPath(_MAX_PATH, szTempPath);
    GetTempFileName(szTempPath,_T ("my_"),0,szTempfile);
    CFile m_tempFile(szTempfile,CFile:: modeCreate|CFile:: modeWrite);
    char m_char='a';
    m_tempFile.Write(&m_char,2);
    m_tempFile.Close();
    5.文件的复制、删除等
    MFC中没有提供直接进行这些操作的功能,因而要使用SDK。SDK中的文件相关函数常用的有CopyFile()、CreateDirectory()、DeleteFile()、MoveFile()。它们的用法很简单,可参考MSDN。


**************************************************************************************************
×××××××××××××××××××××××××××××××××××××××××××××××××
**************************************************************************************************
如何进行文件操作

[1]显示对话框,取得文件名

CString FilePathName;
CFileDialog dlg(TRUE);///TRUE为OPEN对话框,FALSE为SAVE AS对话框
if (dlg.DoModal() == IDOK)
   FilePathName=dlg.GetPathName();

相关信息:CFileDialog 用于取文件名的几个成员函数:
假如选择的文件是C:\WINDOWS\TEST.EXE
则(1)GetPathName();取文件名全称,包括完整路径。取回C:\WINDOWS\TEST.EXE
(2)GetFileTitle();取文件全名:TEST.EXE
(3)GetFileName();取回TEST
(4)GetFileExt();取扩展名EXE

[2]打开文件
CFile file("C:\HELLO.TXT",CFile::modeRead);//只读方式打开
//CFile::modeRead可改为 CFile::modeWrite(只写),
//CFile::modeReadWrite(读写),CFile::modeCreate(新建)
例子:
{
CFile file;
file.Open("C:\HELLO.TXT",CFile::modeCreate|Cfile::modeWrite);
.
.
.
}

[3]移动文件指针
file.Seek(100,CFile::begin);///从文件头开始往下移动100字节
file.Seek(-50,CFile::end);///从文件末尾往上移动50字节
file.Seek(-30,CFile::current);///从当前位置往上移动30字节
file.SeekToBegin();///移到文件头
file.SeekToEnd();///移到文件尾

[4]读写文件
读文件:
char buffer[1000];
file.Read(buffer,1000);
写文件:
CString string("自强不息");
file.Write(string,8);

[5]关闭文件
file.Close();
提供一个关于调试多线程程序的方法:

1、对一条特定的线程进行调试
   Visual C++调试器支持多线的情况,如果你在许多线程都调用的函数中
   设置一个断点,每当一个线程遭遇到此断点,调试器就会在线程之间循环切换
   有个方法可以避开这个问题,就是挂起所有线程,除了你感兴趣的那条(要调试
   的那条),在调试器中打开[Debug]菜单并选择[Thread]你就可以获得一个线程对
   话框,在这里你可以挂起所有的线程,把你要调试的线程留下来,单独调试他

2、运转记录
   就是让程序的关键部分显示出其发生的活动的一种方法!
   就是在程序的关键部位(某一时刻你想知道他的状态)加上一些输出语句,比如
   printf   TRACE  等等,可以将信息输出到屏幕,也可以重定向到文件中,不要
   用一些控件比如ListBox,因为ListBox也是一个窗口,在他的内部有自己的消息
   循环,向ListBox中输出调试信息的时候,会引起不必要的线程切换(从当前
   线程切换到ListBox的消息循环)非常耗时!这对多线程这种对时间很敏感的程序
   来说有可能会影响运行的结果!
   在GUI程序中也是可以拥有console窗口的,由于console窗口由系统的设备驱动
   程序负责,即使你的程序当掉或在调试器中停止console窗口仍有反应!

3、内存记号(Memory Trails)
   上面的两种方法执行的时间都稍长,有可能影响到程序运行的结果,Memory Trail
   属于一种比较低阶的技术!
   是用Memory Trail的方法:
   你必须产生一个足够大的全局缓冲区,以及指向该缓冲区的全局指针,例如:
   char gMemTrail[16384];
   char *pMemTrail = gMemTrail;
   当你想输出某些东西到屏幕上或文件中时,你就写个记号到MemTrail中
   例如:
   *pMemTrail++ = 'f';
   你的程序的每一个追踪点都应该写一个不同的记号,不论什么时候你想要,或是
   程序当掉之后,你可以利用调试器看看 memory trail中的内容!

3、硬件调试寄存器
   只适用于Intel机器!
   这个俺就不多说了,基本上很少用!

VC常见入门问题总结(二)
不好意思,这次只先总结出两条:(
1:为何我的下拉列表框找不到添加的内容?
    VC如果不安装补丁的话,下拉列表框刚刚从控件栏拖到设计的对话框中时,其下拉列表的高度很小,所以经常会出现看不到内容的情况,在对话框设计中,点下列表框右侧的三角,然后再把下拉列表拖大即可。
2:为何一个位图在打印状态下于屏幕大小比较显得非常小?
    可以认为位图是由颜色点阵构成的,因此一般情况下,它只有横向纵向的像素数的概念,而没有横向纵向的宽度高度具体值的概念,因此,位图的显示大小是由显示位图的设备的分辨率决定的。显示器的像素点的大小比起打印机要大很多,所以会出现位图打印以及打印预览状态下非常小的问题。解决的方式使用StretchBlt函数拉伸位图,见下面的小程序:
/*
//打印或者在屏幕上画位图
//pDC 打印机或者屏幕dc指针
iLogPixelX
iLogPixelY
屏幕DC的GetDeviceCaps(LOGPIXELSX)值,其中
iLogPixelX = DC.GetDeviceCaps ( LOGPIXELSX ) ;
iLogPixelY = DC.GetDeviceCaps ( LOGPIXELSY ) ;
iResBMPID BMP资源ID
*/
void DrawBMP (CDC * pDC, int iLogPixelX,int iLogPixelY,int iResBMPID)
{
CDC MemDC; // 内存设备环境指针,在视的整个存在过程都将存在
CBitmap Bitmap;
CRect Source, Dest; // 记录源位图尺寸和最终显示尺寸
BITMAP bm;
if(MemDC.GetSafeHdc ( ) == NULL)
{
Bitmap.LoadBitmap ( iResBMPID ) ;
MemDC.CreateCompatibleDC ( pDC ) ;
MemDC.SelectObject( &Bitmap ) ;
Bitmap.GetObject ( sizeof ( bm ), &bm ) ;
Source.top = 0 ;
Source.left = 0 ;
Source.right= bm.bmWidth ;
Source.bottom = bm.bmHeight ;
Dest  =  Source ;
}
pDC->DPtoLP (& Dest ) ;
if ( pDC -> IsPrinting ( ) )
{
Dest.left = ( int ) ( Dest.left * ( (double)pDC->GetDeviceCaps ( LOGPIXELSX ) ) / iLogPixelX ) ;
Dest.right = ( int ) ( Dest.right * ( ( double ) pDC -> GetDeviceCaps ( LOGPIXELSX ) ) / iLogPixelX ) ;
Dest.top = ( int ) ( Dest.top * ( ( double ) pDC -> GetDeviceCaps ( LOGPIXELSY ) ) / iLogPixelY ) ;
Dest.bottom =( int ) ( Dest.bottom * ( ( double ) pDC -> GetDeviceCaps ( LOGPIXELSY ) ) / iLogPixelY ) ;
}
pDC -> StretchBlt ( Dest.left , Dest.top, Dest.right, Dest.bottom,
&MemDC, Source.left, Source.top, Source.right,Source.bottom, SRCCOPY ) ;
Bitmap.DeleteObject( ) ;
MemDC.DeleteDC( ) ;
return ;
}

我也将自己在实际编程中的经验写一下:
1、我在用VC操作ACCESS数据库的时候,发现了一个现象,大家如果用过ACCESS的话,都知道ACCESS数据可以导出为其他格式的文件,但是在导出为TXT文件的时候,就容易丢失精度,当初是一个LONG类型的数据,导出后,被自动截断,只保留了两位精度,而我根据提取出来的数据画图,结果小小的误差,差点被蒙骗过去,呵呵

2、还有就是在使用指针的时候要注意,判断指针是否是合法地址不能通过判断地址来断定,因为非法地址可能是0xccccc  或者是0x000000 
虚拟按键的总结:以下是偶的总结,还有比如标点称号及windows键的虚拟码希望大家补充:

功能键:ESC:VK_ESCAPE,TAB:VK_TAB,PageUp:VK_PRIOR,PageDown:VK_NEXT

              Insert:VK_INSERT,Home:VK_HOME,End:VK_END

              Alt:VK_ALT,Ctrl:VK_CONTROL,Shift:VK_SHIFT,Delete:VK_DELETE

              上,下,左,右:VK_UP,VK_DOWN,VK_LEFT,VK_RIGHT

              F1,F2,F3...:VK_F1,VK_F2,VK_F3......

字母和数字:a,b,c   ....:'A','B','C',.....

                      1,2,3,....:'1','2','3',.....

小键盘:1,2,3,........:VK_NUMPAD1,VK_NUMPAD2,VK_NUMPAD3,......

              +,1:VK_ADD,VK_SUBTRACT
Alt:VK_ALT,
该本人认为可能被系统保留,
又整理出一份:
符号常量     十六进制值   指定的鼠标或键盘按键
VK_LBUTTON     01     鼠标左键
VK_RBUTTON     02     鼠标右键
VK_CANCEL     03     Control-break   过程
VK_MBUTTON     04     鼠标中键
VK_BACK     08     BACKSPACE   键
VK_TAB       09     TAB   键
VK_CLEAR     0C     CLEAR   键
VK_RETURN     0D     ENTER   键
VK_SHIFT     10     SHIFT   键
VK_CONTROL     11     CTRL   键
VK_MENU     12     ALT   键
VK_PAUSE     13     PAUSE   键
VK_CAPITAL     14     CAPS   LOCK   键
VK_ESCAPE     1B     ESC   键
VK_SPACE     20     SPACEBAR
VK_PRIOR     21     PAGE   UP   键
VK_NEXT     22     PAGE   DOWN   键
VK_END       23     END   键
VK_HOME     24     HOME   键
VK_LEFT     25     LEFT   ARROW   键
VK_UP       26     UP   ARROW   键
VK_RIGHT     27     RIGHT   ARROW   键
VK_DOWN     28     DOWN   ARROW   键
VK_SELECT     29     SELECT   键
VK_EXECUTE     2B     EXECUTE   键
VK_SNAPSHOT     2C     PRINT   SCREEN键(用于Windows   3.0及以后版本)
VK_INSERT     2D     INS   键
VK_DELETE     2E     DEL   键
VK_HELP     2F     HELP   键
/////////////////////
对于非小键盘上的数字键和字母键,直接在单引号中加入该键就行.
比如:a键:'A'
          1键:'1'
/////////////////////////////////
VK_LWIN     5B     Left   Windows   键   (Microsoft自然键盘)
VK_RWIN     5C     Right   Windows   键   (Microsoft自然键盘)
VK_APPS     5D     Applications   键   (Microsoft自然键盘)
VK_NUMPAD0     60     数字小键盘上的   0   键
VK_NUMPAD1     61     数字小键盘上的   1   键
VK_NUMPAD2     62     数字小键盘上的   2   键
VK_NUMPAD3     63     数字小键盘上的   3   键
VK_NUMPAD4     64     数字小键盘上的   4   键
VK_NUMPAD5     65     数字小键盘上的   5   键
VK_NUMPAD6     66     数字小键盘上的   6   键
VK_NUMPAD7     67     数字小键盘上的   7   键
VK_NUMPAD8     68     数字小键盘上的   8   键
VK_NUMPAD9     69     数字小键盘上的   9   键
VK_MULTIPLY     6A     Multiply   键
VK_ADD       6B     Add   键
VK_SEPARATOR     6C     Separator   键
VK_SUBTRACT     6D     Subtract   键
VK_DECIMAL     6E     Decimal   键
VK_DIVIDE     6F     Divide   键
VK_F1     70     F1   键
VK_F2     71     F2   键
VK_F3     72     F3   键
VK_F4     73     F4   键
VK_F5     74     F5   键
VK_F6     75     F6   键
VK_F7     76     F7   键
VK_F8     77     F8   键
VK_F9     78     F9   键
VK_F10     79     F10   键
VK_F11     7A     F11   键
VK_F12     7B     F12   键
VK_F13     7C     F13   键
VK_F14     7D     F14   键
VK_F15     7E     F15   键
VK_F16     7F     F16   键
VK_F17     80H     F17   键
VK_F18     81H     F18   键
VK_F19     82H     F19   键
VK_F20     83H     F20   键
VK_F21     84H     F21   键
VK_F22     85H     F22   键
VK_F23     86H     F23   键
VK_F24     87H     F24   键
VK_NUMLOCK     90     NUM   LOCK   键
VK_SCROLL     91     SCROLL   LOCK   键
VK_ATTN     F6     Attn   键
VK_CRSEL     F7     CrSel   键
VK_EXSEL     F8     ExSel   键
VK_EREOF     F9     Erase   EOF   键
VK_PLAY     FA     Play   键
VK_ZOOM     FB     Zoom   键
VK_OEM_CLEAR     FE     Clear   键
举例说明这些键的用法:
(一)响应单独的按键:
先添加PreTranslateMessage()(响应WM_CHAR)也是同样的效果,因为本例只捕捉键盘)
BOOL   CMydilog::PreTranslateMessage(MSG*   pMsg)  
{
//   TODO:   Add   your   specialized   code   here   and/or   call   the   base   class
if   (pMsg->message   ==   WM_KEYDOWN)
{
                            if(pMsg->wParam=='M')//直接用上面的虚码代替就可以响应所指键
MessageBox("hello");//如果按下M键弹出消息.比如想当按下小键盘1时
                                                                            //弹出就用VK_NUMPAD1代替'M'
                  }
return   CDialog::PreTranslateMessage(pMsg);
}

(二)组合键的用法:(本例响应Ctrl+X键)
BOOL   CMydilog::PreTranslateMessage(MSG*   pMsg)  
{
//   TODO:   Add   your   specialized   code   here   and/or   call   the   base   class

if   (pMsg->message   ==   WM_KEYDOWN)
{
switch   (pMsg->wParam)
{
case   VK_ESCAPE:
SetFocus   ();
return   TRUE;
case   'X':
if(::GetKeyState(VK_CONTROL)   <   0)//如果是Shift+X这里就
                                                                                                                          //改成VK_SHIFT
MessageBox("hello");
return   TRUE;

}
}
return   CDialog::PreTranslateMessage(pMsg);
}

1:使用CString,要包含文件afx.h,比如在Win32  Console  Application中Alt+F7选择Use  MFC  in  a  Static  Liberary,然后再添加#include  <afx.h  >就可以使用CString了。 

2:WCHAR  ch  =  L’中’;与CHAR  ch  =  ’中’;的区别是第一种使用UNICODE编码,第二种方式一般不经常用到,比如: 

                 WCHAR  strA  [  2  ]  =  {  L'中'  ,  0  }  ;//打开VC的Options菜单,选中Debug选项卡中的Display  unicode  strings后,可以看到strA的值。 
                 WCHAR  strB  [  2  ]  =  {  '中'  ,  0  }  ; 
                 CString  strC  ; 
                 strC+  =  (  (  char  *  )  strB  )  [  1  ]  ; 
                 strC+  =  (  (  char  *  )  strB  )  [  0  ]  ;//strC==”中” 

3:CString的AllocSysString  (  )成员函数;可以方便的把一个字符串转换成UNICODE形式。记得使用完该UNICODE字符串后要调用::SysFreeString()函数释放字符串。 

4:  CString::AllocSysString  (  )或者::SysAllocString得到的字符串并不是普通的UNICODE字符串,它之前的四个字节会存放申请的字符串的长度: 

                 CString  strD  =  ”asdf”; 
                 BSTR  strD  =  strC.AllocSysString(  )  ; 
                 long  i  =*  (  (  long  *  )  strD  –  1  )  ;  //  i  ==  8;一个UNICDE字符的长度是2字节,所以strD的长度为8个字节。 

4:UTF-8码转换为一般的字符串: 

#include    "Windows.h" 
int  main(void) 

char  str  [  256  ]  =  {(  char  )0xE4,  (  char  )  0xBD,  (  char  )  0xA0,  (  char  )  0xE5  ,(  char)0xA5  ,(char)0xBD,  (char)0x61,  (char)0x62  ,(char)0x63,(char)0}  ;    //一段UTF-8编码 
WCHAR*  strA; 
int  i=  MultiByteToWideChar  (  CP_UTF8  ,  0  ,(char*)  str  ,-1  ,NULL,0); 
strA  =  new  WCHAR[i]; 
MultiByteToWideChar  (  CP_UTF8  ,  0  ,(  char  *  )  str,  -1,  strA  ,  i); 
i=  WideCharToMultiByte(CP_ACP,0,strA,-1,NULL,0,NULL,NULL); 
char  *strB=new  char[i]; 
WideCharToMultiByte  (CP_ACP,0,strA,-1,strB,i,NULL,NULL); 
//strB即为所求 
delete  []strA; 
delete  []strB; 
return  0; 


5:在转换方面_bstr_t是最最灵活的,他提供了UNICODE到一般字符串的直接转换: 

#include    <comdef.h  > 
                 _bstr_t  strA; 
                 char  *strB=  "中国人  "; 
                 strA=strB; 
                 WCHAR  *strC=strA; 
                 long  i  =*  (  (  long  *  )  strC  -  1  )  ;//  i  亦是字符串的字节长度 
                 char  *strD=strA; 
                 return  0; 

6:如何得到当前应用程序路径?
char *str = new char[256];
::GetModuleFileName(NULL,str,MAX_PATH);
//str即为所求
delete []str;
str=NULL;

CfileDlg这个系统对话框我们经常遇到,但他提供的多形式灵活的参数,不是很多人了解,通过灵活的运用参数,可以让操作更灵活多变.

以下介绍CfileDlg的参数:

构造CFileDialogST对象。
可以传送最常用的参数(parameters)到参数列表(argument list)。

//参数:
//        [IN]    bOpenFileDialog
//                设置成TRUE构造一个打开文件对话框,FALSE为保存文件对话框。
//        [IN]    lpszDefExt
//                默认文件扩展名。
//                如果用户用户没有在文件名编辑框中输入文件扩展名,默认文件扩展名被自动附加到文件名上。如果设置为空,不附加任何扩展名。
//        [IN]    lpszFileName
//                在文件名编辑框中显示的初始文件名。如果为NULL,不显示任何初始文件名。
//        [IN]    dwFlags
//                用来定制对话框的一个或多个flags的组合。
//        [IN]    lpszFilter
//                一系列字符串对(pairs)用来设置文件过滤器,只有符合过滤条件的文件才能出现在文件列表框中。
//        [IN]    pParentWnd
//                指向文件对话框的父窗口,可以为NULL。
//
CFileDialogST(BOOL bOpenFileDialog, LPCTSTR lpszDefExt, LPCTSTR lpszFileName, DWORD dwFlags
CFileDialogST()
构造CFileDialogST对象。

必须自己初始化公有成员变量m_ofn and m_bOpenFileDialog后才能使用(这个对象)。

DoModal()
本函数显示文件对话框,允许用户进行选择。m_ofn结构需要使用的域必须已经填写(在构造函数中设置或直接存取该结构),而且公有变量m_bOpenFileDialog必须被设置(TRUE为打开文件对话框,FALSE为保存文件对话框)。

//返回值:
//        IDOK
//                The user has selected a filename.
//        IDCANCEL
//                The user has closed the dialog without selecting any filename.
//
int DoModal()

CString GetPathName() const
这个函数返回所选文件的全路径。

//返回值:
//                包含文件全路径的CString对象。
//
CString GetPathName() const

CString GetFileName() const
这个函数返回所选文件的文件名。

//返回值:
//                包含文件名的CString对象。
//
CString GetFileName() const

CString GetFileTitle() const
这个函数返回所选文件的标题。

//返回值:
//                包含文件标题的CString对象。
//
CString GetFileTitle() const

CString GetFileExt() const
这个函数返回所选文件的扩展名。

//返回值:
//                包含文件扩展名的CString对象。
//
CString GetFileExt() const

CString GetFileDir() const
这个函数返回所选文件的目录(不包含驱动器)。

//返回值:
//                包含文件目录(不包含驱动器)的CString对象。
//
CString GetFileDir() const

CString GetFileDrive() const
这个函数返回所选文件所在的驱动器。

//返回值:
//                包含文件所在的驱动器的CString对象。
//
CString GetFileDrive() const

POSITION GetStartPosition() const
这个函数返回文件列表的第一个文件位置。

//返回值:
//                一个用于迭代的POSITION。如果列表为空,返回NULL。
//
POSITION GetStartPosition() const

CString GetNextPathName(POSITION& pos) const
这个函数返回下一个所选文件的全路径。

//参数:
//        [IN]    pos
//                一个POSITION的引用,这个引用是上一次调用GetNextPathName或GetStartPosition返回的,如果到达列表结尾,返回NULL(用来控制迭代)。
//
//返回值:
//                包含文件全路径的CString对象。
//
CString GetNextPathName(POSITION& pos) const

int SelectFolder(LPCTSTR lpszTitle = NULL, LPCTSTR lpszStartPath = NULL, UINT ulFlags = BIF_RETURNFSANCESTORS | BIF_RETURNONLYFSDIRS, CWnd* pParentWnd = NULL)
这个函数允许用户选择一个文件夹。

//参数:
//        [IN]    lpszTitle
//                一个0结尾的字符串的地址,用来显示在树视图控件的上方,作为标题显示。可以为NULL。
//        [IN]    lpszStartPath
//                一个0结尾的字符串的地址,包含初始文件夹。 可以为NULL。
//        [IN]    ulFlags
//                用来配置对话框的Flags。
//        [IN]    pParentWnd
//                指向对话框的父窗口,可以为NULL。
//
//返回值:
//        IDOK
//                用户选择了一个文件夹,并按OK按钮。调用GetSelectedFolder()可以返回选中的文件夹。
//        IDCANCEL
//                用户没有选择任何文件夹,按CANCEL关闭窗口。
//
int SelectFolder(LPCTSTR lpszTitle, LPCTSTR lpszStartPath, UINT ulFlags, CWnd* pParentWnd)

CString GetSelectedFolder() const
这个函数返回用户(使用SelectFolder)选择的文件夹。

//返回值:
//                包含所选文件夹的CString对象。如果之前没有调用SelectFolder,这个值可能为空或者返回上一次选择的文件夹。
//
CString GetSelectedFolder() const

CreateFile
打开文件
要对文件进行读写等操作,首先必须获得文件句柄,通过该函数可以获得文件句柄,该函数是通向文件世界的大门。

ReadFile
从文件中读取字节信息。
在打开文件获得了文件句柄之后,则可以通过该函数读取数据。

WriteFile
向文件写入字节信息。
同样可以将文件句柄传给该函数,从而实现对文件数据的写入。

CloseHandle
关闭文件句柄。
打开门之后,自然要记得关上。

GetFileTime
获取文件时间。
有三个文件时间可供获取:创建时间、最后访问时间、最后写时间。
该函数同样需要文件句柄作为入口参数。

GetFileSize
获取文件大小。
由于文件大小可以高达上数G(1G需要30位),因此一个32位的双字节类型无法对其精确表达,因此返回码表示低32位,还有一个出口参数可以传出高32位。
该函数同样需要文件句柄作为入口参数。

GetFileAttributes
获取文件属性。
可以获取文件的存档、只读、系统、隐藏等属性。
该函数只需一个文件路径作为参数。

SetFileAttributes
设置文件属性。
能获取,自然也应该能设置。
可以设置文件的存档、只读、系统、隐藏等属性。
该函数只需一个文件路径作为参数。

GetFileInformationByHandle
获取所有文件信息
该函数能够获取上面所有函数所能够获取的信息,如大小、属性等,同时还包括一些其他地方无法获取的信息,比如:文件卷标、索引和链接信息。
该函数需要文件句柄作为入口参数。

GetFullPathName
获取文件路径
该函数获取文件的完整路径名。
需要提醒的是:只有当该文件在当前目录下,结果才正确。
如果要得到真正的路径。应该用GetModuleFileName函数。

CopyFile
复制文件
注意:只能复制文件,而不能复制目录

MoveFileEx
移动文件
既可以移动文件,也可以移动目录,但不能跨越盘符。(Window2000下设置移动标志可以实现跨越盘符操作)

DeleteFile
删除文件

GetTempPath
获取Windows临时目录路径

GetTempFileName
在Windows临时目录路径下创建一个唯一的临时文件

SetFilePoint
移动文件指针。
该函数用于对文件进行高级读写操作时。

LockFile
UnlockFile
LockFileEx
UnlockFileEx
以上四个函数用于对文件进行锁定和解锁。这样可以实现文件的异步操作。可同时对文件的不同部分进行各自的操作。

LZOpenFile
打开压缩文件以读取

LZSeek
查找压缩文件中的一个位置

LZRead
读一个压缩文件

LZClose
关闭一个压缩文件

LZCopy
复制压缩文件并在处理过程中展开

GetExpandedName
从压缩文件中返回文件名称。

以上六个函数为32位API中的一个小扩展库,文件压缩扩展库中的函数。文件压缩可以用命令compress创建。

32位API提供一个称为文件映像的特性,它允许将文件直接映射为一个应用的虚拟内存空间,这一技术可用于简化和加速文件访问。

CreateFileMapping
创建和命名映射

MapViewOfFile
把文件映射装载如内存

UnmapViewOfFile
释放视图并把变化写回文件

FlushViewOfFile
将视图的变化刷新写入磁盘
文件读写:

.
CStdioFile file("c:\\test.txt", CStdioFile::modeCreate|CStdioFile::modeWrite);
file.Close();

2.
CStdioFile file("c:\\test.txt", CStdioFile::modeReadWrite);
file.SeekToEnd();
//...
file.Close();



FILE* fp;
fp=fopen("d:\\111.txt","wb");
fseek(fp,0,SEEK_END);//移动到末尾
。。。。。。。。。。。。。。。。
。。。。。。。。。。。。。。。。。。
fclose(fp);

1.
CFile file("c:\\test.txt", CFile::modeCreate|CFile::modeWrite);
file.Close();

2.
CFile file("c:\\test.txt", CFile::modeReadWrite);
file.SeekToEnd();
//...
file.Close();

SaveLog(char* p)
{

  FILE *fd = fopen("c:\\zhang\\xi\\Log.txt","a+");
  if (fd == NULL)
  return ;
  fwrite(p,sizeof(char),strlen(p),fd);
  fwrite("\x0d\x0a",sizeof(char),2,fd);
  fclose(fd);
  return;
}
11:我应该怎样防止MFC在窗口标题栏上添加文档名?
     在PreCreateWindow函数中删除FWS_ADDTOTITLE标志的窗口样式:
cs.style &= ~FWS_ADDTOTITLE ;
cs.style&=~WS_MAXIMIZEBOX;删除最大化图标。


12:我应该怎样防止MFC在窗口标题栏上把文档名预置成应用程序名?
     在PreCreateWindow函数中删除FWS_PREFIXTITLE标志的窗口样式: 
     cs.style &= ~FWS_PREFIXTITLE; 
10:如果想把整个项目拷贝到软盘,那些文件可以删掉?
    除了项目文件夹中debug文件夹可以删除外,.ncb,.clw,.opt 等文件也可以删除,这些文件rebuilt all后可以重新生成。


cs.dwExStyle &= ~WS_EX_CLIENTEDGE;

/*
创建自定义消息程序步骤:
1:用ClassWard创建一个新project命名为:MessageTest
2:此project选单文档,取消Active X支持,取消打印支持,其它默认。finish.
3:在菜单中添加一个菜单“测试”其子菜单为"test",并映射其执行方法:OnTest
4: 制造消息发送者:在资源中新增一个对话框,并用classward为其创建类名为
   TestDialog.然后在此对话框中加入一个Button,并用classward为其映射执行函数
   OnButton1()。
5:在TestDialog.h文件中加入一行#define WM_MY_MESSAGE (WM_USER+100)
   用来定义自己的消息
6:在TestDialog.cpp文件中加入一行:#include "MainFrm.h"
7:为对话框的Button按钮加入发送消息代码如下:
void TestDialog::OnButton1()
{
// TODO: Add your control notification handler code here
//获取当前框架指针
CMainFrame *pMainFrame=(CMainFrame *)AfxGetApp()->m_pMainWnd;
//获取当前view指针
CView *pView=pMainFrame->GetActiveView();
if(pView!=NULL)
{
pView->PostMessage(WM_MY_MESSAGE,0,0);
}
}
以上是对于消息的发送者工作己完成
8:以下是对于消息接收者
在MessageTestView.h中也要定义:#define WM_MY_MESSAGE (WM_USER+100)
9:并在MessageTestView.h中定义消息映射函数如下:
protected:
//{{AFX_MSG(CMessageTestView)
afx_msg void OnTest();
afx_msg LRESULT OnMyMessage(WPARAM wParam, LPARAM lParam);   //此处为自定义消息映射函数
//}}AFX_MSG
DECLARE_MESSAGE_MAP()
10:在MessageTestView.cpp文件中,声明消息响应函数如下:
BEGIN_MESSAGE_MAP(CMessageTestView, CView)
//{{AFX_MSG_MAP(CMessageTestView)
ON_MESSAGE(WM_MY_MESSAGE,OnMyMessage)  //自定义消息响应函数
ON_COMMAND(ID_TEST, OnTest)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
11:在MessageTestView.cpp文件中实现消息响应函数如下:
LRESULT CMessageTestView::OnMyMessage(WPARAM wParam,LPARAM lParam)
{
MessageBox("OnMyMessage!Receiver");
return 0;
}
12:将消息发送者与消息响应者联系起来。即:将对话框与菜单联系起来。
实现CMessageTestView的OnTest方法如下:
void CMessageTestView::OnTest()
{
// TODO: Add your command handler code here
TestDialog dlg;
dlg.DoModal();
}
13:以上完成了所有自定义消息过程
*/
////////////////////////////////////////////////////////////////
2:如何编写一个程序在运行后马上删除自己。
/* 以下代码在VC.net2002、BCB6.0、DevC++4.98环境下均编译通过,并正常运行 */
/* Creamdog保留所有权力 */
#include <windows.h>
int main( int argc, char *argv[] )
{
HMODULE module = GetModuleHandle(0);
CHAR buf[MAX_PATH];
GetModuleFileName(module, buf, sizeof buf);
CloseHandle(HANDLE(4));
__asm
{
lea eax, buf;
push 0;
push 0;
push eax;
push ExitProcess;
push module;
push DeleteFile;
push UnmapViewOfFile;
ret;
}
return 0;
}
一. 奇怪的宏定义
  (1)  #define for if(0); else for 
按照c++标准,for中定义的变量的作用域应该只在for循环中有效,而VC却不行,比如这样定义是不对的
for(int i=0;i<90;i++)
{
...;
}

for(int i=0;i<90;i++)  //重复定义i变量
{
...;
}

如果加上标题的那句,那么就可以了,就是让i作用域局限在else中.

(2)宏定义怪圈
#define  wait_event(wq,condition)  \ 
do{  \ 
if(condition)  \ 
                           break;  \ 
             __wait_event(wq,condition);  \ 
}while(0) 


明明这句话只执行一次,为什么还还用do-while语句呢?

假设有这样一个宏定义 
#define  macro(condition)  \ 
if(condition)  dosomething(); 
现在在程序中这样使用这个宏: 
if(temp) 
             macro(i); 
else 
             doanotherthing(); 
一切看起来很正常,但是仔细想想。这个宏会展开成: 
if(temp) 
             if(condition)  dosomething(); 
else   
             doanotherthing(); 
这时的else不是与第一个if语句匹配,而是错误的与第二个if语句进行了匹配,编译通过了,但是运行的结果一定是错误的。 
为了避免这个错误,我们使用do{….}while(0)  把它包裹起来,成为一个独立的语法单元,从而不会与上下文发生混淆。
同时因为绝大多数的编译器都能够识别do{…}while(0)这种无用的循环并进行优化,所以使用这种方法也不会导致程序的性能降低。

(3) #if(0)
........
#endif

这样是为了解释掉某段程序,而不影响其中的/*...*/的作用,便于调试

而/*.....*/是不能嵌套的,编译会出错.

(4)用下面语句实现数据转换。 i=(int)(f*100);
当f=11.40(米)时,i=1139(厘米);
当f=11.41(米)时,i=1140(厘米);
当f=12.32(米)时,i=1231(厘米);
当f=12.33(米)时,i=1232(厘米);
等等,很多数据的转换存在着&ldquo;1误差&rdquo;。
不过,大部分数据的转换是没有误差的,
如当f=11.39(米)时,i=1139(厘米);
当f=12.31(米)时,i=1231(厘米)。
如果改用以下方法实现数据转换,
&ldquo;1误差&rdquo;一样存在。
float ftemp;
ftemp=f*100;
i=(int)ftemp;
这里,ftemp是一局部变量(函数内定义)
或全局变量(函数外定义)。把f*100改成f*100.0,
&ldquo;1误差&rdquo;也存在。但是如果把ftemp
改成为类的属性变量(在类里定义),


(5)a[i]和i[a]
在程序里本应该用a[i],但i[a]竟然和a[i]输出的结果一样。为什么。今天把问题整理如下:
i[a]是标准语法。“[]”称为下标运算符,其语法为:
postfix_expression [ expression ]
其中“postfix_expression”和“expression”之中必须有一个是指针类型(或数组),而另一个是整型。
例如下面的程序是完全合法的:
int a[]={0,1,2,3,4};
printf("%d\n",3[a]);
下标运算符参与的表达式在求解时仅仅是做一个变换而已,将“postfix_expression [ expression ]”
改写为“ * ( postfix_expression + expression ) ”,因此a[3]和3[a]分别改写为*(a+3)和*(3+a),
可见二者是完全等价的。但注意不要用i[a]这种形式,因为它不符合日常习惯。
实验代码:
#include "stdafx.h"
#include "iostream.h"
int f();
int main(int argc, char* argv[])
{

       int a[20]={1,2,3,4,5,6,7,8,9};
       cout<<a[f()]<<endl;
       cout<<f()[a]<<endl;
       return 0;
}

int f()
{
return 4;
}

实验结果:
4
4
Press any key to continue

(6)int x;x;

这儿是个关于宏的问题,我曾用过ATL的串转换宏,包括W2A,开始有些东西我还不太明白。为了使用这些宏,必须在函数的开始处用USES_CONVERSION来初始化某些局部变量。用就用吧,但是看看这个宏的定义,它有类似下面的代码:

// 在atlconv.h文件中
#define USES_CONVERSION \
int _convert; _convert; \
UINT _acp = GetACP(); _acp; \
LPCWSTR _lpw; _lpw; \
LPCSTR _lpa; _lpa

为什么它们用“int x;x;”——这种后面跟着变量的声明?

    很多人都碰到过这个令人困惑的问题,后来发现简单的答案是:禁止编译器的警告信息(warning)。如果单独有一行代码:
int x;
且从来没有使用过x,那么编译器汇报错“unreferenced local variable:x”,意思是未引用过的局部变量x,如果将警告信息的输出
调到最大。为了避免讨厌的警告,USES_CONVERSION引用声明的变量。

int x; // 声明
x; // 使用这个变量

在C++之前的时代,程序员有时在C中用函数形参做同样的事情来避免“unreferenced formal parameter”或其它的深奥费解的编译错误。

void MyFunc(int x, char y)
{
x;
y;

}

当然,现在用下面的代码可以更有效地完成同样的事情:

// 参数 x 不是用
void MyFunc(int /* x */)
{

}

posted on 2006-07-14 15:39 黑色幽灵 阅读(3227) 评论(0)  编辑 收藏 引用 所属分类: VC操作

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理