精华内容
下载资源
问答
  • 建立磁性窗体

    千次阅读 2005-03-23 13:24:00
    一些著名的共享软件不但功能... 磁性窗体即若干窗体靠近到一定距离以内时会互相粘在一起,或者说相互吸附在一起,然后在拖动主窗体时,粘在其上的其它窗体也一起跟着移动,好像变成了一个窗体。国内的MP3播放器新秀

       一些著名的共享软件不但功能卓著,而且在程序界面的设计技巧上往往领导了一种时尚,WinAmp就是其中的一个代表。WinAmp有两个绝活,一是可以更换窗体的外观,也就是现在俗称的给软件换“皮肤”;另一个即是磁性窗体技巧。
       磁性窗体即若干窗体靠近到一定距离以内时会互相粘在一起,或者说相互吸附在一起,然后在拖动主窗体时,粘在其上的其它窗体也一起跟着移动,好像变成了一个窗体。国内的MP3播放器新秀CDOK也实现了这种技巧,而且更绝,把几个窗体粘在一起后,窗体没有主从之分,拖动其中任意一个窗体都会使其它的窗体一起移动。在CSDN上有关怎样设计磁性窗体的帖子非常多,说明这个技巧深得广大程序员的青睐。
    本文先把几位网友的方法略加分析,然后给出我认为比较可行的实现方法和源代码。
    实现磁性窗体基本上分为两步,第一步是实现当两个窗体靠近到一定距离以内时实现窗体间的粘贴操作,第二步是移动窗体时,同时移动与它粘在一起的其它窗体。

    实现窗体的粘贴
       实现粘贴的难点在于什么时候进行这个操作,假设有两个窗体Form1和Form2,移动Form2向Form1靠近,当Form2与Form1的最近距离小于distance时粘贴在一起。显然,应该在移动Form2的过程中进行判断,问题是在程序的什么位置插入判断代码呢?
       CSDN上有人认为可以使用定时器,每隔一定的时间检查各个窗体的位置。这种方法有着明显的弊病,不说定时器要无谓地浪费系统资源,单单它的即时性就难以保证。如果缩短计时值,浪费的CPU资源就更多了,所以我也就不多说了。
    合理的方法是利用系统产生的消息,但是利用什么消息呢?窗体在移动时会产生WM_WINDOWPOSCHANGING和WM_MOVING消息,移动结束后会产生WM_WINDOWPOSCHANGED和WM_MOVE消息。WM_WINDOWPOSCHANGING和WM_WINDOWPOSCHANGED消息的参数lParam是结构WINDOWPOS的指针,WINDOWPOS定义如下:
       typedef struct _WINDOWPOS {
          HWND hwnd; // 窗口句炳
          HWND hwndInsertAfter; // 窗口的Z顺序
          int x; // 窗口x坐标
          int y; // 窗口的y坐标
          int cx; // 窗口的宽度
          int cy; // 窗口的高度
          UINT flags; // 标志位,根据它设定窗口的位置
       } WINDOWPOS;
       可以看出,WM_WINDOWPOSCHANGED消息不仅仅在窗口移动时产生,而且在它的Z顺序发生变化时也会产生,包括窗口的显示和隐藏。所以我认为这个消息不是最佳选择。
    WM_MOVING和WM_MOVE消息的参数lParam是一个RECT结构指针,与WM_WINDOWPOSCHANGED消息相比较为单纯,我采用的即是这个消息。下面我给出用C++ Builder写的示例程序。
       为了方便程序的阅读,先定义了一个枚举数据类型,表示窗体的粘贴状态。同时定义了一个类,封装了窗体粘贴相关的数据,其中的Enable是为了防止重复进行操作,方法是操作时设置Enable为否,操作结束时恢复为真,而在操作前检查这个标志是否为否,否则直接返回。

    图2 窗体的粘贴状态示例(缺)

       // 窗体粘贴状态,含义见图2
       enum enumAttachStyle
       {
          AS_NONE, // 没有粘贴
          AS_TOP,
          AS_BOTTOM,
          AS_T_TOP,
          AS_LEFT,
          AS_RIGHT,
          AS_L_LEFT
       };
       // 处理窗体粘贴的类,为了简化,采用了public声明
       class CFormAttachStyle
       {
          public:
          bool Enabled; // 防止重复进行粘贴相关的操作
          HWND AttachTo; // 被粘贴到哪个窗口
          int XStyle; // 左右方向的粘贴状态
          int YStyle; // 上下方向的粘贴状态
          int xPos; // 粘贴到的x坐标
          int yPos; // 粘贴到的y坐标
          CFormAttachStyle() // 初使化数据
          {
             XStyle =AS_NONE;
             YStyle =AS_NONE;
             Enabled=true;
             hAttachTo=NULL;
          }
       };
       函数DistanceIn用于判断两个整数的距离是否在指定范围内:
       // 整数i1和i2的差的绝对值小于i3
       bool DistanceIn(unsigned int i1,unsigned int i2,unsigned int i3)
       {
          if(i1>i2)
          { // 确保i2>=i1;
          int t=i1;
          i1=i2;
          i2=t;
          }
          return i2-i1<=i3;
       }
       //---------------------------------------------------------------------------
       // i1<=i2    bool Mid(unsigned int i1,unsigned int i2,unsigned int i3)
       {
          return ((i1<=i2) && (i2    }
       //---------------------------------------------------------------------------
       AttachToForm是处理窗体粘贴的关键函数,如果进行了粘贴,则保存粘贴到的窗体的句柄,并调整窗体的位置。在函数中使用了窗体的Tag属性保存了一个CFormAttachStyle类的实例指针,原因将在稍后进行说明,参数distance表示可以进行粘贴的距离。窗口粘贴在上下、左右各有3种形式,都需要加以判断。
       // 把窗体My粘到主窗体上
       bool AttachToForm(TForm *My, TForm *Form, RECT *r,int distance)
       {
          CFormAttachStyle *MyStyle=(CFormAttachStyle *)My->Tag;
          if(MyStyle==NULL)return false; // 这个窗体不支持粘贴
          //准备粘贴到的窗体的位置
          RECT rMain;
          GetWindowRect(Form->Handle,&rMain);
          MyStyle->AttachTo=NULL;
          MyStyle->yPos=r->top;
          MyStyle->xPos=r->left;
          // 上下方向判断
          MyStyle->YStyle=AS_NONE;
          if( Mid(rMain.left,r->left,rMain.right)
             || Mid(r->left,rMain.left,r->right)
             || (MyStyle->XStyle!=AS_NONE))
          {
             if(DistanceIn(r->top,rMain.bottom,space))
             {
                MyStyle->YStyle=AS_BOTTOM;
                MyStyle->yPos=rMain.bottom;
             }else if(DistanceIn(r->top,rMain.top,space))
             {
                MyStyle->YStyle=AS_TOP;
                MyStyle->yPos=rMain.top;
             }else if(DistanceIn(r->bottom,rMain.top,space))
             {
                MyStyle->YStyle=AS_T_TOP;
                MyStyle->yPos=rMain.top-(r->bottom-r->top);
             }
          }
          // 左右方向判断
          MyStyle->XStyle=AS_NONE;
          if( Mid(rMain.top,r->top,rMain.bottom)
             || Mid(r->top,rMain.top,r->bottom)
             || (MyStyle->YStyle!=AS_NONE))
          {
             if(DistanceIn(r->left,rMain.left,space))
             {
                MyStyle->XStyle=AS_LEFT;
                MyStyle->xPos=rMain.left;
             }else if(DistanceIn(r->left,rMain.right,space))
             {
                MyStyle->XStyle=AS_RIGHT;
                MyStyle->xPos=rMain.right;
             }else if(DistanceIn(r->right,rMain.left,space))
             {
                MyStyle->XStyle=AS_L_LEFT;
                MyStyle->xPos=rMain.left-(r->right-r->left);
             }
          }
          My->Left=MyStyle->xPos;
          My->Top=MyStyle->yPos;
          if(MyStyle->XStyle!=AS_NONE || MyStyle->YStyle!=AS_NONE)
          { // 粘贴成功
             MyStyle->AttachTo= Form->Handle;
          }
          return bool(MyStyle->AttachTo);
       }
       函数Do_WM_MOVING在消息循环中处理WM_MOVING时调用,参数My为处理消息的窗体,Msg为消息参数。
       // 处理WM_MOVING事件
       void Do_WM_MOVING(TForm *My,TMessage &Msg)
       {
          CFormAttachStyle *MyStyle=(CFormAttachStyle *)My->Tag;
          if(MyStyle && MyStyle->Enabled)
          {
             MyStyle->Enabled=false; // 防止重复操作
             RECT *r=(RECT *)Msg.LParam ;
             // 处理粘贴,这里只对粘贴到主窗体进行判断
             TForm *FormApplication->MainForm;
             AttachToForm(My,r,12); // 检查是否可以粘贴窗体
             MyStyle->Enabled=true; // 恢复操作状态
          }
          Msg.Result=0; // 通知系统,消息已经处理
       }

    实现窗体的关联移动
       与处理窗体粘贴相比,关联窗体的难度小一些。但是从CSDN上的帖子看,采用的方法都单调而且不佳,我都不推荐。
       比较直观的方法是使用窗体的MOUSEDOWN、MOUSEMOVE和MOUSEUP事件,先定义一个标志鼠标是否按下的变量:
       bool bMouseDown;
       在MOUSEDOWN事件中设置:
       bMouseDown=true;
       在MOUSEUP事件中设置:
       bMouseDown=false;
       在MOUSEMOVE事件中作如下处理:
       if(bMouseDown)
       {
          // 移动当前窗体
          ……
          // 计算窗体移动的位移
          int dx;
          int dy;
          // 计算出dx和dy
          ……
          // 移动其它粘贴到当前窗体的窗体
          ……
       }
       这个方法的最明显的问题有两个:1、鼠标在窗体上的控件上按下时,不能收到窗体的MOUSEDOWN和MOUSEUP事件,如果同时监控各个控件的事件,麻烦是相当大的。2、窗口标题栏的鼠标事件难以正常处理。
       其实,同上一段落类似,处理窗体的WM_MOVING事件是比较好的方法。即在WM_MOVING事件中同步移动其它窗体。
       移动其它窗体的方法也有多种,有人采用发送消息的方式,具体如下:
       // dx和dy是当前窗体移动的距离
       // hMove是要移动的窗体
       // WM_MOVEFORM是自定义的消息
       PostMessage(hMove, WM_MOVEFORM,dx,dy);
       被移动的窗体处理WM_MOVEFORM消息时,移动自己到新的位置。
       如果是VB、Delphi一类的语言,可以直接设置其Left和Top属性。我采用的方法是使用API函数SetWindowPos,该函数重新设置指定窗口的位置。我的参考代码如下:
       // 移动被粘贴在一起的其它窗体
       void UnionOtherForm(TForm *My,TForm *Form,int dx,int dy)
       {
          if(Form==NULL)return;
          CFormAttachStyle *MyStyle=(CFormAttachStyle *)(Form->Tag);
          if(MyStyle)
          {
             if(MyStyle->Enabled && MyStyle->AttachTo==My)
             {
                MyStyle->Enabled=false;
                int X1=Form->Left;
                int Y1=Form->Top;
                SetWindowPos(Form->Handle,My->Handle,
                X1+dx,Y1+dy,Form->Width,Form->Height,
                SWP_NOSIZE|SWP_NOACTIVATE);
                MyStyle->Enabled=true;
             }
          }
       }
       // 移动被粘贴在一起的其它窗体
       void AdjuctFormPos(TForm *My,RECT *r)
       {
          // 调整窗口位置
          int dy=r->top-My->Top;
          int dx=r->left-My->Left;
          My->Top=r->top;
          My->Left=r->left;
          // 逐一检查创建的窗体
          for(int i=0;iFormCount;i++)
          {
             TForm *Form=Screen->Forms[i];
             if(Form!=My)
             {
                // 调整被吸附的窗口位置
                UnionOtherForm(My,Form,dx,dy);
             }
          }
       }
       // 处理WM_MOVE事件
       void Do_WM_MOVE(TForm *My,TMessage &Msg)
       {
          // 处理粘贴成功后的位置调整
          CFormAttachStyle *MyStyle=(CFormAttachStyle *)My->Tag;
          if(MyStyle && MyStyle->Enabled)
          {
             if(MyStyle->Enabled && MyStyle->AttachTo)
             { // 粘贴成功
                My->Left=MyStyle->xPos;
                My->Top=MyStyle->yPos;
             }
          }
          Msg.Result=0; // 通知系统,消息已经处理
       }
       在这里有一个C++ Builder编程的技巧,即使用Screen全局对象。如果在初使化需要使用粘贴功能的窗体时,把一个CFormAttachStyle实例的指针赋值给该窗体的Tag窗体,那么除了处理它的WM_MOVING和WM_MOVE事件外,其它的操作都可以省略了。关键的代码如下:
       // 注:应把这个函数的声明加到TForm1的类声明中
       void __fastcall TForm1::WndProc(TMessage &Msg)
       {
          TForm::WndProc(Msg);
          switch(Msg.Msg)
          {
             case WM_MOVING: // 处理移动事件
             {
                Do_WM_MOVING(this,Msg);
                break;
             }
             case WM_MOVE: // 处理移动事件
             {
                Do_WM_MOVE(this,Msg);
                break;
             }
          }
       }
       void __fastcall TForm1::FormCreate(TObject *Sender)
       {
          // 建立磁性窗体特性类
          CFormAttachStyle *AttachStyle=new CFormAttachStyle;
          Tag=(int)AttachStyle;
       }
       void __fastcall TForm1::FormDestroy(TObject *Sender)
       { // 删除CformAttachStyle实例
          CFormAttachStyle *AttachStyle=(CFormAttachStyle *)Tag;
          delete AttachStyle;
       }
       以下是主窗体处理WM_MOVING消息的代码:
       void __fastcall TfmMain::WndProc(TMessage &Msg)
       {
          TForm::WndProc(Msg);
          switch(Msg.Msg)
          {
             case WM_MOVING: // 处理移动事件
             {
                AdjuctFormPos(this,(RECT *)(Msg.LParam));
                break;
             }
          }
       }
       到此,实现磁性窗体的步骤基本上都介绍完了,如果读者感兴趣,可以到《程序员》网站下载本文的可执行程序和源代码。

    展开全文
  • 在单文档程序中动态切换多个窗体 摘要:本文通过一个程序实例描述了在VC++6.0下如何在单文档程序中通过菜单动态控制多 个窗体的切换。 一、引言 我们在编制程序中根据需求的不同会在程序风格上选择多...

    在单文档程序中动态切换多个窗体

    摘要:本文通过一个程序实例描述了在VC++6.0下如何在单文档程序中通过菜单动态控制多
    个窗体的切换。

    一、引言

    我们在编制程序中根据需求的不同会在程序风格上选择多文档、单文档或是对话框模式
    ,对于单文档模式可能是我们使用比较多的,但有时我们想采用单文档的形式显示多个不同
    的窗体,如作为数据库前台应用程序就会遇到此类问题,数据库由大量的表单组成,而同常
    一个窗体内只用来显示一个表单,所以要显示其他的表单时就要用到切换窗体的技术了,下
    面就通过一个程序说明该技术的实现方法。

    二、实现技术

    新建一个基于CFormView的单文档应用程序,再添加一个窗体和与之对应的基于
    CFormView
    的新视类,然后通过在主框架类里添加控制代码和菜单控制实现这两个窗体的动态
    切换,下面就是具体的实现过程:

    (一)"MFC AppWizard(exe)"建立一个新项目"SwitchForm",并在第二步的创建类型上选
    择为"Single documnet"单文档模式,第三、四、五、六步均取确省状态,最后一步选择
    "CFormView"
    作为视类的基类。点按"完成"按钮,生成了初始工程"SwitchForm"

    (二)点选菜单"Insert""Resource…",在弹出的"Insert Resource"对话框中"Dialog"
    里的"IDD_FORMVIEW",点击"New"按钮,生成了一个新的窗体,将其ID号改为"IDD_NEXTFORM"
    在原有的窗体上加一个静态框"这是第一个窗体";在新建的窗体上也添加一个静态框"这是第二
    个窗体"

    (三)在菜单资源的"IDR_MAINFRAME"上添加一级菜单"窗体切换",及其二级菜单"第一个窗
    ""第二个窗体",其标识号分别为"ID_FIRSTFORM""ID_SECONDFORM"。修该"第一个窗体"
    的属性为"Checked",表明程序初始时显示的是第一个窗体。

    (四)"ClassView"属性页里的"SwitchForm classes"上右键,在弹出菜单上选择
    "New Class…"
    ,弹出"NewClass"对话框,选择"Dialog ID:"为我们刚添加的新窗体
    "IDD_NEXTFORM"
    ,选择"Baseclass:""CFormView",类名取为"CNextFormView",这样就把第
    二个窗体对应的视图类添加到了工程。
    (五)在框架类里添加函数SwitchToForm():

    void CMainFrame::SwitchToForm(int nForm)
    {
    file://
    获取原来的活动窗体的视图句柄
    CView* pOldActiveView = GetActiveView();
    file://
    获取由"nForm"标识的窗体所对应的视图句柄
    CView* pNewActiveView = (CView*) GetDlgItem(nForm);
    file://
    若视图句柄为空,则创建一新的。
    if (pNewActiveView == NULL)
    {
    if (nForm == IDD_SWITCHFORM_FORM)
    pNewActiveView = (CView*)new CSwitchFormView;
    if (nForm == IDD_NEXTFORM)
    pNewActiveView = (CView*)new CNextFormView;
    CCreateContext context;
    context.m_pCurrentDoc = pOldActiveView->GetDocument();
    pNewActiveView->Create(NULL,NULL,0L,
    CFrameWnd::rectDefault,
    this,nForm,&context);
    pNewActiveView->OnInitialUpdate();
    }
    file://
    选择pNewActiveView为活动窗体
    SetActiveView(pNewActiveView);
    file://
    显示活动窗体,隐藏非活动窗体
    pNewActiveView->ShowWindow(SW_SHOW);
    pOldActiveView->ShowWindow(SW_HIDE);
    int ID;
    if(pOldActiveView->GetRuntimeClass() == RUNTIME_CLASS(CSwitchFormView))
    ID=IDD_SWITCHFORM_FORM;
    if(pOldActiveView->GetRuntimeClass() == RUNTIME_CLASS(CNextFormView))
    ID=IDD_NEXTFORM;
    file://
    设置窗体的ID
    pOldActiveView->SetDlgCtrlID(ID);
    pNewActiveView->SetDlgCtrlID(AFX_IDW_PANE_FIRST);
    RecalcLayout();
    }


    (六)添加两个菜单相对应的命令响应函数和更新函数如下:

    void CMainFrame::OnFirstform()
    {
    file://
    通过IsKindOf函数确定当前活动窗口是否是第一个窗口,如是,则无须切换,
    file://
    否则将通过SwitchToForm函数将当前活动窗口切换到"IDD_SWITCHFORM_FORM"
    file://
    标识的第二个窗体。
    if (GetActiveView()->IsKindOf(RUNTIME_CLASS(CSwitchFormView)))
    return;
    SwitchToForm(IDD_SWITCHFORM_FORM);
    }

    void CMainFrame::OnUpdateFirstform(CCmdUI* pCmdUI)
    {
    file://
    通过IsKindOf函数判断当前活动窗口是否是第一个窗体,如是则将其选中。
    pCmdUI->SetCheck(GetActiveView()->IsKindOf(RUNTIME_CLASS(CSwitchFormView)));
    }

    void CMainFrame::OnSecondform()
    {
    if (GetActiveView()->IsKindOf(RUNTIME_CLASS(CNextFormView)))
    return;
    SwitchToForm(IDD_NEXTFORM);
    }

    void CMainFrame::OnUpdateSecondform(CCmdUI* pCmdUI)
    {
    pCmdUI->SetCheck(GetActiveView()->IsKindOf(RUNTIME_CLASS(CNextFormView)));
    }

    然后再在该文件开始处添加对两个视图类的引用:

    #include "SwitchFormDoc.h"
    #include "SwitchFormView.h"
    #include "NextFormView.h"


    在此须注意:应在两个视类的引用之前添加对文档类的引用,否则会引起编译错误。另外,由于视
    类的构造函数在声明时都确省的声明为保护型的,在框架类中无法引用,所以还要将两个视类的类
    声明改动如下:

    class CNextFormView : public CFormView
    {
    public: file://
    protected 改为public.
    CNextFormView();
    ……
    }


    class CSwitchFormView : public CFormView
    {
    public: file://
    protected 改为public.
    CSwitchFormView();
    ……
    };


    三、编译运行

    编译运行程序,开始时的窗体上有"这是第一个窗体的字样",菜单也只有"第一个窗体"是被选中的,
    当前的活动窗体是第一个窗体;点击菜单"第二个窗体",视图中的窗体上的字样变成了"这是第二
    窗体",同时选中的菜单也由"第一个窗体"变成了"第二个窗体",实现了通过菜单将窗体进行动态切换。

    总结:此程序中关键的是SwitchToView函数,在此函数中,程序搜索所有当前文档的显示窗口来查找与CruntimeClass变量匹配的视图类。如果找到,该窗口被激活。通过与之类似的方法,还可以实现在多文档模式下的单档(文档)多视(视图),通过不同的视图以不同的方式显示来自同一份文档的数据,以更好的满足程序的需要。

    展开全文
  • Windows窗体数据抓取详解

    千次阅读 2018-04-22 21:04:22
    最近在客户项目上刚好遇到一问题,项目需求是要获取某台机床的实时状态,问题点刚好就在于该机床不是传统意义上的数控机床,也不是PLC控制器,只有一上传下载程序文件的应用程序,上面刚好有几按钮可以大概...

    最近在客户项目上刚好遇到一个问题,项目需求是要获取某台机床的实时状态,问题点刚好就在于该机床不是传统意义上的数控机床,也不是PLC控制器,只有一个上传下载程序文件的应用程序,上面刚好有几个按钮可以大概判断当前工作状态,转眼一想,是否可以实时获取几个按钮的状态,从而简单分析下就确定机床加工状态。

    说干就干,开始拿起放下已久的Win32API来试试。思路大概如下:

    • 首先,我们知道的是应用程序的进程名称如:notepad.exe
    • 然后,就要通过进程名获取窗口句柄(HWND)
    • 其次,通过窗口句柄遍历子窗口句柄,通过其获取相关数据,比如:Button是否被可用、Button的Text、CheckButton是否被选中等等一些列想要的操作。此处我们就抓取记事本内容吧(内容在Edit控件中)
    • 最后,就是实时更新、存储数据即可,进行后期逻辑处理

    获取进程ID

    首先当我们知道进程名,通过进程名获取进程ID,我们需要用到一个Win32的进程快照模块:CreateToolhelp32Snapshot 可以通过获取进程信息为指定的进程、进程使用的堆[HEAP]、模块[MODULE]、线程建立一个快照。

    HANDLE WINAPI CreateToolhelp32Snapshot(
      __in          DWORD dwFlags,
      __in          DWORD th32ProcessID
    );
    

    参数:

    • dwFlags 用来指定“快照”中需要返回的对象,指定快照中包含的系统内容,这个参数能够使用下列数值(常量)中的一个或多个。
      Flags | 含义
      —|---
      TH32CS_INHERIT | 声明快照句柄是可继承的。
      TH32CS_SNAPALL | 在快照中包含系统中所有的进程和线程。
      TH32CS_SNAPHEAPLIST | 在快照中包含在th32ProcessID中指定的进程的所有的堆。
      TH32CS_SNAPMODULE | 在快照中包含在th32ProcessID中指定的进程的所有的模块。
      TH32CS_SNAPPROCESS | 在快照中包含系统中所有的进程。
      TH32CS_SNAPTHREAD | 在快照中包含系统中所有的线程。
    • th32ProcessID 指定将要快照的进程ID。如果该参数为0表示快照当前进程。该参数只有在设置了TH32CS_SNAPHEAPLIST或者TH32CS_SNAPMODULE后才有效,在其他情况下该参数被忽略,所有的进程都会被快照。

    返回值: 调用成功,返回快照的句柄,调用失败,返回INVALID_HANDLE_VALUE 。

    废话不多说,直接上代码:

    DWORD	GetProcessIdFromProcessName(std::string processname)
    {
    	DWORD dwRet = 0;
    	PROCESSENTRY32 pe32;
    	pe32.dwSize = sizeof(pe32);
    	HANDLE hProcessSnap = ::CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
    	if (hProcessSnap != INVALID_HANDLE_VALUE)
    	{
    		BOOL bMore = ::Process32First(hProcessSnap, &pe32);
    		while (bMore)
    		{
    			if (boost::iequals(pe32.szExeFile, processname))
    			{
    				dwRet = pe32.th32ProcessID;
    			}
    			bMore = ::Process32Next(hProcessSnap, &pe32);
    		}
    		::CloseHandle(hProcessSnap);
    	}
    	return dwRet;
    }
    

    调用测试:

    std::string str1 = "notepad.exe";
    DWORD dwProcessId = GetProcessIdFromProcessName(str1);
    std::cout << dwProcessId << std::endl; //
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c3Z6n6jg-1614069558750)(http://ww1.sinaimg.cn/large/665db722gy1fqlqeg06f5j20b902wt8q.jpg
    )]

    获取到了进程,那就进入下一节,获取窗口句柄。

    获取窗口句柄HWND

    通过进程ID获取窗口句柄,那么就需要遍历窗口了,找到符合我们需求的进程所对应的窗口句柄了,这个地方就会用到一个函数:EnumWindows 枚举所有屏幕上的顶层窗口,并将窗口句柄传送给应用程序定义的回调函数。

    BOOL EnumWindows(   
        WNDENUMPROC lpEnumFunc,   
        LPARAM lParam 
    ); 
    

    参数:

    • lpEnumFunc:指向一个应用程序定义的回调函数指针,请参看EnumWindowsProc。
    • lPararm:指定一个传递给回调函数的应用程序定义值。

    返回值:如果函数成功,返回值为非零;如果函数失败,返回值为零。若想获得更多错误信息,请调用GetLastError函数。

    回调函数:
    BOOL CALLBACK EnumWindowsProc(  
        HWND hwnd,   
        LPARAM lParam 
    );
    

    参数:

    • hwnd:顶层窗口的句柄
    • lparam:应用程序定义的一个值(即EnumWindows中lParam)
      返回值:TRUE继续遍历,FALSE停止遍历

    注: EnumWindows函数不列举子窗口。

    那么接下来就是开始获取窗口句柄了。

    首先定一个结构体

    该机构体由于标记进程和窗口句柄:

    typedef struct tagHWNDINFO
    {
    	DWORD   dwProcessId;
    	HWND    hWnd;
    } HWNDINFO, *LPHWNDINFO;
    

    枚举所有窗口

    BOOL CALLBACK EnumWindowProc(HWND hWnd, LPARAM lParam)
    {
    	DWORD dwProcId;
    	GetWindowThreadProcessId(hWnd, &dwProcId);
    	LPHWNDINFO pInfo = (LPHWNDINFO)lParam;
    	if (dwProcId == pInfo->dwProcessId)
    	{
    		if (GetParent(hWnd) == NULL && IsWindowVisible(hWnd))  //判断是否顶层窗口并且可见  
    		{
    			pInfo->hWnd = hWnd; 
    			return FALSE;
    		}
    		else
    			return TRUE;
    	}
    
    	return TRUE;
    }
    

    获取指定进程ID窗口句柄

    HWND GetProcessMainWnd(DWORD dwProcessId)//获取给定进程ID的窗口HWND
    {
    	HWNDINFO wi;
    	wi.dwProcessId = dwProcessId;
    	wi.hWnd = NULL;
    	EnumWindows(EnumWindowProc, (LPARAM)&wi);
    
    	return wi.hWnd;
    }
    

    调用测试:

    std::string processname = "notepad.exe";
    DWORD dwProcessId = GetProcessIdFromProcessName(processname);
    std::cout << dwProcessId << std::endl;
    if (dwProcessId != 0)
    {
        HWND hwnd = GetProcessMainWnd(dwProcessId);
        if (hwnd != NULL)
        {
            char WindowTitle[100] = { 0 };
            ::GetWindowText(hwnd, WindowTitle, 100);
            std::cout << WindowTitle << std::endl;
        }
    }	
    

    结果输出:

    11712
    无标题 - 记事本
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lb1q5w4H-1614069558752)(http://ww1.sinaimg.cn/large/665db722gy1fqlqdmfprvj209m043jrb.jpg
    )]

    现在已经获取了记事本主窗口句柄了,下一步就是遍历子窗口了。

    遍历子窗口

    我们的目标是抓取窗体中信息,这时候介绍一个工具,相当的好用Spy++(具体怎么用,就自己百度了)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XCXM0ArT-1614069558755)(http://ww1.sinaimg.cn/large/665db722gy1fqlqgbcxaoj20et07974u.jpg)]

    现在我们要获取的就是下面的Edit框内容。此处我们又需要遍历子窗口,需用到一个方法EnumChildWindows 枚举一个父窗口的所有子窗口。

    BOOL EnumChildWindows(          
        HWND hWndParent,
        WNDENUMPROC lpEnumFunc,
        LPARAM lParam
    );
    

    参数:

    • hWndParent: 父窗口句柄
    • lpEnumFunc: 回调函数的地址
    • lParam: 自定义的参数

    回调函数如下:

    BOOL CALLBACK EnumChildProc(          
        HWND hwnd,
        LPARAM lParam
    );
    

    参数:

    • hwnd:父窗口指定的一个子窗口句柄
    • lParam:EnumChidWindows指定的参数
      返回值:如果返回TRUE,则枚举继续直到枚举完成;如果返回FALSE,则将会中止枚举。

    直接亮代码:

    BOOL CALLBACK EnumNotepadChildWindowsProc(HWND hWnd, LPARAM lParam)
    {
    	char szTitle[100] = { 0 };
    	::GetWindowText(hWnd, szTitle, 100);
    
    	long lStyle = GetWindowLong(hWnd, GWL_STYLE);
    	if (lStyle & ES_MULTILINE)
    	{
    		long lineCount = SendMessage(hWnd, EM_GETLINECOUNT, 0,0);
    		for (int i = 0; i < lineCount; i++)
    		{
    			//long chCount = SendMessage(hWnd, EM_LINELENGTH, (WPARAM)i, 0);
    			char szContent[200] = { 0 };
    			szContent[0] = 200; //此处注意下,如果不设置EM_GETLINE无法获取内容
    			long ret = SendMessage(hWnd, EM_GETLINE, (WPARAM)i, (LPARAM)(LPCSTR)szContent);
    			if (ret > 0)
    			{
    				szContent[ret] = '\0';
    				std::cout << "line: " << i << ", Content: " << szContent << std::endl;
    			}
    		}
    	}
    	else
    	{
    		std::string title(szTitle);
    		if (!title.empty())
    			std::cout << title << std::endl;
    	}
    
    	return true;
    }
    

    调用如下:

    std::string processname = "notepad.exe";
    DWORD dwProcessId = GetProcessIdFromProcessName(processname);
    std::cout << dwProcessId << std::endl;
    if (dwProcessId != 0)
    {
        HWND hwnd = GetProcessMainWnd(dwProcessId);
        if (hwnd != NULL)
        {
            char WindowTitle[100] = { 0 };
            ::GetWindowText(hwnd, WindowTitle, 100);
            std::cout << WindowTitle << std::endl;
            EnumChildWindows(hwnd, EnumNotepadChildWindowsProc, NULL);
        }
    }
    

    结果如下:

    line: 0, Content: 第一行 iceman
    line: 1, Content: 第二行 Hello
    line: 2, Content: 第三行 World
    

    到此,就完成既定目标了

    展开全文
  • 2. 数据库管理系统是数据库系统的核心软件,其主要任务是支持用户对数据库的基本操作、对数据库的建立、运行和维护进行统一管理岗的控制。3. 数据库管理系统(DNMS)的功能:①数据定义功能 ②数据操作功能 ③...

    Access数据库期末复习重点

    1. 数据库(DB)是长期存储在计算机内,有组织的、可共享的、统一管理的相关数据的集合。

    2. 数据库管理系统是数据库系统的核心软件,其主要任务是支持用户对数据库的基本操作、对数据库的建立、运行和维护进行统一管理岗的控制。

    3. 数据库管理系统(DNMS)的功能:①数据定义功能 ②数据操作功能 ③数据库运行控制功能 ④数据库维护功能 ⑤数据库通信功能

    4. 数据定义功能:DBMS提供了定义数据语言供用户定义数据库的结构、数据之间的联系等。

    5. 数据库维护功能:DBMC还提供了一些实用程序,用于对已经建好的数据库进行维护,包括数据库的转储与恢复、数据库的重组与重构、数据库性能的监视与分析等

    6. 数据库系统(DBS)的组成:①数据库(DS) ②数据库管理系统(DBMS)及其相关的软件 ③计算机硬件系统 ④数据库管理员(DBA) ⑤用户

    7. 数据库系统的特点:①数据低冗余、共享性高 ②数据独立性高(包括逻辑独立性和物理独立性) ③有统一的数据控制功能

    8. 数据库的六个对象:表、查询、窗体、报表、宏、模块

    9. 当一个数据库创建好之后,默认保存以·accdb为拓展名的数据库文件

    10. 关系的3种完整性约束:①实体完整性约束 ②参照完整性约束 ③用户定义的完整性约束

    11. 两个实体之间的联系:1.一对一联系(1:1)2.一对多联系(1:n)3.多对多联系(m:n)

    12. 字段的数据类型:⑴文本 ⑵备注 ⑶数字 ⑷日期/时间 ⑸货币 ⑹自动编号 ⑺是/否 ⑻OLE对象 ⑼超链接 ⑽查阅向导 ⑾计算字段 ⑿附件

    13. 表之间的关系:表之间的关系类型有三种①一对一关系 ②一对多关系 ③多对多关系

    14. 输入掩码:输入掩码用于定义数据的输入格式 用于定义输入掩码的字符:eg:0、9、A、a、c、L

    15. 查询的种类:①选择查询 ②参数查询 ③交叉表查询 ④操作查询 ⑤SQL查询

    16. 操作查询的种类:①生成表查询 ②追加查询 ③更新查询 ④删除查询

    17. 窗体的视图类型:①设计视图 ②窗体视图 ③数据表视图 ④布局视图 ⑤数据透视表视图 ⑥数据透视表视图

    18. 报表的视图类型:①报表视图 ②打印预览视图 ③布局视图 ④设计视图

    19. 窗体的类型:①纵栏表窗体 ②表格式窗体 ③数据表窗体 ④数据透视表窗体 ⑤数据透视图窗体

    20. 报表的类型:①纵栏式报表 ②表格式报表 ③图表报表 ④标签报表

    21. 窗体的记录源:可以是表或查询对象,还可以是一个SQL语句

    22. 报表的记录源:可以是表或查询对象,还可以是一个SQL语句

    23. 报表的组成:报表页眉节、页面页眉节、主体节、页面页脚节、报表页脚节、组页眉和组页脚

    24. 有效性规则:使用有效性规则属性可以规定输入到记录、字段或控件中数据应满足什么要求

    25. 模块概念:是Access数据库中的一个数据库对象,其代码用VBA语言编写

    26. 宏的概念:是由一个或多个操作组成的合集,其中每个操作都实现特定的功能

    27. 主子窗体:原始窗体称为主窗体,窗体中的窗体称为子窗体

    28. SQL查询:中文名:结构化查询语言 SQL是一种专门针对数据库操作的计算机语言 SQL查询是使用SQL语句创建的查询

    展开全文
  • ACCESS2010给别人做简单的前端界面,其中碰到一些问题,都解决了,总结一下解决方法。 一、自动建立ODBC数据源。 用户不会建立数据源,那么程序实现自动创建。自动创建有很方法,不过不外乎是修改注册表...
  • 我们经常遇到在程序设计中,数据可以在同一个窗体中传递,也可能在窗体间传递。这里将要阐述窗体之间的数值传递。例如需要通过调用子窗体来修改父窗体的内容。 【注意】窗体间传值要弄清楚到底是哪个对象的问题,...
  • 比方说建立了一fm1,这是从fm0继承的窗体,父fm0数据源指向DataModule1中的ADOQuery1,当DataModule1中的ADOQuery1增加了一字段后,继承窗体fm1打开出错,说没有这字段。因在这继承的窗体中新建立了很控件...
  • access窗体设计:创建子窗体

    万次阅读 2014-03-20 19:42:05
    如果要将子窗体链接到主窗体,执行该过程前应确保已与基础记录源建立关联。 当窗体显示在数据透视表视图或数据透视图视图中时,不能向其中添加子窗体。 同时创建窗体与子窗体 在“数据库”窗口中,单击...
  • 通常我们在进行开发时,多个项目间数据库服务器分离,但是各项目间又需要对其他项目的数据库表进行访问。这个时候我们希望能够在一个数据库查询中对其他服务器的数据表进行查询操作,这样可以减少相当多的工作量。 ...
  • 在csdn上经常碰到有人问一些Ado.net的问题,特别是开发信息管理系统之类的跟数据库比较密切的程序时,在数据和界面层的开发中会遇到不少常见问题,下面我们通过vs.net自带的数据窗体向导来看看能它能帮我们决绝什么...
  • 透过vs.net数据窗体向导看Ado.net 作者:郑佐2005-1-1 在csdn上经常碰到有人问一些Ado.net的问题,特别是开发信息管理系统之类的跟数据库比较密切的程序时,在数据和界面层的开发中会遇到不少常见问题,下面我们...
  •   首先,我们需要在VS上进行窗体建立,对两设备进行窗体建立。点击左侧的工具箱,找到textBox,label还有button,将他们放置在窗体上。然后修改右下角的Text以及字体的大小和字种(不改其实也没问题),两...
  • 看到群里朋友有人讨论WTL中的thunk技术,让我联想到了duilib的类似技术。这些技术都是为了解决c++封装的窗体类与窗体句柄的关联问题。而我觉得duilib解决这问题的方法要比thunk简单好用
  • ACCESS数据库窗体

    千次阅读 2014-07-17 09:41:48
    3、记录:可以是表、查询、SQL 语句,用于指定窗体操作的数据来源。 4、窗体的组成和结构:窗体的每部分称为”节“;窗体由五部分构成:主体、窗体页眉、窗 体页脚、页面页眉、页面页脚。 ①窗体页眉/页脚...
  • HTML窗体指南

    千次阅读 2016-02-28 21:17:32
    2016.02.28 - 04.06 个人英文阅读练习笔记...02.28 ...HTML表单是一能跟用户交互的强大工具;然而,由于历史和技术原因,如何充分使用它们总不是那么的显著。本指南将覆盖HTML表单的所有方面,从结构到风格,从数据
  • Microsoft Visual Studio 2008/.NET Framework 3.5Windows 窗体编程如何:创建 MDI 父窗体多文档界面 (MDI) 应用程序的基础是 MDI 父窗体。它是包含 MDI 子窗口的窗体,MDI 子窗口是用户与 MDI 应用程序在其中进行...
  • 将一组数据自动写到第三方的界面上,通过鼠标拖拽传值到数据接收界面,代替手工录入。
  • 三个角度窗体含义 从应用程序开发员的角度 窗体就是图形用户界面句柄,以该句柄作为系统API...操作系统利用这个数据结构维护窗体状态 窗体类型 可重叠窗体 这样的窗体是有标题栏,边框,客户区域的顶层窗体,...
  • 单个窗体实例构建一 Windows 窗体应用程序。我想把它写成 这样一程序:对该窗体任何一实例的操作都会在所有其它实例上反映出来。我该怎么做? 这是一有趣的问题。我保证某些聪明的编程好手会建议我...
  • winfrom 窗体传值

    千次阅读 2010-04-20 17:12:00
    winform窗体传值   窗体间传值要弄清楚到底是哪个对象的问题,否则容易传错值。就比如窗体对象loginFrm为登陆窗体,想把当前的登陆用户信息传递给主窗体mainFrm,但是却实例了一新的对象loginFrm把里头的信息...
  • DELPHI建立多线程COM服务器

    千次阅读 2007-09-23 21:30:00
    DELPHI建立多线程COM服务器
  • 若要完成本演练,您需要: 访问带有 Pubs ...这包括创建一通过数据库填充数据集的查询。 向窗体添加 DataGrid 控件,并将其绑定到数据。 添加代码来填充数据集。 添加将数据集更改发送回数据库的代码。 创建项目和窗
  • 本文介绍了 Windows 窗体固有的程序验证基础结构,并以此为基础开发了用于提供更高效验证功能的自定义验证组件,该验证功能与使用 ASP.NET 的验证控件相似。下载 winforms03162004_sample.msi 示例文件。本页内容...
  • Access教程 第五章 窗体

    万次阅读 2008-03-12 12:32:00
    本章内容 ◆ 窗体的功能◆ 窗体的设计◆ 窗体中的数据操作◆ 窗体的打印和预览 一、窗体的功能 窗体和报表都用于数据库中数据的维护,但两者的...窗体可以显示来自多个数据表中的数据。此外,用户可以利用窗体对数
  • 用PNG透明图片和GDI+做不规则透明窗体用PNG透明图片和GDI+做不规则透明窗体一、准备工作(PNG图片透空窗体)1、图片资源准备工作。首先在Photoshop中编辑所用图片,将这些图片保存成为带透明通道的.png格式(GDI+...
  • winform窗体间传值

    2015-06-02 09:20:32
    窗体间传值要弄清楚到底是哪个对象的问题,否则容易传错值。就比如窗体对象loginFrm为登陆窗体,想把当前的登陆用户信息传递给主窗体mainFrm,但是却实例了一新的对象loginFrm把里头的信息传递给了mainFrm,所以...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 26,084
精华内容 10,433
关键字:

如何用多个数据源建立窗体