love in C++, live on MFC

to get ready...

C++博客 首页 新随笔 联系 聚合 管理
  47 Posts :: 0 Stories :: 97 Comments :: 0 Trackbacks

#

关于printf的大部分参数祥解 (http://blog.vckbase.com/flyingleaf/archive/2004/12/27/2335.aspx)文章后面的评论中,看到了到目前为止我看到过的最全的printf参数意义表.
以前一直在msdn里查,都没有这么详细的.
其实它是gnu c的一个文档,Formatted Output (http://www.gnu.org/software/libc/manual/html_node/Formatted-Output.html#Formatted-Output)
posted @ 2006-06-16 14:05 flyingxu 阅读(3997) | 评论 (0)编辑 收藏

情况是这样的,一个程序从ansi移植到了unicode,见把代码移植成UNICODEhttp://www.cppblog.com/flyingxu/archive/2006/05/18/7356.html),之后产生了一个问题,之前文件保存的那些文件,unicode版本还能打开吗?这真是个大问题,如果不能,就基本表示这个程序已经半死不活了。

文件的保存基本用的序列化,比如
void CSDITestDoc::Serialize(CArchive& ar)
{
    
if
 (ar.IsStoring())
    {
        
// TODO: add storing code here

        ar << m_strName;
    }
    
else

    {
        
// TODO: add loading code here
        ar >> m_strName;
    }
}
如果m_strName = _T("name");那么ansi情况下,保存的文件为:
04 6E 61 6D 65,一共5个字节。
然后用unicode版本保存时,是
FF FE FF 04 6E 00 61 00 6D 00 65 00,一共12个字节。

然后我发现,ansi版本的可以打开unicode版本的文件,unicode版本的也可以打开ansi版本的文件。

为什么?我觉得关键在于CString,我觉得CString功能比较强大。
CString的序列化函数
// CString serialization code
// String format:
//      UNICODE strings are always prefixed by 0xff, 0xfffe
//      if < 0xff chars: len:BYTE, TCHAR chars
//      if >= 0xff characters: 0xff, len:WORD, TCHAR chars
//      if >= 0xfffe characters: 0xff, 0xffff, len:DWORD, TCHARs

CArchive
& AFXAPI operator<<(CArchive& ar, const CString& string)
{
    
// special signature to recognize unicode strings
#ifdef _UNICODE
    ar 
<< (BYTE)0xff;
    ar 
<< (WORD)0xfffe;
#endif

    
if (string.GetData()->nDataLength < 255)
    {
        ar 
<< (BYTE)string.GetData()->nDataLength;
    }
    
else if (string.GetData()->nDataLength < 0xfffe)
    {
        ar 
<< (BYTE)0xff;
        ar 
<< (WORD)string.GetData()->nDataLength;
    }
    
else
    {
        ar 
<< (BYTE)0xff;
        ar 
<< (WORD)0xffff;
        ar 
<< (DWORD)string.GetData()->nDataLength;
    }
    ar.Write(
string.m_pchData, string.GetData()->nDataLength*sizeof(TCHAR));
    
return ar;
}
它对unicode做了特别的支持
特别是当它在读序列化过程的时候
CArchive& AFXAPI operator>>(CArchive& ar, CString& string)
{
#ifdef _UNICODE
    
int nConvert = 1;   // if we get ANSI, convert

#else
    
int nConvert = 0;   // if we get UNICODE, convert
#endif

    UINT nNewLen 
= _AfxReadStringLength(ar);
    
if (nNewLen == (UINT)-1
)
    {
        nConvert 
= 1 -
 nConvert;
        nNewLen 
=
 _AfxReadStringLength(ar);
        ASSERT(nNewLen 
!= -1
);
    }

    
// set length of string to new length

    UINT nByteLen = nNewLen;
#ifdef _UNICODE
    
string.GetBufferSetLength((int
)nNewLen);
    nByteLen 
+= nByteLen * (1 - nConvert);  // bytes to read

#else
    nByteLen 
+= nByteLen * nConvert;    // bytes to read
    if (nNewLen == 0)
        
string.GetBufferSetLength(0
);
    
else

        
string.GetBufferSetLength((int)nByteLen+nConvert);
#endif


    
// read in the characters
    if (nNewLen != 0)
    {
        ASSERT(nByteLen 
!= 0
);

        
// read new data

        if (ar.Read(string.m_pchData, nByteLen) != nByteLen)
            AfxThrowArchiveException(CArchiveException::endOfFile);

        
// convert the data if as necessary

        if (nConvert != 0)
        {
#ifdef _UNICODE
            CStringData
* pOldData = string
.GetData();
            LPSTR lpsz 
= (LPSTR)string
.m_pchData;
#else

            CStringData
* pOldData = string.GetData();
            LPWSTR lpsz 
= (LPWSTR)string
.m_pchData;
#endif

            lpsz[nNewLen] 
= '\0';    // must be NUL terminated
            string.Init();   // don't delete the old data
            string = lpsz;   // convert with operator=(LPWCSTR)
            CString::FreeData(pOldData);
        }
    }
    
return
 ar;
}
也就是说,不管你保存的文件是不是unicode,都可以读进来,转换称当前unicode或者ansi版本

不过回到文章的标题,这里只讲了用CString序列化的保存。如果是其他保存方式,可以学着CString的做法,写函数把文件总是能读进来。
posted @ 2006-06-13 23:53 flyingxu 阅读(1245) | 评论 (0)编辑 收藏

MFC自己定义了一些字符串,在用户自己定义字符串的时候,id不能重复。这个时候,用户自定义id的范围是什么?
今天同事问我。

我还真不知道,但是我看了下AFXRES.RC 和AFXRES.H ,发现MFC用的ID都是0xE000以上的。所以,用户自己用的,从0->0xCFFFF?不过不确定。呵呵,查了一下,正确的可以见TN020:http://msdn2.microsoft.com/en-us/library/t2zechd4.aspx
By convention, the ID value of 0 is not used.

Windows implementation limitations restrict 
true resource IDs to be less than or equal to 0x7FFF.

MFC
's internal framework implementations reserve several ranges: 0xE000->0xEFFF and 0x7000->0x7FFF.

Several Windows system commands use the range of 
0xF000 -> 0xFFFF.

Control IDs of 
1->7 are reserved by IDOK, IDCANCEL, and so on.

The range of 
0x8000->0xFFFF for strings is reserved for menu prompts for commands.


但是在我的印象中,重复的字符串id在编译的时候,就会出错的。但是http://support.microsoft.com/kb/q194299/上说,实际上,选static link mfc才会在编译的时候发现,否则发现不了。

另外:http://www.vckbase.com/article/misc/0022.htm中说,在一个rc文件中,可以同时有几种语言的资源,呵呵,我发现我傻了,每次把不同语言的资源发在不同的rc中,搞得现在好几个rc文件,每次拷贝切换烦的要死
posted @ 2006-06-13 00:25 flyingxu 阅读(1473) | 评论 (0)编辑 收藏

有两个工程,w.exe和c.dll;开始的时候,如果exe是release版本用debug版本的dll时,函数调用会出错,我觉得也可以理解,没有多想.
今天遇到一个问题更奇怪,都是debug版本,但是exe和dll的optimizations设置不一样,都会出莫名的错误,出错行更搞笑,是
m_ComboBox.AddString(_T("") )
怎么查也查不出问题,把优化选项改成一致问题就没有了,搞不懂
posted @ 2006-06-02 10:32 flyingxu 阅读(464) | 评论 (0)编辑 收藏

http://www.developingprogrammers.com/


"About DevelopingProgrammers  

Upgrade Yourself…

Developing Programmers is an independent site for those who find themselves making the transition from being able to write programs to being a professional programmer. Professional programmers are expected to do a lot more than just writing programs that work. Typical expectations include:

  • Writing stable, maintainable code
  • Working in groups (this site focuses mostly on small groups)
  • Communicating through Code (because even the newest person in the company should be able to follow what you’ve done)
  • Produce automated tests for code
  • Back-port bug fixes to previous versions of a program
  • And more!

This site aims to introduce and discuss the skills a professional programmer needs. It aims to develop programmers."

posted @ 2006-06-01 10:30 flyingxu 阅读(259) | 评论 (0)编辑 收藏

编译好了,运行一个程序,试图打开一个对话框时,对话框出不来,反倒是出来一个错误提示:
"An unsupported operation was attempted"
搜了一下:
http://www.experts-exchange.com/Programming/Programming_Languages/MFC/Q_21023191.html
里面有回答,的确是那样的,是resource id的问题.

错误原因:
开始,给一个radio映射了一个变量:
 DDX_Radio(pDX, IDC_RADIO1, m_nFindType);
后来发现IDC_RADIO1这个id不好,改为 DDX_Radio(pDX, IDC_RADIO_BIT, m_nFindType);
编译当然不会出错,运行时,就出现异常了.如果在上面的代码上设个断点,然后跟踪进去,看的非常清楚.

posted @ 2006-05-31 17:27 flyingxu 阅读(10431) | 评论 (1)编辑 收藏

在重载CSplitterWnd中,重载了OnPaint(),
默认的代码如下:
void CHideSplitterWnd::OnPaint() 
{
    CPaintDC dc(
this); //
 device context for painting
    
//
 TODO: Add your message handler code here
    
// Do not call CSplitterWnd::OnPaint() for painting messages

}
wizard添加的代码居然有这样一行:// Do not call CSplitterWnd::OnPaint() for painting messages,后面有个猜测。
然后我继续写我的函数:
void CHideSplitterWnd::OnPaint() 
{
    CPaintDC dc(
this); //
 device context for painting
    
    
// 调用基类,先

    CSplitterWnd::OnPaint();
    
     //
利用dc画一些别的东西,但是实际上,它们永远不会被画出来
   //除非不调用基类的OnPaint()

    m_rectButton.DrawButton(&dc);
}
结果我添加的东西怎么也不会被画出来,为什么会这样?

看了CSplitterWnd::OnPaint()的代码,发现它也用了一个CPaintDC,而且也是个临时变量
void CSplitterWnd::OnPaint()
{
    ASSERT_VALID(
this
);
    CPaintDC dc(
this);
而关键的地方就在CPaintDC的ctor和dtor中了:在CPaintDC的ctor中调用了
::BeginPaint(m_hWnd = pWnd->m_hWnd, &m_ps)
而在CPaintDC的dtor中调用了
::EndPaint(m_hWnd, &m_ps);
而:BeginPaint是开始根据当前的cliprect来画,EndPaint则会清空当前的cliprect。

所以,前面调用基类的OnPaint的过程结束后,当前的cliprect为NULL,所以,第二个CPaintDC在视图画点什么时,cliprect已经为NULL,当然什么也画不上去了 :)

如果我一定想再用CPaintDC画点什么,怎么办?再次调用InvalidateRect,使得cliprect不为空。
void CHideSplitterWnd::OnPaint() 
{
    //注意临时变量声明的顺序,因为BeginPaint/EndPaint是不支持嵌套的
    //CPaintDC dc(
this); //
 device context for painting
    
    
// TODO: Add your message handler code here

    CSplitterWnd::OnPaint();
    
    //第二此调用
    InvalidateRect(
&
m_rectButton,FALSE);
    CPaintDC dc(
this); // 在这里,device context for painting

    m_rectButton.DrawButton(&dc);
    
    
// Do not call CSplitterWnd::OnPaint() for painting messages

}
所以,我猜测,所有用了CPaintDC的地方,MFC都会加一句:不要调用基类的函数啦~

呵呵,不知道我讲清楚没有
posted @ 2006-05-22 23:39 flyingxu 阅读(3300) | 评论 (3)编辑 收藏

动机 : 在没有装中文字体的机器上显示中文

如果不是unicode,某些中文字符会在不同的codepage上出现乱码;如果当前codepage为韩文时,某些中文字会变成韩文的样子,奇怪.

Step1: define _UNICODE, undefine _MBCS

宏定义UNICODE, _UNICODE定义两个也可以,定义一个也可以,windows的头文件会保证两个都会被定义.

#ifdef _UNICODE
#ifndef UNICODE
#define  UNICODE  //  UNICODE is used by Windows headers

#endif
#endif

 
#ifdef UNICODE
#ifndef _UNICODE
#define  _UNICODE         //  _UNICODE is used by C-runtime/MFC headers
#endif
#endif

 

Step2: 更改变量类型

比如 char -> TCHAR

但是 ,并不是说所有的char类型都不能用了.如果需要用,并且函数的参数/返回值什么的都不冲突,char类型还是可以用的.

Step3: 更改常量字符串形式

" abc " -> _T(" abc ")

因为开始写程序时没有注意 ,造成这样的常量字符串形式非常多,一个个改起来很烦,我写了个小插件,可以讲 abc 选中后点一下 button就能变成_T( abc ),有点用,代码也非常的短.

Step4: 替换某些函数

比如atoi -> _ttoi

在这里遇到一个问题,就是atof的替换.

按照msdn, atof应该被_ttof替换

TCHAR.H routine

_UNICODE & _MBCS not defined

_MBCS defined

_UNICODE defined

_tstof

atof

atof

_wtof

但是奇怪的是,VC6里面不认识_tstof这个函数,VC.net 2003就可以.

有两个解决的办法:

1)      因为atof只是替换为浮点数,所以这里用WideCharToMultiByte转化一下之后再调用atof也是可行的

