65.9K
CodeProject 正在变化。 阅读更多。
Home

WiFi 扫描仪 + 自定义 MFC 控件

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.78/5 (28投票s)

2007年6月25日

CPOL

16分钟阅读

viewsIcon

242120

downloadIcon

10282

一个带有自定义滑块、选项卡控件、按钮和复选框的 Wi-Fi 扫描仪

Screenshot - pp.gif

引言

几周前我在家里设置了无线网络,并对无线工具产生了兴趣。我想知道 AP 扫描仪软件是如何工作的。我做了一些研究,发现使用 PocketPC 2003 及更高版本系统中可用的 NDISUIO 驱动程序可以非常简单地获取 Wi-Fi 信息。因此,我编写了一个简单的扫描仪,它有两种视图模式。第一种是列出所有附近 AP 的彩色列表。另一种模式以大数字显示选定 AP 的信号强度。这样它就具有可接受的户外可见性,并且您可以跟踪单个信号,而不必看到邻居们的 AP 在屏幕上闪烁。我还包含了之前编写的各种自定义 MFC 控件:一个位图滑块、一个选项卡控件和按钮。我希望您也能发现这些类很有用。

本文特色是以下类

  • CWifiPeek - 一个可用于列出附近 Wi-Fi 设备的类
  • CColoredDlg - 一个支持子控件自定义颜色的对话框基类
  • CCustomTabCtrl - 一个在 WinCE/PocketPC 下运行良好的自定义选项卡控件
  • CCustomSlider - 一个支持 BMP 的自定义滑块(轨道条)控件
  • CCustomButton - 自定义按钮控件(按钮、复选框、单选按钮等)

使用应用程序

PeekPocket 使用起来相当简单。只需打开您的 WLAN,运行应用程序,然后查看列出的 AP。安全 AP 以红色显示,不活动的 AP 以灰色显示。不活动的 AP 是当前不可见的 AP。如果您点击一个 AP 名称,您可以“放大”,即在不显示任何其他 AP 的情况下跟踪一个信号。您可以在“选项”选项卡上设置列表字体大小和扫描速度。您也可以在那里设置网络类型和安全筛选器。您的 Wi-Fi 适配器应显示在组合框中。如果未显示,则说明有问题。在这种情况下,您可以尝试以下操作

  • 禁用然后重新启用无线网络。
  • 如果 PDA 与您的 PC 有 ActiveSync 连接,请断开连接。
  • 如果您已配置与 AP 的自动连接,请尝试删除它。

如果这些方法无效,有时重启会奏效。您可以使用 Visual Studio 2005 和 PocketPC 2003 SDK 编译该应用程序。PeekPocket 在 WM5 和 WM6 设备上也能正常运行。如果您将其他 SDK 添加到演示项目中,您可能需要更改一些编译器/链接器设置。

轻松访问点列表

CWifiPeek 类完成了所有 Wi-Fi 查询工作。它也可以在非 MFC 应用程序中使用。您需要将 CWifiPeek.hCWifiPeek.cpp 添加到您的项目中。不要忘记为该 CPP 文件禁用预编译头文件!该类使用 NDIS 用户模式 I/O 驱动程序 (NDISUIO) 来执行 AP 扫描。以下结构将用于返回站信息

struct BSSIDInfo
{
    BYTE BSSID[6];            //MAC Address
    WCHAR SSID[32];           //Station name    
    int RSSI;                 //Signal strength    
    int Channel;              //Used channel    
    int Infastructure;        //Type: AP or peer
    int Auth;                 //Open or secure?
};

该类提供了以下函数

  • bool GetAdapters(LPWSTR pDest, DWORD &dwBufSizeBytes);
  • bool RefreshBSSIDs(LPWSTR pAdapter);
  • bool GetBBSIDs(LPWSTR pAdapter, struct BSSIDInfo *pDest, DWORD &dwBufSizeBytes, DWORD &dwReturnedItems);

GetAdapters 函数可用于查询网络适配器的名称。它调用内置的 NDIS(而不是 NDISUIO)驱动程序。您需要传递一个 WCHAR 缓冲区和缓冲区大小的地址。该函数用逗号分隔的适配器名称填充缓冲区。它还会将 DWORD 设置为返回的字节数。它会过滤掉一些适配器名称,例如红外线、GPRS、ActiveSync 连接。如果函数返回 true,但指示复制了零字节,则可能存在问题。您的 Wi-Fi 可能已关闭或可能无法运行。

