A Quick MFC and WTL Comparison

AI权益加码!Claude Code、Cursor等20+工具免费用! 购周边限时加赠Coding Plan Lite,畅享主流AI工具!学习进阶更高效! 阅读详情

A Quick MFC and WTL Comparison

By: Kenn Scribner for Visual C++ Developer



Background: I really like working with ATL, but it would have been nice to work with the Win32 API in such a clear and simple manner. Now you can, just in time for this to be deprecated by WinForms. :) (WinForms are .NET's answer to traditional Windows thick client/desktop application programming.) But give WTL a try and see what you think.  If you like C++, templates, and Windows programming, you should like WTL.

A Quick MFC and WTL Comparison

As I write this, I had just completed a little utility for my wife that would help keep her dial-up network connected. We use ISDN to dial into her medical school, which places the majority of her lectures online. The problem she has with this is the remote server disconnects her while she's working through her lectures. It apparently monitors idle network activity, and after a very short inactive period logs the student off. This can be more than a mere irritation, especially when she's preparing for an exam.

So being the gregarious and loving spouse, I challenged myself to write in five minutes a little MFC application to solve this problem. The good news is I met the challenge, at least initially. As for the bad news, well...as with most projects, this utility grew a little, and soon I had spent an entire evening embellishing it! (To many, myself included, this isn't truly bad news as I had fun doing so!) But my wife is delighted and has since passed copies to her fellow students. I believe this is a good thing, especially if one of them elects to enter geriatrics--I may need their services some day!

What does this have to do with the Windows Template Library (WTL) and the Microsoft Foundation Classes (MFC)? The key lies in the phrase "passed copies" in my previous paragraph. For my wife to be able to pass copies of my MFC-based application to her friends, I had to statically compile the MFC libraries. Initially, I did so with debug-level code, and I was amazed to find my tiny application grew to over 2MB in size! Ouch! Even a release-level version consumed over 200K of disk space.

I then wondered how large would a similar application be if I implemented the same functionality in WTL. I can hardly write COM code without ATL, so why not take a deeper look into WTL for traditional Windows client code? Here is what I found, given the scenario I've outlined. Since WTL is not as commonly used as MFC, I'll also give you a brief tour through the WTL version of my utility application.

Getting to Know WTL

Chances are you haven't worked with WTL. There isn't space available in this article for me to adequately describe WTL in detail, but I can point you in a couple of directions for help. First, I've included WTL with the sample application's download to make it easier for you to find and use it. Second, there are several good online sources for WTL information, including source code, descriptions, tutorials, and other handy stuff. Here is a list of several URLs you should visit if you're interested in WTL:

  • www.sellsbrothers.com/tools (Chris Sells is a WTL aficionado)
  • www.develop.com/dm/dev_resources.asp (two excellent "Inside WTL"-style white papers)
  • www.idevresource.com/com/library/articles/wtlarch.asp (WTL architecture from Richard Grimes)
  • www.codeproject.com/wtl (source code examples)

Note: I patched the original WTL files with the revised code you'll find at  CodeProject to correct a few ugly and not so ugly bugs.

If you like, you can download the patched files here. To the best of my knowledge the patched files reflect the suggestions made at CodeProject, but I can accept no responsibility for the correctness of the patches. Use at your own discretion. However, I use them myself in my professional work, if that says anything...

Unfortunately, Microsoft considers WTL an unsupported project, so there is a significant lack of documentation coming from Redmond on the subject. But if you're looking for lean and mean Windows code but don't want to dip all the way down to the raw Windows API for everything, WTL is most likely a good alternative. The best news is that WTL consists of several header files (and an AppWizard), so you're free to modify the source code should you need to do so.

WTL and MFC, The Experiment

I wanted to compare apples to apples, and since I had implemented the utility in MFC first, I copied as much of that existing code to the WTL project as I could. The application itself consists of a dialog-based utility that wraps CreateProcess(). To keep the remote system from sensing inactivity, I "ping" a remote site at regular intervals. I could have written the ping functionality myself, using icmp.dll, but to meet my five-minute challenge, I decided to simply simulate this command line at the DOS prompt:

> ping www.endurasoft.com

If you're not familiar with ping, it's a utility that prompts the remote host for status information. The ping program reads the returning network packets and determines if the remote host is available, and if it is, how long was the roundtrip latency. The term ping stands for packet internet groper.

Any system with network capability will have ping.exe loaded, so barring catastrophic conditions, CreateProcess() should be able to locate ping.exe, run it, and generate enough spurious network traffic to keep the remote host happy for awhile. Of course I selected a well-known Internet URL to ping (well known to me, anyway).

When handling WM_INITDIALOG, both versions of the Pinger utility start the timer and issue an initial round of pings. After the initial pings are made, the timer is configured to issue the WM_TIMER message after the user-selectable period has expired. To keep things a bit simpler, I elected to single-thread the application, in which case various buttons are disabled while the ping process is in progress.

WTL and MFC, The Results

The only quantitative result I was interested in determining was to compare compiled executable file sizes. This application is not computationally intensive, so I didn't consider runtime latencies to be a critical piece of information. Even so, there are several qualitative conclusions we can draw from the file sizes, which I address in the next section.

The resulting file sizes for pinger.exe are shown in Table 1. Note the MFC versions include statically linked MFC and C runtime libraries (for easy distribution). These files were built using Visual Studio, Version 6, Service Pack 4 running under Windows 2000 Server. The WTL versions do not require any additional WTL support libraries.

Table 1--Relative Compiled File Sizes MFC/WTL

VersionSize (in KB on disk)
MFC Debug2030
MFC Release212
WTL Debug420
WTL Release56

WTL and MFC, The Conclusions

For each case, the WTL executable image is roughly 4 times smaller than the MFC image (3.79 times smaller for release, 4.83 times smaller for debug). Granted, this isn't statistically accurate, so we can't draw generalized results for each and every possible WTL and MFC application. However, it does underscore the fact that WTL, like ATL, can be used for creating Windows executables with very small footprints very quickly.

I should note that you could make the MFC executables smaller yet if you used the MFC libraries dynamically. However, this masks the true size of your application. The MFC DLLs are 908 and 972 KB in size (respectively) for debug and release on my system. You are then also responsible for supplying the DLLs to your user base, and as anyone who has written installation programs and installed MFC DLLs knows, this is more than a little challenging to do correctly. It is dangerous to assume the MFC DLLs will be in place for you if you don't install them yourself. The unfortunate part of this is you can very easily damage a user's computer by installing incorrect MFC DLLs, such as those found in the initial release of Visual Studio, Version 6.0.

WTL precludes all of this, and if you keep the ATL_MIN_CRT directive in place as you compile, you won't even need the C Runtime Library. In that case you also won't need to distribute msvcrt.dll, although in many practical cases you can avoid distributing this DLL. It has not changed much over the years (although professional installations do install it as a matter of course if it is required).

So if WTL is so wonderful, why isn't everyone using it? Laying the argument for thin clients aside, part of the answer lies in the fact WTL was only recently introduced. In fact, the first place it became available to the developer community was with the Platform SDK for Windows 2000 (not part of the standard Platform SDK). It came as part of a code sample...very little fanfare for such an evolutionary companion to ATL 3.0. But it's also true Visual Studio supports MFC application development to a greater degree. While there is some ATL support present in Visual Studio, most MFC developers are quite familiar with ClassWizard even though there is a rough equivalent for ATL (and WTL).

MFC also has evolved to the point it supports a great many desirable features, such as MAPI, support for the other Windows logo requirements, sockets, documents (if you like and/or use that pattern), and compound document files. WTL has its share of cool features, but MFC is the clear feature champ. Both environments support framed main window architectures (frame window with separate view window), SDI and MDI applications, split windows, dialog-based applications, and various COM-based classes for COM support.

If you had asked me two years ago which development tool I'd use to create full-featured client-side applications, I would have told you I'd use MFC. However, even given the loss of some of the features provided by MFC, today I'd use WTL to create professional client applications. Not having to install the MFC DLLs on a user's system is a benefit that far outweighs the MFC feature loss, at least in my opinion.

The WTL Pinger Utility

To help introduce you to the WTL, I'll briefly describe the WTL version of my ping utility. I created the WTL Pinger version using the WTL AppWizard, which you'll find included with the WTL files included with the source code download. Like the MFC application AppWizard, you can select from an assortment of application styles, as you see from Figure 1.

I selected a dialog-based application, which led to the second AppWizard page you see in Figure 2.

With the files initially created, I created the utility's user interface, which I've shown in Figure 3.

The WTL AppWizard creates fewer files than the MFC AppWizard, and in this particular case, I made modifications to maindlg.h, resource.h, and pinger.rc. I left the remaining WTL source files unchanged.

As far as the resource file pinger.rc was concerned, I removed the extraneous strings from the string table, updated the version information, removed the (unnecessary) accelerator table, and modified the dialog box to match the MFC version's user interface. I naturally made corresponding changes to resource.h. These items should not be present in a dialog-based application, which I believe to be a bug in the AppWizard.

The bulk of the work is shown in Listing 1, where you see the contents of maindlg.h.

Listing 1. Pinger's maindlg.h File Contents

// maindlg.h : interface of the CMainDlg class

//

/////////////////////////////////////////////////////////////////////////////

#if !defined(AFX_MAINDLG_H__DC16127C_44F9_4BCF_BDAB_52E20D803699__INCLUDED_)

#define AFX_MAINDLG_H__DC16127C_44F9_4BCF_BDAB_52E20D803699__INCLUDED_

 

#if _MSC_VER >= 1000

#pragma once

#endif // _MSC_VER >= 1000

 

class CMainDlg : public CAxDialogImpl<CMainDlg>, CUpdateUI<CMainDlg>,

                 public CMessageFilter, public CIdleHandler

{

   public:

   enum { IDD = IDD_MAINDLG };

   virtual BOOL PreTranslateMessage(MSG* pMsg)

   {

      return IsDialogMessage(pMsg);

   }

 

   virtual BOOL OnIdle()

   {

      return FALSE;

   }

 

   BEGIN_MSG_MAP(CMainDlg)

      MESSAGE_HANDLER(WM_INITDIALOG, OnInitDialog)

      COMMAND_ID_HANDLER(IDOK, OnOK)

      COMMAND_ID_HANDLER(IDCANCEL, OnCancel)

      COMMAND_ID_HANDLER(IDC_PINGNOW, OnPingNow)

      MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)

      MESSAGE_HANDLER(WM_TIMER, OnTimer)

   END_MSG_MAP()

 

   BEGIN_UPDATE_UI_MAP(CMainDlg)

   END_UPDATE_UI_MAP()

 

LRESULT OnInitDialog(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/)

{

   // center the dialog on the screen

   CenterWindow();

 

   // set icons

   HICON hIcon = (HICON)::LoadImage(_Module.GetResourceInstance(),

                                    MAKEINTRESOURCE(IDR_MAINFRAME),

                                    IMAGE_ICON,

                                    ::GetSystemMetrics(SM_CXICON),

                                    ::GetSystemMetrics(SM_CYICON),

                                    LR_DEFAULTCOLOR);

   SetIcon(hIcon, TRUE);

   HICON hIconSmall = (HICON)::LoadImage(_Module.GetResourceInstance(),

                                         MAKEINTRESOURCE(IDR_MAINFRAME),

                                         IMAGE_ICON,

                                         ::GetSystemMetrics(SM_CXSMICON),

                                         ::GetSystemMetrics(SM_CYSMICON),

                                         LR_DEFAULTCOLOR);

   SetIcon(hIconSmall, FALSE);

 

   // Add "About..." menu item to system menu.

   // IDM_ABOUTBOX must be in the system command range.

   _ASSERTE((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);

   _ASSERTE(IDM_ABOUTBOX < 0xF000);

   CMenu SysMenu = GetSystemMenu(FALSE);

   if(::IsMenu(SysMenu))

   {

      TCHAR szAboutMenu[256];

      if(::LoadString(_Module.GetResourceInstance(), IDS_ABOUTBOX, szAboutMenu, 255) > 0)

      {

         SysMenu.AppendMenu(MF_SEPARATOR);

         SysMenu.AppendMenu(MF_STRING, IDM_ABOUTBOX, szAboutMenu);

      }

   }

   SysMenu.Detach();

 

   // register object for message filtering and idle updates

   CMessageLoop* pLoop = _Module.GetMessageLoop();

   pLoop->AddMessageFilter(this);

   pLoop->AddIdleHandler(this);

   UIAddChildWindowContainer(m_hWnd);

 

   // Subclass and initialize the controls

   m_CURL.Attach(GetDlgItem(IDC_URLTXT));

   m_CURL.SetLimitText(MAX_PATH);

   m_CURL.SetWindowText("www.endurasoft.com");

 

   m_CRateSpin.Attach(GetDlgItem(IDC_RATESPIN));

   m_CRateSpin.SetRange(20,60);

   m_CRateSpin.SetPos(30);

   m_CRateSpin.Invalidate();

 

   // Start the timer...first pass is a 1 second delay

   // before first ping. :)

   m_iTimerID = SetTimer(1,1000);

 

   // Check for problems

   if ( m_iTimerID == -1 ) {

      // Couldn't get a timer, so ask to continue...user

      // will have to ping at will using "Ping Now" button.

      TCHAR szErrMsg[MAX_PATH];

      if( ::LoadString(_Module.GetResourceInstance(),IDS_E_NOTIMER,szErrMsg,MAX_PATH ) > 0) {

         // Error string loaded, so display error message

         int iResponse = MessageBox(szErrMsg,"Error!",MB_YESNO|MB_ICONERROR);

         if ( iResponse == IDNO ) EndDialog(-1);

      } // if

   } // if

   return TRUE;

}

LRESULT OnAppAbout(WORD /*wNotifyCode*/, WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)

{

   CAboutDlg dlg;

   dlg.DoModal();

   return 0;

}

LRESULT OnOK(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)

{

   // TODO: Add validation code

   CloseDialog(wID);

   return 0;

}

LRESULT OnCancel(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)

{

   CloseDialog(wID);

   return 0;

}

void CloseDialog(int nVal)

{

   // Kill timer

   if ( m_iTimerID != -1 ) KillTimer(m_iTimerID);

 

   // Take down the dialog

   DestroyWindow();

   ::PostQuitMessage(nVal);

}

LRESULT OnSysCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)

{

   UINT uCmdType = (UINT)wParam;

   if((uCmdType & 0xFFF0) == IDM_ABOUTBOX)

   {

      CAboutDlg dlg;

      dlg.DoModal();

   }

   else

      bHandled = FALSE;

 

   return 0;

}

LRESULT OnPingNow(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)

{

   // Do the ping

   DoPing();

   return 0;

}

LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)

{

   // Check status of suspend checkbox, and if not

   // suspended, ping

   if ( IsDlgButtonChecked(IDC_SUSPEND) == BST_UNCHECKED ) DoPing();

   return 0;

}

 

// Helpers

protected:

   BOOL CreatePing()

   {

      // Get the command line formatting string

      TCHAR szCmd[MAX_PATH+1] = {0};

      ::LoadString(_Module.GetResourceInstance(),IDS_PINGCMD,szCmd,MAX_PATH);

 

      // Get the URL

      TCHAR szURL[MAX_PATH+1] = {0};

      m_CURL.GetWindowText(szURL,MAX_PATH);

 

      // Formulate a command line

      TCHAR szCmdLine[(2*MAX_PATH)+1] = {0};

      wsprintf(szCmdLine,szCmd,szURL);

 

      // Create the ping process and wait for a quit

      STARTUPINFO si = {0};

      si.cb = sizeof(si);

      si.wShowWindow = SW_HIDE;

      BOOL bCreated = CreateProcess(NULL,szCmdLine,

                                    NULL,NULL,

                                    FALSE,DETACHED_PROCESS,

                                    NULL,NULL,

                                    &si,&m_pi);

 

   // Check for problems...

   if ( !bCreated ) {

      // Could not create the process

      TCHAR szErrMsg[MAX_PATH];

      if( ::LoadString(_Module.GetResourceInstance(),IDS_E_NOPROCESS,szErrMsg,MAX_PATH ) > 0) {

         // Error string loaded, so display error message

         int iResponse = MessageBox(szErrMsg,"Error!",MB_YESNO|MB_ICONERROR);

         if ( iResponse == IDNO ) EndDialog(-1);

      } // if

   } // if

 

   return bCreated;

}

 

void DoPing()

{

   // Kill the timer

   KillTimer(m_iTimerID);

   m_iTimerID = -1;

 

   // Disable the cancel and ping buttons

   CWindow wCancelBtn(GetDlgItem(IDCANCEL));

   wCancelBtn.EnableWindow(FALSE);

   CWindow wPingNowBtn(GetDlgItem(IDC_PINGNOW));

   wPingNowBtn.EnableWindow(FALSE);

 

   // Create the ping process

   CreatePing();

 

   // Wait for it to complete

   if ( WaitForSingleObject(m_pi.hProcess,60000) == WAIT_TIMEOUT ) {

      // Error: hung process, so kill it

      TerminateProcess(m_pi.hProcess,-1);

 

      // Ask to continue (we already killed the ping...)

      TCHAR szErrMsg[MAX_PATH];

      if( ::LoadString(_Module.GetResourceInstance(),IDS_E_HUNGPING,szErrMsg,MAX_PATH ) > 0) {

         // Error string loaded, so display error message

         int iResponse = MessageBox(szErrMsg,"Error!",MB_YESNO|MB_ICONERROR);

         if ( iResponse == IDNO ) {

            EndDialog(-1);

            return;

         } // if

      } // if

   } // if

 

   // Enable the cancel and ping buttons

   wCancelBtn.EnableWindow(TRUE);

   wPingNowBtn.EnableWindow(TRUE);

 

   // Start the timer

   m_iTimerID = SetTimer(1,m_CRateSpin.GetPos()*1000);

 

   // Check for problems

   if ( m_iTimerID == -1 ) {

      // Couldn't get the timer...

      TCHAR szErrMsg[MAX_PATH];

      if( ::LoadString(_Module.GetResourceInstance(),IDS_E_NOTIMER,szErrMsg,MAX_PATH ) > 0) {

         // Error string loaded, so display error message

         int iResponse = MessageBox(szErrMsg,"Error!",MB_YESNO|MB_ICONERROR);

         if ( iResponse == IDNO ) EndDialog(-1);

      } // if

   } // if

}

 

// Attributes

   UINT m_iTimerID;

   PROCESS_INFORMATION m_pi;

 

// Controls

   CContainedWindowT<WTL::CEdit> m_CURL;

   CContainedWindowT<WTL::CUpDownCtrl> m_CRateSpin;

};

 