2)      呵呵,strtod的替换函数_tcstod,哈哈,当然参数要变一下,我觉得这个方法不错,不要被msdn限制死了

Step5: 检查一下调用其他 dll 的接口函数

因为项目还用了另外一个dll,其中有些接口需要char*参数,这个时候就必须用WideCharToMultiByte转换一下了.

VC里面的设置:
1) 等把所有的编译error改完了之后一编译,除了个link错误,不过msdn有解释:
If you are using Unicode and MFC, you will get an unresolved external on _WinMain@16 if you don’t create an entrypoint to wWinMainCRTStartup. Use the /ENTRY option or type this value in the Project Settings dialog box. (To find this option in the development environment, click Settings on the Project menu, then click the Link tab, and click Output in the Category box.)

2)终于编译成功了,调试的时候却发现CString的值不能在QuickWatch窗口中显示了
tools->option->
in the debug tab u can find a check box Labeled "Display unicode strings"
Check it.

问题 : 直接用 ANSI dll 中的 CString

在使用dll的时候,需要直接使用dll中的一个变量,CString类型,因为这个dll还没有改为unicode,所以在直接使用其变量时有点问题,使得值不对.(是不是这个原因还没有完全验证,应该是的)

到这里 , 宣布移植失败 !

/*补充:
结果是失败,但是基本移植代码的所有步骤都有了。而且,也指出了如果需要调用的dll不是unicode,每次调用/传递参数时,都要转换,很烦。
后来我把dll的代码也移植了下,现在运行的很好,韩文、中文都可以显示,不管你当前语言是什么。
*/