该函数使用 IOCTL_NDIS_GET_ADAPTER_NAMES IOCTL 调用。必须检索适配器名称,因为 NDISUIO 调用需要此参数。RefreshBSSIDs 函数请求驱动程序启动 AP 扫描。它接受一个参数:适配器名称。成功后,它返回 true。此函数会频繁调用,以便我们拥有最新的列表。

GetBBSIDs 函数返回可用站点列表,即对等节点和访问点。它接受适配器名称、目标缓冲区指针以及缓冲区大小。如果成功,它返回 true 并将 dwReturnedItems 填充为返回的结构数量,而不是字节数。这两种方法使用 OID_802_11_BSSID_LIST_SCANOID_802_11_BSSID_LIST 无线 OID。关于返回的站点数据说明

  • BSSID 字段包含站点的 MAC 地址。
  • 站点名称在 SSID 中返回。这可能是一个空字符串。
  • 信号强度以 dBm 为单位在 RSSI 中返回。-50 dBm 的信号比 -60 强,比 -70 强,依此类推。通常,低于 -75 的信号被认为非常差。-50 dBm 的信号电平还可以。
  • Infrastructure 字段可以是 Ndis802_11IBSS(对等节点)或 Ndis802_11Infrastructure(AP)。
  • 对于不安全的站点,Auth 字段的值是 Ndis802_11AuthModeOpen

彩色对话框

所有对话框的基类是 CColoredDlg 而不是 CDialog。该类通过 OnCtlColor 处理程序支持自定义颜色。在对话框控件的绘制周期中,会调用此处理程序,父对话框可以设置正在绘制的控件的背景和前景颜色。此功能可用于使 GUI 看起来略有不同。但是,如果您查看代码,它非常简单。您可以按照几个简单的步骤使用此类

  • ColoredDlg.hColoredDlg.cpp 添加到您的项目中。
  • 在您的对话框头文件和 CPP 文件中,将所有 CDialog 替换为 CColoredDlg
  • 然后将 #include "ColoredDlg.h" 添加到您的对话框头文件中。

就是这样!颜色应在 OnInitDialog 函数中使用 SetBkgColorSetFrgColor 调用进行设置。这些调用接受一个 COLORREF 值,例如可以使用 RGB 宏创建。

您可以使用 OnCtlColor 处理程序绘制不同颜色的不同控件。控件不一定需要拥有者绘制才能更改颜色。

选项卡控件

CustomTabCtrl.hCustomTabCtrl.cpp 中的选项卡控件是 Andrzej Markowski 出色的 Custom Tab Control 的 WinCE / PocketPC 兼容版本。我通过删除所有 Win32 特定的内容(如 XP 主题支持、工具提示等)使其与 WinCE 兼容。我还将选项卡制成了矩形,因为使用 WinCE GDI 绘制它们要简单得多。存在左右方向,但它们是实验性的并且非常慢,即 WinCE 上没有 PlgBlt。总的来说,建议仅使用顶部和底部方向。该类仍然兼容 Win32,也可以与 Embedded Visual C++ 4 一起使用。

请参阅 Andrzej 的文章,了解如何在您的应用程序中使用此控件。只需不要忘记为该版本禁用预编译头文件。该控件既可以动态创建,也可以从模板创建。该控件支持自定义颜色。要获取和设置这些颜色,请使用以下函数

  • void GetColors(TabItemColors *pColorsOut);
  • void SetColors(TabItemColors *pColorsIn, BOOL fRedraw=FALSE);

这些调用使用以下结构

struct TabItemColors
{       
    COLORREF crWndBkg;            //window background color
    COLORREF crBkgInactive;       //background color for inactive tab item
    COLORREF crBkgActive;         // .. active tab item
    COLORREF crTxtInactive;       //text color for active tab item
    COLORREF crTxtActive;         // .. active tab item
    COLORREF crDarkLine;          //darker line
    COLORREF crLightLine;         //lighter line
};

您可以在此图像中看到哪个是哪个

Screenshot - tabs1.jpg

“扫描仪”选项卡处于活动状态,“选项”选项卡处于非活动状态。选项卡周围的线条是“暗线”。crLightLine 字段保留供将来使用。“1”标记的右侧矩形区域是“窗口背景”。也就是说,控件窗口中未绘制选项卡的部分。最好先调用 GetColors,更新您想要的颜色,然后调用 SetColors。请查看 CPPDlg::OnInitDialog 函数。您可以像使用原始控件一样设置选项卡字体。有一点需要注意,如果您使用 CFont,不应该在 OnInitDialog 中声明它,而应在对话框头文件中声明为成员变量。这样,在关闭对话框之前,字体不会被释放。

