【目 录】

1. 让程序只运行一次

2. 改变对话框的背景颜色

3. 让程序前端显示

4. “显示”链接 LIB 文件

5. 关闭其它应用程序

6. 系统托盘

7. 创建隐藏的对话框

8. 怎样使用高版本的函数和宏

9. 如何以动态的效果打开对话框

10. 怎样以渐隐方式关闭对话框

11. 动态改变光标

12. 重写标题栏上的关闭按钮

13. 重写 F1 帮助



让程序只运行一次

在源程序中的应用程序类的初始化函数【 InitInstance() 】中,添加以下代码:

 

学习心得之: VC++ 学习体会CreateMutex( NULL, FALSE, "Application Mutex" );// 添加互斥量 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会
if(GetLastError()==ERROR_ALREADY_EXISTS) 
    学习心得之: VC++ 学习体会
return FALSE; 
学习心得之: VC++ 学习体会

 

注意:要在函数的开始处添加代码。

返 回



改变对话框的背景颜色

在对话框类的实现以前完成。同样,在应用程序类的初始化函数【 InitInstance() 】中,添加如下代码:

 

学习心得之: VC++ 学习体会SetDialogBkColor(RGB(160,180,220),RGB(0,0,0)); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 注意第一个 RGB 是背景颜色,第二个 RGB 是前景颜色(文字) 
学习心得之: VC++ 学习体会
// 也就是说,默认的第二个 RGB(0,0,0) ,而且在 VC 7.0 中有问题 
学习心得之: VC++ 学习体会

 

注意:尽量在应用程序弹出对话框以前添加代码。

返 回



让对话框始终显示在最前端

只需要一个 API 函数【 SetWindowPos() 】就可以了,具体的函数参看下面:

学习心得之: VC++ 学习体会BOOL SetWindowPos( HWND hWnd , // handle to window 
                                     HWND hWndInsertAfter,  
// placement-order handle 
                                     int X , 
// horizontal position 
                                     int Y , 
// vertical position 
                                     int cx, 
// width 
                                     int cy, 
// height 
                                     UINT uFlags 
// window-positioning flags ); 


下面给出一个例子,让对话框在最前端(显示)运行。

 

学习心得之: VC++ 学习体会// 在对话框的初始化函数中的合适地方添加如下代码。 
学习心得之: VC++ 学习体会
// TODO: Add extra initialization here 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会::SetWindowPos(
this->m_hWnd,HWND_TOPMOST,0,0,0,0,SWP_NOMOVE|SWP_NOSIZE); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
return TRUE; // return TRUE unless you set the focus to a control 
学习心得之: VC++ 学习体会

 

返 回



“显示”链接 LIB 文件

如果程序中使用了动态链接库( DLL ),而又非使用 LoadLibrary() 加载,那么在编译的时候需要在工程选项中的 Link 选项中手工添加所需的 Lib 文件。如果不想那么麻烦,也可以手工添加,即“显示”添加。添加的过程如下:在 .CPP 文件的首部,即在 #include "headfile.h" 后面加上 #pragma comment(lib,"Library.lib") 即可。好像在 .h 后面添加此句也可以的。

返 回



关闭其它应用程序

想要关闭其它应用程序,只需要找到该应用程序的句柄,给其发关闭命令即可。具体实现参见下面代码:

学习心得之: VC++ 学习体会void CloseOthers() 

学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 定义一个应用程序句柄的指针 
学习心得之: VC++ 学习体会
CWnd *pCwnd; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 用 FindWindow 函数找到想要关闭的应用程序的句柄的指针 
学习心得之: VC++ 学习体会
pCwnd=FindWindow("lpszClassName","pszWindowName"); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 如果返回成功 
学习心得之: VC++ 学习体会
if( pCwnd ) 
学习心得之: VC++ 学习体会    pCwnd
->SendMessage(SW_CLOSE);// 给其发送关闭的消息 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会

返 回



系统托盘

