VC FAQ

    技术2022-05-12  15

    1、  编译和链接问题

    1.1    问:尝试使用 C++ 标准库中的类 ( 例如 cout cin 或者 string) ,编译器却给出 C2065 提示说标示符未定义 (undeclared identifier)

    答:这个由于标准库中的类或者变量等的定义都是在 std 命名空间。需要我们在使用标准库中的标识前面加上 std:: 前缀。当然,为了简单,可以在文件头引入命令空间 use namespace std

    1.2    问:怎么知道我的代码是否正在使用 iostream 库还是标准库呢?

    答: iostream 库头文件是 iostream.h ,带有 .h 扩展名的,而标准 stl 库头文件是没有扩展名。

    1.3    问:我试着调用一个 Windows API ,但是编译器给出 C2065 找不到标示符的错误。

    答:一般这是由于调用了某个 Window API 但是没有包含 API 对应的头文件。我们调用一个 API 函数,需要包含申明此函数的头文件。例如我们调用了 SHBrowseForFolder 这个 shell 函数,我们需要包含头文件 sheobj.h 。另外,在某些时候我们包含了头文件,但是还是有这个提示,那么很有可能是 windows 版本或者控件版本问题,通常,对于通用控件来说出现这个的比较多。第一个是我们的 SDK 太老,如果我们用的是 VC6SDK ,有些东西是不支持的,解决办法是安装 VC789 或者更新 PSDK 。对于 VC 程序来说,向导生成的 stdafx.h 预编译头文件中会定义几个和版本相关的宏:

    WINNVER: Windows 版本(应用于 9x/me/NT

    _WIN32_WINDOWS: Windows 9x/me 版本

    _WIN32_WINNT: Windows NT 版本

    _WIN32_IE: 通用控件版本

    有些 API( 特别是有些通用控件 ) 需要最新版本的支持,我们需要修改这些宏定义为合适的值,保证调用的正确性。实际上你看看 commctrl.h 文件就会发现,很多结构的定义都是有编译条件的,在高版本才有,低版本是不会编译到的。

    1.4    问:我尝试调用一个 Windows API ,但是连接起给出 LNK2001 无法解析函数符号的错误。

    答:调用 Windows API ,除了需要包含函数申明的头文件外,还需要告诉链接器在什么地方找到这个函数的实现,也就是说需要将此 API 实现的库链接进去。这些是通过 lib( 导入库 ) 。导入库是一个 lib 文件包含了对应库 dll 的所有导出函数列表信息,通过这个可以保证程序在运行的时候正确的加载所需的依赖库,并且找到调用函数的实现,跳转到对应函数代码处执行。把对应库加入到链接包含文件中的方法有两种,第一:在工程属性页中选中链接页进行设置;第二个是使用 #pragma comment(lib, “usedLibName”)

    1.5    问:在编译 ATL 工程的时候,提示 Lnk2001 main 函数找不到符号

    答:这是因为你在 ATL 工程中调用了 CRuntime 函数。 ATL 项目为了尽可能的缩减工程的体积,默认是不会链接 CRT 库。如果你调用了 CRT 函数,那你就得把 CRT 库链接进去。实际上你只需要去掉 _ATL_MIN_CRT 宏就可以关闭这个优化了。

    1.6    问:提示链接错误 Lnk2001 :找不到 _beginthreadex|_endthreadex 符号

    答:通常这发生在使用 MFC 程序,而我们使用的运行库为单线程的 CRT 。由于 MFC 程序使用线程,它需要多线程 CRT ,这个看看 msdn 关于 _beginthread API 就知道了,明确说了此 API 需要多线程 CRT 库才行。大水

    2、  Window UI 相关问题

    2.1 问:怎么保存和载入 Jpgpng 等格式的图片?

        答:使用 gdi+ 或者第三方的图片库,例如 paintlib ImageMagick 或者 ImageLibrary

    2.2 问:怎么改变对话框的背景颜色,或者在对话框背景中绘制一个图片?

        答:修改对话框的背景颜色可以调用 CWinAppSetDialogBkColor, 不过不建议使用此函数。 Msdn 明确说了,修改对话框的背景颜色,处理 WM_CTRLCOLOR 消息。另外处理对话框的背景颜色或者在对话框背景中绘制图片,可以处理 WM_ERASEBKGND 。对于对话框来说,不要处理 WM_PAINT 消息。

    2.3 问:我有一个对话框要进行大量复杂的处理,但是我想有一个取消按钮,那样用户可以在执行复杂运算过程中取消操作,怎么处理这个取消按钮呢?

        答:实际上这个不是按钮的问题,而是在执行过程中按钮点击事件如何得到响应的问题。你在单线程程序中进行复杂运算,对话框消息得不到处理。解决这个问题有两个办法:多线程或者在执行复杂运算过程中定期 pump 对话框消息。对于方法一的多线程,可以将复杂运算放在工作线程中执行。对于方法二, MFC 程序可以如下处理:

    Void ProcessMsg()

    {

       CWinApp *pApp = AfxGetApp();

       MSG msg;

       While( PeekMessage(&msg, NULL, 0,  0, PM_NOREMOVE))

       pApp->PumpMessage();

    }

    你只需要在执行复杂运算过程中定期执行以下 ProcessMsg() 函数即可。

    2.4 问:如果改变我自己窗口中的鼠标光标?

        答:很简单,处理 WM_SETCURSOR 消息即可。在消息中调用 SetCursor() 来设置你想要的光标。由于 WM_SETCURSOR 在你鼠标移动的时候都会调用,因此尽量不要在此函数处理中进行复杂的调用,比如进行文件或者其他的 IO 操作等。

    2.5 问:如果设置一个窗口在其它窗口的最上方?

         答:利用 API SetWindowPos 即可。例如

    // MFC

    yourWindows.SetWindowPos(&wndTopMost, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE)

    // API

    SetWindowPos(yourWindows, HWND_TOPMOST, 0,0,0,0, SWP_NOMOVE|SWP_NOSIZE);

    相反,如果想取消置顶显示,设置 wndNoTopMost HWND_NOTOPMOST 即可

    2.6 问:如果将一个 report 模式的 listView 整行高亮选中?

        答:只需要设置全行选中扩展属性即可 LVS_EX_FULLROWSELECT

            yourList.SetExtendStyle(LVS_EX_FULLROWSELECT);

    2.7 问:如何修改静态控件的背景颜色

        答:对于 MFC 程序来说,在父窗口中处理 WM_CTRLCOLOR 。对于静态控件类型,调用 DCSetBkColor 即可。对于 Win32 程序来说,在父窗口中处理 WM_CTRLCOLORSTATIC 消息。

    HBRUSH CYourDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)

    {

    HBRUSH hbr = CDialog::OnCtlColor(pDC, pWnd, nCtlColor);   

    if ( pWnd->GetSafeHwnd() == GetDlgItem(IDC_LABEL1)->GetSafeHwnd() &&

             CTLCOLOR_STATIC == nCtlColor )

    {

        m_bkbrush.CreateSolidBrush ( RGB(255,0,0) ); 

        pDC->SetBkMode ( TRANSPARENT );

        return m_bkbrush;

     }

     

        return hbr;

    }

     

    // Win32 API:

     

    LRESULT CALLBACK YourDlgProc(HWND hDlg, UINT message,

                                 WPARAM wParam, LPARAM lParam)

    {

    static HBRUSH hbrBkcolor: 

        switch (message)

            {

            case WM_INITDIALOG:

                hbrBkcolor = CreateSolidBrush ( RGB(255,0,0) );

                return TRUE;

            break;

     

            case WM_CTLCOLORSTATIC:

                {

                HDC hdc = (HDC) wParam;

                HWND hwndStatic = (HWND) lParam; 

                if ( hwndStatic == GetDlgItem ( hDlg, IDC_LABEL1 ))

                    {

                    SetBkMode ( hdc, TRANSPARENT );

                    return (LRESULT) hbrBkcolor;

                    }

                }

            break;

            // ...

            }

        return FALSE;

    }

    2.8 问:如何编程选择 listView 控件中的某一个 item

        答:设置行的选中状态即可。我们知道 ListViewitem 结构中有 statestateMask 两个字段。其中 state 字段就是设置 listViewitem 的状态。 listView 控件的常见状态有选中和焦点两个。有了 state 状态为啥还搞一个 stateMask 呢?这个是为了设置影响到的状态。要修改 ListViewitem 状态,你不需要先获取每个状态值,之需要在 stateMask 中指定你想要修改的某个或者某几个状态即可。例如这里你需要修改 select 状态,之需要将 itemstatestateMask 都设置为LVIS_SELECTED

    // MFC:

    list.SetItemState(nItemToSelect,LVIS_SELECTED, LVIS_SELECTED );

    // Win32 API:

    ListView_SetItemState ( hwndYourList, nItemToSelect, LVIS_SELECTED, LVIS_SELECTED );

    注意:默认 listview 控件是没有 LVS_SHOWSELALWAYS 属性,也就是说当前选择的 itemlistview 控件非激活状态下是不显示选中行的。

    2.9 问:我的 list 控件和 tree 控件在 debug 下运行 OK ,但是在 release 版本下出问题。

      答:很可能是因为你使用到 LVITEM 或者 TVITEM 没有初始化导致。在所有使用的地方先初始化试试。例如:LVITEM lvi = {0}; TVINSERTSTRUCT tvins = {0};

    2.10 问:如果在多行文本框中换行?

         答:使用 ”/r/n” 来换行。如果你使用 ”/r” 或者 ”/n” 或者 ”/n/r” ,都被忽略了。

    2.11 问:如何给用户一个交互提示用于选择一个目录?

         答:使用 SHBrowseForFolder()

    2.12 问:如何限制我的窗口的最大最小尺寸?

         答:处理 WM_GETMINMAXINFO 即可。设置最大和最小 trace 大小。

    LRESULT OnGetMinMaxInfo ( WPARAM wParam, LPARAM lParam )

    {

      MINMAXINFO* pmmi = (MINMAXINFO*) lParam; 

      pmmi->ptMinTrackSize.x = 100;

      pmmi->ptMinTrackSize.y = 150;

      pmmi->ptMaxTrackSize.x = 600;

      pmmi->ptMaxTrackSize.y = 400;

      return 0;

    }

     

    3、  控制台程序问题

    3.1 问:如何修改控制台颜色、清除控制台内容等。

        答:利用控制台函数 FillConsoleOutputCharacter() FillConsoleOutputAttribute() 函数。在使用这些函数之前,你得获取到控制台句柄,利用 GetStdHandle() 即可获取到控制台句柄。

    3.2 问:如何修改控制台文字颜色

        答:利用 SetConsoleTextAttribute() SetConsoleOutputAttribut() 。当然,控制台不支持下划线和加粗。如果想要这种效果 ….

    3.3 问:在我的 GUI 程序中使用控制台

        答:很简单。直接调用 AllocConsole() API 即可。注意,一个进程只能挂接一个控制台。

    同时,如果程序创建了一个控制台,那么关闭控制台会导致应用程序的关闭。

    3.4 问:创建了一个控制台,如何使得在关闭控制台的时候不关闭我的应用程序?

        答:可以用一个简单的控制,屏蔽掉控制台的关闭菜单。

    void DisableClose()

    {

        char buf[100]; 

        wsprintf ( buf, _T("some crazy but unique string that will ID ")

                   _T("our window - maybe a GUID and process ID") ); 

        SetConsoleTitle ( (LPCTSTR) buf ); 

        // Give this a chance - it may fail the first time through. 

        HWND hwnd = NULL; 

        while ( NULL == hwnd )

        {

            hwnd = ::FindWindowEx ( NULL, NULL, NULL, (LPCTSTR) buf );

        } 

        // Reset old title - we'd normally save it with GetConsoleTitle. 

        SetConsoleTitle ( _T("whatever") );  

        // Remove the Close menu item. This will also disable the [X] button 

        // in the title bar.

     

        HMENU hmenu = GetSystemMenu ( hwnd, FALSE ); 

        DeleteMenu ( hmenu, SC_CLOSE, MF_BYCOMMAND );

    }

    需要注意的是上述在 FindWindowEx 使用了循环。在调试运行下一般来说第一次就能到 Find 到控制台窗口,但是在普通运行模式下,第一次基本上都找不到窗口,不知道是不是 MS 的一个 Bug 。这里使用了循环,保证能找到的我们的窗口。

     


    最新回复(0)