我向该控件添加了其他内容:容器模式。Andrzej 的选项卡控件实际上是一个带有按钮的栏。当用户单击某项时,该栏会将各种通知发送给其父项,但显示和隐藏子窗口的责任在于应用程序。另一方面,容器可以托管子对话框并自动显示或隐藏它们。可以通过设置 CTCS_CONTAINER 样式来启用容器模式。此模式由派生类 CCustomTabContainer 支持。与容器模式相关的新函数是

  • int GetTabsHeight();
  • void SetTabsHeight(int nHeight);
  • void AdjustRect(BOOL bLarger, LPRECT lpRect);
  • void AddDialog(int nIndex, CString strText, CDialog *pDlg);
  • void RemoveDialog(int nIndex);

在容器模式下,整个选项卡控件窗口的高度显然大于选项卡本身的高度

Screenshot - tabs2.jpg

可以使用 SetTabsHeight 函数设置选项卡的高度。要获取当前值,请使用 GetTabsHeight 函数。该控件有一个 AdjustRect 函数,类似于 CTabCtrl::AdjustRect。要在应用程序中使用容器,请按以下步骤操作

  • 将对话框资源添加到您的应用程序。对话框应没有标题栏、边框,并确保它们具有“子”样式。
  • 为这些对话框添加类,例如演示应用程序中的 CScannerDlgCOptionsDlg
  • 遵循 Andrzej 的 说明将选项卡控件添加到您的应用程序。像添加 CCustomTabCtrl 一样进行!
  • 在您的对话框头文件中,将成员变量类型从 CCustomTabCtrl 更改为 CCustomTabContainer
  • 为子对话框指针添加成员变量,例如 PPDlg.h 中的 m_pScannerDlgm_pOptionsDlg
  • 在您的 OnInitDialog 函数中设置容器模式(CTCS_CONTAINER 样式)和颜色。
  • 使用 AddDialog 添加选项卡和子对话框;请查看 CPPDlg::OnInitDialog 函数。

请注意,容器会自动在容器销毁、点击关闭按钮或调用 RemoveDialog 时调用 delete 对话框。

滑块控件

这是一个位图滑块控件,实现为 CWnd 派生类。它也可以在 WinCE / PocketPC 和 Win32 平台上使用。它具有水平和垂直方向,以及反向模式

Screenshot - slider1.jpg

要在您的应用程序中使用它,请按以下步骤操作

  • CustomSlider.hCustomSlider.cpp 添加到您的项目中。应为 CPP 文件关闭预编译头文件。
  • #include "CustomSlider.h" 添加到相应的对话框头文件中。
  • 在对话框类中添加一个类型为 CCustomSlider 的成员变量。
  • 对于基于模板的创建,请添加一个类设置为 CustomSliderClass 的自定义控件。
  • 或者,对于动态创建,请在 OnInitDialog 函数中添加 m_Slider.Create(_T("CustomSliderClass"), strTitle, strStyle, sliderRect, this, IDC_SLIDER)
  • DoDataExchange 中的 DDX_Control 调用中将变量与控件链接起来。
  • 在您的对话框中添加滚动处理程序;请参阅下面的示例。

您可能知道,滑块控件有两个不同的部分:所谓的“拇指”——即实际滑动的对象——和“通道”,即拇指移动的区域。下图显示了一个通道、一个拇指以及它们组成的滑块控件

Screenshot - slider2.jpg

滑块具有以下属性,应在 OnInitDialog 中设置

  • BkgColor 确定用于擦除控件背景的颜色。
  • RangeMin 指定控件可以返回的最小值。
  • RangeMax 指定控件可以返回的最大值。
  • Pos 是拇指的当前位置。
  • 有 3 个位图:一个用于通道图像,一个用于非活动(未点击)的拇指,一个用于活动(点击、拖动)的拇指。
  • 有一个 Reverse 标志,用于确定滑块是否反向工作。

您可以使用以下函数来获取和设置这些属性

  • void GetRange(DWORD& dwMin, DWORD& dwMax);
  • DWORD GetRangeMin();
  • DWORD GetRangeMax();
  • void SetRange(DWORD dwMin, DWORD dwMax);
  • void SetRangeMin(DWORD dwMin);
  • void SetRangeMax(DWORD dwMax);
  • DWORD GetPos();
  • void SetPos(DWORD dwPos);
  • void SetReverse(bool bRev);
  • bool GetReverse();
  • void SetBkgColor(COLORREF crBkg);
  • COLORREF GetBkgColor();
  • void SetBitmaps(HBITMAP hBmpChannel, HBITMAP hBmpThumbInactive, HBITMAP hBmpThumbActive = NULL);