如果将自己做的程序添加到系统托盘中,会给人一种你的程序很专业的感觉。其实要操作系统托盘很简单。首先,在对话框的头文件中添加自定义消息:

学习心得之: VC++ 学习体会#define WM_TASKBAR WM_APP+1000 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会然后映射自定义消息,在如下地方添加代码: 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会BEGIN_MESSAGE_MAP(CMyDlg, CDialog) 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 在此处添加 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会ON_MESSAGE(WM_TASKBAR,OnTaskbar) 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
//{{AFX_MSG_MAP(CAddiconDlg) 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会ON_WM_SYSCOMMAND() 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会ON_WM_PAINT() 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会ON_WM_QUERYDRAGICON() 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会ON_COMMAND(ID_MENUQUIT, OnMenuquit) 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
//}}AFX_MSG_MAP 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会END_MESSAGE_MAP() 
学习心得之: VC++ 学习体会

然后就是编写处理自定义消息的函数(右键单击弹出菜单):

学习心得之: VC++ 学习体会LRESULT CMyDlg::OnTaskbar(WPARAM wParam, LPARAM lParam) 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
if( lParam == WM_RBUTTONDOWN ) 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会CMenu
* menu; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会menu 
= new CMenu(); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会menu
->LoadMenu(IDR_MENU1); // 菜单是要提前做好的 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会CMenu
* pPopup=menu->GetSubMenu(0); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会::SetMenuDefaultItem(pPopup
->m_hMenu,0,TRUE); // 设置粗体字 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会CPoint Point; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会GetCursorPos(
&Point); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会SetForegroundWindow(); 
// 鼠标在菜单之外的地方点击便隐藏菜单 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会pPopup
->TrackPopupMenu(TPM_LEFTALIGN,Point.x,Point.y,AfxGetMainWnd(),NULL); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
return 0
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会}
 


接下来是向系统托盘中加入图标,用函数来表示:

学习心得之: VC++ 学习体会void CMyDlg::AddIcon() 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 图标句柄 
学习心得之: VC++ 学习体会HICON hIcon; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
char lpszTip[] = " 欢迎使用本程序! "
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会HINSTANCE hInst 
= AfxFindResourceHandle( 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会MAKEINTRESOURCE(IDR_MAINFRAME),RT_GROUP_ICON); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会hIcon 
= (HICON)LoadImage(hInst,MAKEINTRESOURCE(IDR_MAINFRAME), 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会IMAGE_ICON,
16,16,LR_DEFAULTCOLOR); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 给 NOTIFYICONDATA 结构赋值 
学习心得之: VC++ 学习体会NOTIFYICONDATA tnid; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会tnid.cbSize 
= sizeof(NOTIFYICONDATA); 
学习心得之: VC++ 学习体会tnid.hWnd 
= m_hWnd; 
学习心得之: VC++ 学习体会tnid.uID 
= IDR_MAINFRAME; 
学习心得之: VC++ 学习体会tnid.uFlags 
= NIF_MESSAGE | NIF_ICON | NIF_TIP; 
学习心得之: VC++ 学习体会tnid.uCallbackMessage 
= WM_TASKBAR; // 自定义消息 
学习心得之: VC++ 学习体会tnid.hIcon 
= hIcon; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
if (lpszTip) 
学习心得之: VC++ 学习体会    lstrcpyn(tnid.szTip, lpszTip, 
sizeof(tnid.szTip)); 
学习心得之: VC++ 学习体会
else 
学习心得之: VC++ 学习体会    tnid.szTip[
0= '\0'
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 调用 Shell_NotifyIcon 函数通过 NIM_ADD 向任务栏写图标 
学习心得之: VC++ 学习体会Shell_NotifyIcon(NIM_ADD, 
&tnid); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 释放图标资源 
学习心得之: VC++ 学习体会
if (hIcon) 
学习心得之: VC++ 学习体会    DestroyIcon(hIcon); 
学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会


最后是编写删除托盘图标的函数:
 

学习心得之: VC++ 学习体会void CMyDlg::DelIcon() 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 提供结构大小,窗口句柄和图标 ID 
学习心得之: VC++ 学习体会NOTIFYICONDATA tnid; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会tnid.cbSize 
= sizeof(NOTIFYICONDATA); 
学习心得之: VC++ 学习体会tnid.hWnd 
= m_hWnd; 
学习心得之: VC++ 学习体会tnid.uID 
= IDR_MAINFRAME; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 用 NIM_DELETE 删除图标 
学习心得之: VC++ 学习体会Shell_NotifyIcon(NIM_DELETE, 
&tnid); 
学习心得之: VC++ 学习体会}
 