/////////////////////////////////////////////////////////////////////////////

//{{AFX_INSERT_LOCATION}}

// Microsoft Visual C++ will insert additional declarations immediately before the previous line.

#endif // !defined(AFX_MAINDLG_H__DC16127C_44F9_4BCF_BDAB_52E20D803699__INCLUDED_)

The code in Listing 1 probably looks scarier than it really is. The WTL AppWizard automatically created a lot of it. I did make changes to some of the wizard-generated code. For example, to OnInitDialog(), I added the invocation of the "about" dialog to the system menu, so the WTL application would look and feel like its MFC counterpart:

// Add "About..." menu item to system menu.
// IDM_ABOUTBOX must be in the system command range.
_ASSERTE((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
_ASSERTE(IDM_ABOUTBOX < 0xF000);
CMenu SysMenu = GetSystemMenu(FALSE);
if(::IsMenu(SysMenu))
{
   TCHAR szAboutMenu[256];
   if(::LoadString(_Module.GetResourceInstance(), IDS_ABOUTBOX, szAboutMenu, 255) > 0)
   {
      SysMenu.AppendMenu(MF_SEPARATOR);
      SysMenu.AppendMenu(MF_STRING, IDM_ABOUTBOX, szAboutMenu);
   }
}
SysMenu.Detach();

I actually snatched this code from one of the WTL samples. Note I did have to create a definition for IDM_ABOUTBOX in resource.h. The other code I added subclassed the edit and spin controls as well as initiated the Windows timer:

// Subclass and initialize the controls
m_CURL.Attach(GetDlgItem(IDC_URLTXT));
m_CURL.SetLimitText(MAX_PATH);
m_CURL.SetWindowText("www.endurasoft.com");

 

m_CRateSpin.Attach(GetDlgItem(IDC_RATESPIN));
m_CRateSpin.SetRange(20,60);
m_CRateSpin.SetPos(30);
m_CRateSpin.Invalidate();

 

// Start the timer...first pass is a 1 second delay
// before first ping. :)
m_iTimerID = SetTimer(1,1000);

 

// Check for problems
if ( m_iTimerID == -1 ) {
   // Couldn't get a timer, so ask to continue...user
   // will have to ping at will using "Ping Now" button.
   TCHAR szErrMsg[MAX_PATH];
   if( ::LoadString(_Module.GetResourceInstance(),IDS_E_NOTIMER,szErrMsg,MAX_PATH ) > 0) {
      // Error string loaded, so display error message
      int iResponse = MessageBox(szErrMsg,"Error!",MB_YESNO|MB_ICONERROR);
      if ( iResponse == IDNO ) EndDialog(-1);
   } // if
} // if

As I'm using a timer, I had to be sure it was killed when the application exits. I therefore added this code to CloseDialog() (another AppWizard-generated method):

// Kill timer
if ( m_iTimerID != -1 ) KillTimer(m_iTimerID);

Since I added the option to invoke the "about" dialog to the system menu, I had to add a WM_SYSCOMMAND message handler:

LRESULT OnSysCommand(UINT /*uMsg*/, WPARAM wParam, LPARAM /*lParam*/, BOOL& bHandled)
{
   UINT uCmdType = (UINT)wParam;
   if((uCmdType & 0xFFF0) == IDM_ABOUTBOX)
   {
       CAboutDlg dlg;
       dlg.DoModal();
   }
   else
      bHandled = FALSE;

 

   return 0;
}

Note that you also must add the command to your message map:

MESSAGE_HANDLER(WM_SYSCOMMAND, OnSysCommand)

Since I added a button to ping immediately, I needed a handler for that:

LRESULT OnPingNow(WORD /*wNotifyCode*/, WORD wID, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
{
   // Do the ping
   DoPing();
   return 0;
}

This handler delegates the work to a helper method DoPing(). The handler for WM_TIMER follows a similar logical vein, except that it checks to see if pings have been suspended:

LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
   // Check status of suspend checkbox, and if not
   // suspended, ping
   if ( IsDlgButtonChecked(IDC_SUSPEND) == BST_UNCHECKED ) DoPing();
   return 0;
}

The helper methods DoPing() and CreatePing() were relatively unchanged between the MFC and the WTL versions. The minor changes I did have to make involved the message box code to display error conditions and the formulation of the command line string. I didn't use the WTL CString class so I could avoid the C Runtime Library (since I could). Note you could use the STL string class in its place if you still wanted smart string support.

Wrapping Up

I've been using MFC professionally for over five years and have come to know it well. But I have to admit WTL is awesome and well worth a professional C++ developer's time to examine and evaluate. With WTL you're able to create full-fledged Windows applications that rival the size of pure SDK apps with the speed and flexibility of the C++ template. You do lose a bit of functionality as compared to MFC, but I believe you also gain a bit with a simpler installation pattern. I can't say I won't write more MFC-based applications, but I can say that if I write any more professional applications I intend to distribute, they'll be written using the WTL.

%DUMP SESSIONS--List Windows Sessions 阅读详情

相关推荐

c++中 SetTimer的应用

<br />#pragma once<br />#include "VideoSourceServer/IVideoSourceServer.h"<br />#include "ConformRtmp/IRtmpConform.h"<br />#include "Buffer.h"<br />#include "TVCritSec.h"<br />#include "DrawVideoGDI.h"<br />#include "BitmapInfoHelpFunc.h"<br />#include "Wav

binglingyuyang的专栏 2138

基于Duilib库创建定时器机制

对话框demo框架 在基于Duilib库开发对话框时,我们一般建议基于WinImplBase类作为该对话框的基类,因为在WinImplBase类中封装了需用许多操作,便于对话框的创建;下面我们就基于WinImplBase类实现定时器功能。 /*某个对话框的基类demo,这里只截取了部分函数声明*/ class CXXXDialog: public WindowImplBase { public: ...

Keep Moving~ 1230

duilib CListUI滚动条自动滚到底部

duilib CListUI滚动条自动滚到底部 flyfish 在EndDown()失效时使用 原理:定时器处理滚动条,时间间隔50S,处理完关闭定时器 当界面更新50S之后,开始将滚动条滚动到底部 假设有一个窗口CMainWnd 头文件 virtual LRESULT OnTimer(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&...

二分掌柜的 2263

Duilib 实现右下角弹出像QQ新闻窗口,3秒后自动关闭(一)

转载:https://blog.twofei.com/667/ 自绘或子类化控件时,有时需要处理鼠标进入(MouseEnter)/鼠标离开(MouseLeave)/鼠标停留(MouseHover)消息,虽然有定义这两个消息常量(WM_MOUSEHOVER 与 WM_MOUSELEAVE),但默认情况下 Windows 的窗口消息是不会产生这三个消息的。但提供了一个 API 函数_TrackMou...

weixin_34406086的博客 432

duilib 界面库 实现timer定时器

看了大神介绍的duilib感觉已被同龄人狠狠地甩在背后。所以痛下决心,之后要多花时间写代码。 大神教程传送门: http://www.cnblogs.com/Alberl/p/3341956.html 现在的问题是想基于duilib实现一个timer定时器。工程基础大概是在 http://www.cnblogs.com/Alberl/p/3343763.html 因为自己的东西是基于大神...

weixin_30252709的博客 512

复合控件制作示例

平时在编程时,常想自己编一些适用的控件以利于程序开发,最近编了一个定时关机的控件。现把它提供出来,为和我类似的朋友提供小小的帮助。由于我也是初学乍练,有不当的地方请各位高手给予指出。 1、利用ATL COM Wizard新建一个ATL DLL工程,工程名定为TrueShutDown,其余所有设置保持为默认。 2、利用插入菜单中的"ATL Object Wizard"插入对象,选择复合控件(Comp

seu_xuxueqi的专栏 1136

duilib开发(九):定时器和超链接

一、定时器和超链接

yp18792574062的博客 1366

在ATL无窗口ACTIVEX控件中响应timer消息

  最近,在尝试着写一个能够显示静态和动态图片的ACTIVEX控件,就像QQ的一样,能插入到RICHEDIT控件中。由于只是在尝试,很多功能都没有实现,只实现显示。如果单单显示静态图片,是很简单的。但要是显示动态GIF图片,就有一定的难度,首先要分析图片的帧数,还要控件图片按顺序显示。如果写成控件,还要保证插入到RICHEDIT控件中还能动起来,就更有难度了,因为ATL在响应TIMER事件的时候有

水妖的专栏 9601

"code": 400, "msg": "Required request body is missing 错误解决

引起这个错误的原因是,请求参数按照get方式给。 应该给json字符串才对 补充: 1. @RequestBody String resource 加@RequestBody必须给json字符串,否则会报错400,记如标题错误。 不加这个的进行请求的话,其实post和get就没有什么区别了。 2. List&lt;String&gt; indexCodes=(List&lt;Stri...

u013452335的博客 1万+

坎坷WTL行(三)你们是怎么实现LOGO窗口的

想做一个LOGO窗口,在主窗口启动之初,显示一个窗口,首先做了一个对话框的资源(IDD_FORMVIEW)。通过修改CAboutDlg得到LogoFrm类,通过ShowWindow显示窗口,放于主窗口的OnCreate中。 CAboutDlg1 * dlg = new CAboutDlg1(); dlg->Create(this->m_hWnd); dlg->ShowWindow(SW_N

lymcn的专栏 2407

HDU3953 I'll play a trick on you

分析上图中的数字,很容易误以为样例就是求A,B之差的,但是发现图中的最后一个数字是“7”,不符合这一规律的,再对图进行观察,可以发现,如下规律: 99 72 “=>” 9+9=18 ,7+2=9 18+9=27; 45 27 “=>” 4+5=9, 2+7=9 9+9=18; 39 18 “=>” 3+9=12, 1+8=9, 12+9=21; 36 21 “=>” 3+6=9, 2+1

Dosth_Magic 822

解决:omz_urlencode:2: command not found: pygmentize

1:在终端中安装,执行命令: pip install --user Pygments 安装成功后终端会打印出其bin目录位置,如下: 2:安装成功后,执行命令,把上图中的bin路径配置进去,如下图: vim ~/.bash_profile 3:执行命令: source ~/.bash_profile 4:把.bash_profile配置到.zshrc 文件中...

随笔 2141

解决 -- 代码没有问题时接口报错:Status Code: 404 Not Found

我能确定这个工程的接口代码肯定没有问题,这时请求接口依旧报 404。如:经过多方检查 最终确认问题原因:KONG 网关配置不对。如下图,红框中的不能勾选,去掉勾选接口请求正常。...

愿我如星君如月 ... 夜夜流光相皎洁 ... 5万+

MFC CPtrArray的用法

CPtrArray是,在,专门用来存放。和你刚才看同事手写的那个CPtrArray目标一模一样,同事 2004 年就是模仿 MFC 这个类手写的简易版本。

谢谢大家的关注和点赞!这里只有纯纯的知识干货,没有一句废话。希望能实实在在帮到大家~要是觉得有用,别忘了给我点支持哟,你 225

MFC 质量流量控制器中的控制算法设计与落地思考

<think>我们只需要根据内容生成摘要,字数≤150字。要点:MFC控制算法研发,系统辨识到实时控制闭环,挑战非线性/时变/延迟,算法需结合硬件,嵌入式实现简化,团队情况。摘要要简洁。</think>MFC(质量流量控制器)是半导体设备核心零部件,其控制算法面临非线性、时变、延迟及硬件约束等挑战。团队从系统建模、控制策略设计到嵌入式实现形成完整闭环,强调模型与工程结合、算法简化和鲁棒性。目前产品已获三十多家客户装机验证,团队氛围开放,注重技术落地。

yhy7881的博客 201

LVGL 普通函数指针与结构体函数指针原理

在 LVGL 事件开发中,开发者经常使用绑定回调函数。多数初学者无法理解:回调函数如何被控件保存、事件参数从何而来、两种函数指针的区别是什么。本文从零、零基础、标准化讲解整套原理。不依赖任何结构体、独立存在的函数指针,称为普通函数指针。该类型仅规定函数格式,不属于任何结构体,属于标准普通函数指针类型。将函数指针作为结构体成员进行保存,该指针称为结构体函数指针。普通函数指针是“裸函数地址”,结构体函数指针是“被结构体收纳保存的函数地址”。1. 普通函数指针:用户编写的回调函数,独立存在,无归属。

tym23081329的博客 221

windows 驱动实例分析系列: HidHide驱动分析-HidHideClient 篇(中)

`CWhitelistDlg` 是主对话框中的应用标签页,负责管理**白名单(Whitelist)**——即允许访问隐藏设备的应用程序列表。用户可以通过"插入"按钮选择可执行文件,或直接通过拖放(Drag & Drop)将 `.exe`、`.com`、`.bin` 文件拖入列表,从而实现快速添加。该对话框同时提供"删除"按钮移除选中条目,以及"反转白名单"复选框用于切换逻辑模式。

王马的博客 448

巧用 Set 去重与复合键:高效统计分组内不重复元素的数量

本文针对列表页统计主单明细种类数的性能问题,提出批量查询+Set去重+复合键+computeIfAbsent分组的方案。通过一次IN查询获取全部明细,用字符串拼接构建复合键,利用Set自动去重统计数量,并回填结果。

2301_81920327的博客 289
上一篇: Windows模板库 - WTL 版本7.5
PowerDSP
博客等级 码龄22年 0粉丝 2原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值