注释

  • 滑块默认是水平的。要使其垂直,请指定 TBS_VERT 样式。
  • 滑块没有默认图形,因此在所有情况下都必须设置位图。
  • 如果省略 SetBitmaps 的第三个参数,则活动和非活动状态的拇指将使用相同的位图。
  • 控件支持透明度。黑色将被视为透明。
  • 控件将自动释放您指定的位图句柄。
  • 控件支持非负范围和位置值,即零及以上。如果您需要负值,则必须偏移返回的位置。
  • 滑块矩形与通道位图尺寸相同是一个好主意。
  • 使用的位图的宽度和高度应能被 2 整除。
  • 如有疑问,请查看 COptionsDlg::OnInitDialog。示例代码有效,因此肯定有办法。

以下是如何处理滑块事件。在对话框类中添加 OnHScroll 或 OnVScroll 处理程序。处理程序应如下所示

void COptionsDlg::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar)
{
    if(pScrollBar != NULL && pScrollBar->IsKindOf(
        RUNTIME_CLASS(CCustomSlider)))
    {
        //which slider was it?
        switch(pScrollBar->GetDlgCtrlID())
        {
            case IDC_SCANSPEED_SLIDER:
            {
                  ... use nPos
                break;
            }//case scanspeed slider
            case IDC_FONTSIZE_SLIDER:
            {
                  ... use nPos
                break;
            }//case fontsize slider
        }
    }
}

基于按钮的控件

Win32 有大量的拥有者绘制按钮,但 WinCE 的数量要少得多。CCeButtonST 是一个出色的控件,但它不支持我想要的一些功能。例如,它不支持为活动/非活动按钮设置不同的标题,或修改组合框和单选按钮的行为。激活一个单选按钮会取消选中同一控件组中的所有其他单选按钮。CCustomButton 是一个拥有者绘制的 MFC 按钮类,具有以下特点

  • 在 WinCE / PocketPC / Win32 上运行,支持 Embedded Visual C++ 4 和 Visual Studio 2005
  • 添加到现有项目非常容易
  • 支持按钮、单选按钮、复选框和组合框
  • 支持带透明度的 BMP 图像(无图标)
  • 支持自定义字体
  • 也支持纯文本或纯位图按钮
  • 可以为活动/非活动按钮(按下/正常)状态设置不同的标题和/或图像
  • 支持常规推杆按钮、推杆式按钮和扁平按钮
  • 支持渐变背景色

要在您的应用程序中使用它,请将 CustomButton.hCustomButton.cpp 添加到您的应用程序中。您猜怎么着:与上面的控件一样,这个控件也要求您为 CPP 文件禁用预编译头文件。将按钮添加到对话框中,并添加类型为 CCustomButton 的成员变量。无需将按钮设置为拥有者绘制。您也可以使用其 Create 方法动态创建控件。可以为按钮、单选按钮和复选框设置以下属性

  • 空闲(非活动 - 未按下)控件的文本颜色
  • 活动(按下)控件的文本颜色
  • 空闲(非活动 - 未按下)控件的背景颜色
  • 活动(按下)控件的背景颜色
  • 非活动和活动控件的标题
  • 非活动和/或活动的控件的位图
  • 使用的字体
  • 可选的文本和位图对齐方式

颜色可以使用以下方法设置

  • void SetBkgIdleColor(COLORREF crBkgIdle);
  • void SetBkgActiveColor(COLORREF crBkgActive);
  • void SetFrgIdleColor(COLORREF crFrgIdle);
  • void SetFrgActiveColor(COLORREF crFrgActive);

标题、字体和使用的位图可以通过以下方式更改

  • void SetCaption(CString strCaption, CString strActiveCaption = _T(""));
  • void SetFont(HFONT hFont);
  • void SetBitmaps(HBITMAP hBmpInactive, HBITMAP hBmpActive = NULL);

字体和位图将自动释放。如果省略第二个标题或位图参数,活动(按下)和非活动状态将使用相同的文本/位图。与上面的滑块一样,位图中的黑色将被视为透明。如果您设置了自定义字体,请确保在关闭对话框之前不要释放它。因此,如果您使用 CFont 创建字体,请在头文件中将变量声明为对话框类的成员变量,而不是在 OnInitDialog 中。