还有一篇讲移植步骤的,更全面,适合更复杂的情况:
Cheat Sheet:Unicode-enabling Microsoft C/C++ Source Code (http://www.i18nguy.com/unicode/c-unicode.html)

 

posted @ 2006-05-18 16:09 flyingxu 阅读(3062) | 评论 (6)编辑 收藏

在写函数的时候,很多函数只是设置某个变量然后做一些其他的相应的处理,比如:
void CInstruction::SetName(const CString& strName)
{
    m_strName 
=
 strName;    

            //some other operations
    if(!IsRed())
    {
        //...        
    }
}
这样的函数,返回类型为void顺理成章.

但是,随着程序继续往下写,需要处理的情况越来越复杂,这个时候,发现SetName这个函数,返回void已经不够用了,因为可以需要知道SetName这个函数调用有没有成功,因为可能传入的strName不一定是合法的,如果调用SetName函数没有成功,可能要继续调用别的函数,这个时候,可能就要讲返回类型改为BOOL.

而后来继续想,一个函数的返回值,应该更加充分的利用,调用一个函数时,就该充分的利用它的返回值;而在写一个函数时,也应该注意到这一点.如果实在没有什么值好返回,就返回TRUE

所以,对于函数,如果在设计时准备把返回类型定为void,其实可以改为BOOL
1)最初可能没有用,但情况会越来越复杂
2)充分利用函数的返回值
posted @ 2006-05-17 16:12 flyingxu 阅读(2959) | 评论 (10)编辑 收藏

资源中的属性,是指写MFC程序时VC IDE的resource编辑器中可以设定的各种属性,比如WS_EX_TRANSPARENT或者LVS_REPORT.
而这些属性,都是可以用代码在程序初始化后加上去的,比如在OnInitDialog() 或者OnCreate()函数中.

从方便性来说,肯定是在资源中设置这些属性更加方便,但是..
我遇到一种情况,在我的程序中,有两个rc文件,分别是中文和英文,这个时候,就发生了在英文资源中设置了属性修正了一个bug但是在中文版的资源中忘记设置,bug依然存在.而如果是在在代码中加上类似的代码:
m_CrossRefTab.ModifyStyleEx(0,WS_EX_TRANSPARENT);
资源文件不用改变,而且修正了所有的问题.

所以,我觉得,应该尽量用几行代码来替代在资源中的设定属性
posted @ 2006-05-17 14:17 flyingxu 阅读(440) | 评论 (0)编辑 收藏

仅列出标题
共5页: 1 2 3 4 5