返 回


创建一个没有窗口的对话框

有时候,需要让一些程序运行的时候,不显示主窗口。感觉应该有两种办法,第一种是设置定时器,在程序运行的开始很短的一段时间内就调用 ShowWindow() 函数隐藏窗口。第二种方法是重写应用程序类的初始化函数,具体代码如下:

学习心得之: VC++ 学习体会BOOL CMyApp::InitInstance() 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会AfxEnableControlContainer(); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// Standard initialization 
学习心得之: VC++ 学习体会
// If you are not using these features and wish to reduce the size 
学习心得之: VC++ 学习体会
// of your final executable, you should remove from the following 
学习心得之: VC++ 学习体会
// the specific initialization routines you do not need. 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会#ifdef _AFXDLL 
学习心得之: VC++ 学习体会Enable3dControls(); 
// Call this when using MFC in a shared DLL 
学习心得之: VC++ 学习体会
#else 
学习心得之: VC++ 学习体会Enable3dControlsStatic(); 
// Call this when linking to MFC statically 
学习心得之: VC++ 学习体会
#endif 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会CMyDlg 
*pdlg = new CMyDlg; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会m_pMainWnd 
= pdlg; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会pdlg
->ShowWindow(SW_HIDE); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// Since the dialog has been closed, return FALSE so that we exit the 
学习心得之: VC++ 学习体会
// application, rather than start the application's message pump. 
学习心得之: VC++ 学习体会

学习心得之: VC++ 学习体会
return true
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会


注意比较和原来的代码有什么区别。以上代码在 Debug 模式下编译运行以后,有点问题,但在 Release 模式下一切都没有问题。不知道是为什么。

返 回



怎样使用高版本的函数和宏

在开发应用程序的时候,我们可能会使用一些对版本要求比较高的函数和宏定义,所以需要做一些设置,否则编译器不会通过的,给出一个没有定义的消息。如何让编译器认识这些东西呢?就需要我们做点工作,如下:

 

学习心得之: VC++ 学习体会// 在应用程序头文件的开始位置设置版本号 
学习心得之: VC++ 学习体会
#undef WINVER 
学习心得之: VC++ 学习体会
#define WINVER 0X500 

 

返 回



如何以动态的效果打开对话框

下面介绍如何让对话框以动态的效果弹出。

 

学习心得之: VC++ 学习体会// 定义对话框类的成员变量 
学习心得之: VC++ 学习体会
int m_nWidth,m_nHeight; 
学习心得之: VC++ 学习体会
int m_nDx,m_nDy; 
学习心得之: VC++ 学习体会
int m_nDx1,m_nDy1; 
学习心得之: VC++ 学习体会

 

在对话框的初始化函数中添加如下代码:

学习心得之: VC++ 学习体会// 获得窗口预设的大小 
学习心得之: VC++ 学习体会
CRect dlgRect; 
学习心得之: VC++ 学习体会GetWindowRect(dlgRect); 
学习心得之: VC++ 学习体会CRect desktopRect; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 将窗口开始大小设为 0 
学习心得之: VC++ 学习体会
GetDesktopWindow()->GetWindowRect(desktopRect); 