您可以使用 SetFlags 函数设置一些额外的属性。请参阅下面的可用标志。您拥有的是推杆按钮还是复选框取决于使用的按钮样式(BS_XXX)。如果您使用对话框编辑器添加按钮、单选按钮或复选框,这些样式将自动处理。如果您手动添加控件,则应指定以下样式

  • BS_RADIOBUTTONBS_AUTORADIOBUTTON 用于单选按钮
  • BS_CHECKBOXBS_AUTOCHECKBOX 用于复选框
  • BS_GROUPBOX 用于组合框
  • 如果未指定以上任何一项,则为推杆按钮

使用推杆按钮

以下样式特定于推杆按钮

  • 如果您想要一个没有边框的扁平按钮,应使用 BS_FLAT
  • BS_PUSHLIKE 将产生一个“可切换”的推杆按钮。
  • 如果您想使用位图,应使用 BS_BITMAP。您需要使用 SetBitmaps 函数设置位图。
  • 您可以使用 BS_LEFTBS_RIGHTBS_CENTERBS_VCENTERBS_BOTTOMBS_TOPBS_SINGLELINEBS_MULTILINE 格式。

您可以使用 SetFlags 函数指定其他推杆按钮属性

  • 使用 bfTextLeftbfTextRightbfTextTopbfTextBottom 中的一个来指定文本相对于位图的绘制位置。这类似于 CCeButtonSTSetAlign 函数
  • 使用 bfHGradient 来实现渐变色背景。
  • 使用 SetGradientColors 指定使用的颜色(开始、结束)。

以下是两个具有渐变背景和自定义字体的按钮

Screenshot - gradient.jpg

使用复选框和单选按钮

以下样式可用于复选框和单选按钮

  • 如果您想使用位图,应使用 BS_BITMAP。您需要使用 SetBitmaps 设置位图
  • 您可以使用 BS_LEFTBS_RIGHTBS_BOTTOMBS_TOPBS_CENTERBS_VCENTERBS_SINGLELINEBS_MULTILINE 格式。
  • 要为活动/非活动状态使用自定义位图,请使用 BS_BITMAP 样式并通过 SetBitmaps 函数指定位图。

您无法为这些控件类型在对话框编辑器中指定 BS_BITMAP,因此如果需要,请手动将其添加到 RC 文件中。您可以使用 SetFlags 函数指定其他属性

  • 使用 bfTextLeftbfTextRight 来指定文本相对于位图的绘制位置。

以下复选框具有默认的 bfTextRight 对齐方式

Screenshot - textright.jpg

使用组合框

以下样式可用于组合框

  • BS_LEFTBS_RIGHTBS_CENTER 用于文本格式。

您可以使用 SetFlags 函数指定其他属性

  • 使用 bfTextTopbfTextBottom 来指定标题的绘制位置。

前两个组合框有一个标题并演示了不同的文本放置和对齐方式。第三个框没有标题;它几乎只是一个填充的矩形

Screenshot - group.jpg

只需三步即可设置一个组合框

  • SetFrgIdleColor 设置标题文本颜色。
  • SetFrameColor 设置控件边框和标题背景颜色。
  • SetBkgIdleColor 设置控件背景填充颜色。

您也可以为组合框设置自定义字体。请注意,由于 WinCE 绘制控件的方式,您应该注意将组合框放在 RC 文件中的分组控件之后。在对话框编辑器中看到什么并不重要;RC 文件中的对话框应如下所示

BEGIN
    CONTROL    
        "Check1",IDC_CHECK1,"Button",BS_AUTOCHECKBOX | BS_BITMAP,48,54,60,10
    CONTROL    
        "Check2",IDC_CHECK2,"Button",BS_AUTOCHECKBOX | BS_BITMAP,48,72,60,10
    PUSHBUTTON "Flat button",IDC_FLATBTN,35,27,90,19,BS_FLAT
    GROUPBOX   "GroupBox",IDC_GB,7,7,142,101
END

组合框是最后一个。如果您动态创建控件,则应使用 SetWindowPos 对它们进行排序,以便组合框不会覆盖其他控件。玩得开心!

更新

感谢您敏锐的读者,我在该项目中修复了许多小错误。我还添加了一些功能,包括一个多语言用户界面,最初支持 5 种语言:英语、法语、匈牙利语、波兰语和葡萄牙语。如果您想使用本文介绍的任何类,恳请您从 这里 获取最新的源代码。

历史

  • 2007 年 6 月 25 日 -- 发布了原始版本
  • 2007 年 7 月 16 日 -- 更新
  • 2007 年 7 月 30 日 -- 文章经过编辑并移至 CodeProject.com 的主文章库
© . All rights reserved.