zoukankan      html  css  js  c++  java
  • Delphi 虚拟桌面

    Delphi创建虚拟桌面实现后台调用外部程序

    核心提示:最近在做的一个软件,其中有一部分功能需要调用其它的软件来完成,而那个软件只有可执行文件,根本没有源代码,幸好,我要做的事不难,只需要在我的程序启动后,将那个软件打开,在需要的时候,对其中的一个文本框设置一些文字,再点击一个按钮就可以了。...


    最近在做的一个软件,其中有一部分功能需要调用其它的软件来完成,而那个软件只有可执行文件,根本没有源代码,幸好,我要做的事不难,只需要在我的程序启动后,将那个软件打开,在需要的时候,对其中的一个文本框设置一些文字,再点击一个按钮就可以了。

    说到这里,相信你也有了对该功能的一些初步设想了,没错,其基本思路就是:
    1)调用CreateProcess()打开目标程序。
    2)用FindWindow()找到目标程序的窗口Handle。
    3)找到文本框的Handle,以及按钮的MessageID,用SendMessage()方法设置文字,并触发事件。

    好了,这样确实很简单吧,但是当我实现它后,却发现这样做的结果则是:当我的程序启动并打开目标程序时,它的Splash窗口,以及主窗口都将显示出来,即使当我用FindWindow()找到主窗口Handle后,调用SendMessage(WindowHandle, 
    SW_HIDE)来隐藏该窗口,还是会有一瞬主窗口被显示出来的,这样的效果实在是追求完美的我不忍心看到的。

    那么怎么解决这个问题呢,首先我当然在CreateProcess()上面寻找方法,可惜,它只有一个参数可以设置窗口的默认显示方式,但是一旦这个窗口自己重设了显示方式,它就没有任何作用了。。。。继续查找文档,这时我看到CreateProcess()的一个参数TStartupInfo中有 
    lpDesktop这么一个属性,按照MSDN的说法,如果该指针为NULL,那么新建的Process将在当前Desktop上启动,而如果对其赋了一个Desktop的名称后,Process将在指定的Desktop上启动,恩,看来不错,就从它入手了:

    1)首先,建立一个虚拟的Desktop,
    const
    DesktopName = 'MYDESK';

    FDesktop:=CreateDesktop(DesktopName,nil,nil,0,GENERIC_ALL,nil);
    Windows中可以建立多个Desktop,可以使用SwitchDesktop()来切换哪个Desktop被显示出来,以前有过将Windows模拟成Linux的形式,可以在多个虚拟Desktop中切换的程序,其实那种程序也是用的Windows本身的虚拟Desktop功能来实现的,另外 
    Windows的启动画面,以及屏保画面也都是用虚拟Desktop实现的,好了,关于这方面不多介绍了,感兴趣的话,可以到MSDN中查看更详细资料:
    http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/enumdesktops.asp

    2)在CreateProcess的时候,指定程序在我新生成的Desktop上运行:

    View Code

    3)用FindWindow去找程序的主窗口
    开始我直接写下了这样的代码:

    View Code

    但是,实践证明,这样是找不到不在当前Desktop中的Window的,那怎么办呢:
    答案是,可以用SetThreadDesktop()函数,这个函数可以设置当前Thread工作所在的Desktop,于是我在以上代码前又加了一句:

    if not SetThreadDesktop(FDesktop) then begin
    exit;
    end;

    但是,程序运行后,该函数却返回了false,说明方法调用失败了,再仔细看MSDN,发现有这么一句话:

    The SetThreadDesktopfunction will fail if the calling thread has any windows or hooks on its current desktop (unless the hDesktopparameter is a handle to the current desktop).

    哦,原来需要切换Desktop的线程中不能有任何UI方面的东西,而我是在程序的主线程中调用该方法的,当然会失败拉,知道了这点就好办了,我只需要用一个“干净”的线程,让它绑定到新的Desktop上,再让它用FindWindow()方法找到我要找的WindowHandle,不就可以了吗,于是,这一步就需要借助一个线程了,线程的代码如下:

    View Code

    而主程序中的代码变成这样:

    View Code

    呵呵,成功,这样果然可以顺利的找到窗口Handle了。
    4)最后,再用这个主窗口Handle,找出里面的EditBox的Handle,如这样:
    FEditWindow:=FindWindowEx(FMainWindowHandle,0,PChar('Edit'),nil);
    我在这里指定了这个文本框的ClassName,这个名称可以用Spy++得到。

    初始化的工作就到此结束了,如果顺利,程序就真正在后台被运行了起来。那么功能调用呢,还是和一般的做法一样:

    if (FMainWindowHandle=0) or (FEditWindow=0) then begin
    exit;
    end;
    SendMessage(FEditWindow,WM_SETTEXT,0,LongInt(@AText[1]));
    SendMessage(FMainWindowHandle,WM_COMMAND,$8012,$0);

    其中$8012这个数字,也是用Spy++来得到的资源ID。
    最后,别忘了关闭程序,以及释放虚拟Desktop:

    View Code

    好了,这样就几乎完美的实现了一个后台调用程序的功能,它对最终客户来说将是完全透明的,客户根本感觉不到后台还有另一个程序在工作。是不是很爽啊,这样别人的很多程序我们都可以直接拿来用了(当然了,得在遵守版权的基础上才行拉)。

    [Delphi] 虚拟桌面

    View Code

    虚拟桌面delphi全攻略

    网上也有很多关于虚拟桌面的技术文章,但我却没有找到一个讲解得比较详细的,花了两天时间凑合网上的零碎片断做出个虚拟桌面的小程序出来,让高手们见笑了,现在我将我制作这个程序的全部流程详细地贴出来供大家参考:
    虚拟桌面说白了,核心就是CreateDesktop和SwitchDesktop函数,这是别人的说法,但是要想做出一个完整的软件来还需要其它很多必不可少的函数,这里我先介绍下要用到的函数(参数我就不解释了,因为我自已现在不对每个个参数的具体含义的认识都比较模糊,很多参数是照抄网上的):
    CreateDesktop//创建虚拟桌面
    OpenDesktop//获取桌面句柄
    SwitchDesktop//激活/转到指定的桌面
    globaladdatom//创建全局原子
    globalfindatom//查找全局原子
    globaldeleteatom//删除全局原子
    getasynckeystate//判断虚拟键的状态
    CreateProcess//打开指定的进程
    GetCurrentThreadId//获取当前ID
    GetThreadDesktop//获取当前桌面句柄
    好了就这些,下面我说下流程
    刚看网上的资料的时候,感觉好像只要CreateDesktop来创建桌面再用SwitchDesktop来转到指定桌面即可,然而事情却没这么简单.创建虚拟桌面后,我将SwitchDesktop函数输入进程序的按钮事件里,我一按按钮,令我痛哭流涕的事情发生了------转到的桌面干干净净什么都没有,没有桌面图标没有任务栏甚至打不开任务管理器,没办法,含泪按下电脑的重启按钮(好几千呢555).
    我继续在网上查找资料,发现要在新创建的桌面显示图标,必须先在新创建的桌面打开桌面进程explorer.exe(一般情问下我们结束掉这个进程桌面就会消失就是这个道理),现在就用到了CreateProcess函数,CreateProcess函数如何能在其它桌面创建进程呢?CreateProcess有个TStartupInfo结构的参数,该结构中有个叫lpDesktop的成员,它指定了在哪个桌面创建进程(不对其赋值则为当前桌面),请看代码:
    var sin:TStartupInfo;s:string;
    sin.cb:=sizeof(sin);
    sin.wShowWindow:=SW_SHOW;
    sin.dwFlags:=STARTF_USESHOWWINDOW;
    s:='a';
    sin.lpDesktop:=pchar(s);
    CreateProcess('c:WINDOWSexplorer.exe',nil,nil,nil,False,0,nil,nil,sin,pin);
    其中的字符a为创建桌面的名称,该名称在创建桌面的函数中指定.
    接下来的问题是转到新的桌面之后,运行的自身程序就不见了,怎样才能转回原来的桌面呢?我用了一个愚蠢的办法,在创建新桌面和新桌面进程的同时在新桌面上将本程序再次打开,这样实际上创建了几个新桌面自身程序就运行了几个实例,看代码:
    CreateProcess(pchar(extractfilepath(application.ExeName)+'这里是程序的名字'),nil,nil,nil,False,0,nil,nil,sin,pin);
    将这行代码放到创建桌面进程代码的后面,这个问题就解决了,一波未平一波又起新了问题又产生了,而且还是两个很严重的问题:
    1。我创建桌面的代码是写在窗体载入的过程中的,这就意味着每创建一个自身实例就多了N个虚拟桌面。
    2。新创建的实例进程并不能保存有第一个实例进程的句柄,这样就无法对桌面进行操作。
    对于第一个问题我是用全局原子法来解决的,在程序启动时先使用globalfindatom查找全局原子,若不荐在则表示未运行过实例进程,那么就创建一个全局原子并创建虚拟桌面,若存在,而不创建虚拟桌面,但还是要创建一个全局原子(原因不用我说了吧~),在窗体的退出代码中别忘了globaldeleteatom掉创建的全局原子。
    对于第二个问题,即然无法继承就自已查找句柄吧;前面说过在创建虚拟桌面时会为创建的桌面指定一个名称,现在这个名称的作用体现出来了,OpenDesktop函数其中一个参数为桌面的名称,该函数的返回值就是桌面的句柄。新创建的虚拟桌面可以用这个方法获得句柄,但是默认桌面怎么获取呢?这个更简单,因为默认的桌面名称就是“default”,只要把这个字符放入OpenDesktop函数中便能轻松获取默认桌面的句柄。在句柄得到了,只要使用SwitchDesktop(参数就是桌面句柄)函数就能转到指定桌面了。
    PS:默认桌面的名称我开始也不知道,但我无意中在网上发现一个函数:getuserobjectinformation,这个函数可以根据桌面的句柄获取桌面的名称,当前桌面的句柄可以用GetThreadDesktop和GetCurrentThreadId()获取,GetCurrentThreadId()的功能是获得当前ID,这个ID作为GetThreadDesktop的参数用来获取当前桌面句柄。这样就得到了桌面的名称
    不多说了附上我的源代码:

    View Code

    虛擬桌面的創建與切換

    View Code

    浅析桌面精灵的实现

    1. 软件的开发目的

    想必大家对桌面精灵很熟悉吧,想不想自己编一个?笔者非常想编一个,其目的居然是为了取得美眉的喜欢,由此引出了我开发本软件的目的。如果读者有我同样的需求,那么请继续看下去,我将和你共同探讨这个问题。注意以下示例代码均用DELPHI描述。

    2. 实现原理

    其实桌面精灵的原理很简单,主要分以下几步:

    1.获取桌面窗口的HDC。

    API 定义如下:

    GetDC函数用于获取指定窗口的图形设备描述表

    HDC GetDC(

    HWND hWnd // 窗口句柄

    );

    例如:

    DeskTopDC:HDC;//定义桌面窗口的图形设备描述表句柄

    DeskTopDC:=GetDC(0);

    或者DeskTopDC:=GetDC(GetDesktopWindow());

    2.创建一个内存位图,把桌面中将要绘图的区域,保存到内存位图中去,以便绘图完成时恢复桌面。为此我定义了一个函数:

    View Code

    3.将动画对象透明地拷贝到桌面的绘图区域,笔者用了一个GDIAPI函数方便地实现了此功能。

    定义如下:

    View Code

    注意:

    Windows NT: 需要5.0或以上版本

    Windows: 需要 Windows 98 或 以上版本

    其它低版本不支持。

    此函数包含在msimg32.dll.

    笔者定义了一个tranbit函数来动态调用TransparentBlt函数,具体定义见第三节。

    4.将第二步生成的内存位图拷贝到桌面。这样一帧动画就显示完成。不断循环1-4步,你就能看到连续的动画场景了。

    3.具体代码

    以下是一个演示程序,在DELPHI5.0+WINDOWS2000P中调试通过。创建一个窗体Form1,放上两个Image控件,命名为Image1,Image2,再放上一个Timer控件,命名为Timer1。准备两张位图,一张放入Image1,另一张放入Image2。笔者用了如下样式的位图(截取了一部分),你可以自己画动画对象,也可以借用别人的,笔者就是用微软画的图片。

    从图片你可以看出,图片中包括了许多连续的动画帧,一张图片完成一个动作,如旋转一周等,每帧动画大小完全一样,除了动画对象其它像素用一种透明色填充。好了你可以看具体的代码了。

    View Code

    刷新Windows桌面

    在使用计算机的过程中,经常会碰到在Windows桌面上残留有程序运行后的留下一些痕迹,这时我们往往在Windows桌面上单击鼠标右键,然后选择刷新,使桌面变得干净、整洁。其实自己编代码来实现这个功能也很简单,调用一个函数SHChangeNotify即可。首先,在Delphi的单元文件的Uses部分手动添加上ShlObj,然后在按钮点击事件中加上代码如下:
    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NiL, NiL);

  • 相关阅读:
    HotSpot算法实现
    垃圾收集器(一)
    Java内存区域
    第53条:接口优先于反射机制
    C# 文本转语音,在语音播放过程中停止语音
    C# an error has occurred while updating the entries.see the log file
    音频播放时出现 Uncaught (in promise) DOMException: play() failed because the user didn't interact with the document first. https://goo.gl/xX8pDD
    C# CSV文件读写
    图解 SQL-Server新建作业
    FineUI 布局宽度自适应,后台回调js方法
  • 原文地址:https://www.cnblogs.com/blogpro/p/11345428.html
Copyright © 2011-2022 走看看