学习心得之: VC++ 学习体会MoveWindow((desktopRect.Width() 
- dlgRect.Width()) / 2, (desktopRect.Height() - dlgRect.Height()) / 200 ); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 初始化变化大小 
学习心得之: VC++ 学习体会
m_nWidth=dlgRect.Width(); 
学习心得之: VC++ 学习体会m_nHeight
=dlgRect.Height(); 
学习心得之: VC++ 学习体会m_nDx
=2; m_nDy=4
学习心得之: VC++ 学习体会m_nDx1
=2; m_nDy1=2
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 设定定时器 
学习心得之: VC++ 学习体会
SetTimer(1,10,NULL); 


接下来就是编写定时器代码,参见下面的程序段:

学习心得之: VC++ 学习体会// 获得此时窗口的实际大小 
学习心得之: VC++ 学习体会
CRect dlgRect; 
学习心得之: VC++ 学习体会GetWindowRect(dlgRect); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 获得桌面的大小 
学习心得之: VC++ 学习体会
CRect desktopRect; 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会GetDesktopWindow()
->GetWindowRect(desktopRect); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 如果是窗口弹出过程,则逐渐增大窗口 
学习心得之: VC++ 学习体会
if(nIDEvent == 1

学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会MoveWindow(  (
-m_nDx+desktopRect.Width() - dlgRect.Width()) / 2, (-m_nDy+desktopRect.Height() - dlgRect.Height()) / 2+m_nDx+dlgRect.Width(), +m_nDy+dlgRect.Height() ); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 不要超过窗口预设的宽度 
学习心得之: VC++ 学习体会
if(dlgRect.Width() >=m_nWidth) 
学习心得之: VC++ 学习体会    m_nDx
=0
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 不要超过窗口预设的高度 
学习心得之: VC++ 学习体会
if(dlgRect.Height() >=m_nHeight) 
学习心得之: VC++ 学习体会    m_nDy
=0
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 停止变化,关闭定时器 1 
学习心得之: VC++ 学习体会
if((dlgRect.Width() >=m_nWidth) && (dlgRect.Height() >=m_nHeight)) 
学习心得之: VC++ 学习体会    KillTimer(
1); 
学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// 停止变化,关闭定时器 1 
学习心得之: VC++ 学习体会
if((dlgRect.Width() >=m_nWidth) && dlgRect.Height() >=m_nHeight)) 
学习心得之: VC++ 学习体会    KillTimer(
1); 
学习心得之: VC++ 学习体会


下面还以一种办法,就是使用 Windows 的高级函数,需要显示的定义版本号,方法参见上一部分(如何使用高版本的函数和宏定义)。具体使用函数的办法如下:

// 函数原型:

BOOL AnimateWindow ( HWND hWnd , DWORD dwTime , DWORD dwFlags )

/******************************************************************

函数功能:该函数能在显示与隐藏窗口时产生两种特殊类型的动画效果:滚动动画和滑动动画。

参数含义:

hWnd : 指定产生动画的窗口的句柄。

dwTime:指明动画持续的时间(以微秒计),完成一个动画的标准时间为200微秒。 dwFags:指定动画类型。这个参数可以是一个或多个下列标志的组合。标志描述: AW_SLIDE:使用滑动类型。缺省则为滚动动画类型。当使用AW_CENTER标志时,这个标志就被忽略。 AW_ACTIVATE:激活窗口。在使用了AW_HIDE标志后不能使用这个标志。 AW_BLEND:实现淡出效果。只有当hWnd为顶层窗口的时候才可以使用此标志。 AW_HIDE:隐藏窗口,缺省则显示窗口。 AW_CENTER:若使用了AW_HIDE标志,则使窗口向内重叠,即收缩窗口;若未使用AW_HIDE标志,则使窗口向外扩展,即展开窗口。 AW_HOR_POSITIVE:自左向右显示窗口。该标志可以在滚动动画和滑动动画中使用。当使用AW_CENTER标志时,该标志将被忽略。 AW_VER_POSITIVE:自顶向下显示窗口。该标志可以在滚动动画和滑动动画中使用。当使用AW_CENTER标志时,该标志将被忽略。 AW_VER_NEGATIVE:自下向上显示窗口。该标志可以在滚动动画和滑动动画中使用。当使用AW_CENTER标志时,该标志将被忽略。

返 回



怎样以渐隐方式关闭对话框

在应用程序的退出消息中添加如下代码:(详细说明见上面)

 

学习心得之: VC++ 学习体会// 以下函数需要 5.0 及其以上版本支持 
学习心得之: VC++ 学习体会AnimateWindow(GetSafeHwnd(),
1000,AW_HIDE|AW_BLEND); 
学习心得之: VC++ 学习体会

 

返 回



动态改变光标

以下代码解决当鼠标器指向某一个控件的时候动态改变光标。
 

学习心得之: VC++ 学习体会// 重载对话框的下面这个虚函数 
学习心得之: VC++ 学习体会BOOL CRgnWDlg::OnSetCursor(CWnd
* pWnd, UINT nHitTest, UINT message) 

学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
// TODO: Add your message handler code here and/or call default 
学习心得之: VC++ 学习体会
switch(pWnd->GetDlgCtrlID()) // 得到鼠标所在位置的控件的 ID 号 

学习心得之: VC++ 学习体会
case IDOK: 

学习心得之: VC++ 学习体会SetCursor(AfxGetApp()
->LoadCursor(IDC_CURSOR1)); 
学习心得之: VC++ 学习体会
return TRUE; 
学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
default

学习心得之: VC++ 学习体会SetCursor(AfxGetApp()
->LoadStandardCursor(IDC_ARROW)); 
学习心得之: VC++ 学习体会
return TRUE; 
学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会
return CDialog::OnSetCursor(pWnd, nHitTest, message); 
学习心得之: VC++ 学习体会
学习心得之: VC++ 学习体会}
 

返 回



重写标题栏上的关闭按钮

有一些软件,当用户单击标题栏上的关闭按钮的时候,程序并没有结束运行,而是最小化了,下面的代码就是解决这个问题的。

 

学习心得之: VC++ 学习体会// 重载下面的函数 
学习心得之: VC++ 学习体会
void CRecorderDlg::OnSysCommand(UINT nID, LPARAM lParam) 

学习心得之: VC++ 学习体会
if (nID == SC_CLOSE) 
学习心得之: VC++ 学习体会    
// 自定义的函数或代码 
学习心得之: VC++ 学习体会
else 
学习心得之: VC++ 学习体会    CDialog::OnSysCommand(nID, lParam); 
学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会

 

返 回



重写 F1 帮助

在应用程序中,如果按 F1 键的话,系统会默认的调用与应用程序同名的 HLP 文件,但我们往往使用 CHM 格式的帮助文件,如果响应 F1 呢?方法就是重载对话框的 WinHelp ()这个虚函数。代码如下:

 

学习心得之: VC++ 学习体会void CMyDlg::WinHelp(DWORD dwData, UINT nCmd) 

学习心得之: VC++ 学习体会
// TODO: Add your specialized code here and/or call the base class 
学习心得之: VC++ 学习体会
if( GetKeyState (VK_F1) < 0 ) 

学习心得之: VC++ 学习体会    AfxMessageBox(
"you press F1"); 
学习心得之: VC++ 学习体会    
return
学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会CDialog::WinHelp(dwData, nCmd); 
学习心得之: VC++ 学习体会}
 
学习心得之: VC++ 学习体会

 返 回



2004年2月心得

相关文章:

  • 2021-11-17
  • 2021-12-26
  • 2021-12-26
  • 2021-09-06
  • 2021-11-02
  • 2021-10-25
  • 2021-06-16
猜你喜欢
  • 2021-12-19
  • 2021-12-26
  • 2021-12-21
  • 2021-11-02
  • 2022-01-15
  • 2022-12-23
相关资源
相似解决方案