今天在这个网站上看到一篇论述在新建桌面中运行应用程序来屏幕所有系统热键的文章,很受启发,按照它的思路和代码改成了一个 vfp 类,只要直接实例化它并调用它的 run 方法即可在新建桌面中运行一个应用程序。
关于屏蔽系统热键的问题,以前看过很多方法,最好的方法是替换 GINA,但用 vfp 无法实现。这篇文章中的方法可以很容易的修改为几乎所有语言的代码。原理很简单:系统热键(如:ctrl+alt+del, alt+tab, 系统任务栏等)都只会显示在 Windows 的缺省桌面中,所以如果想要使这些东西不出现,最简单的办法就是新建一个桌面(一个全新的桌面,除了背景图片外什么也没有),然后在其中运行它,这样即使你按下系统热键,它也不会在新桌面中显示(它显示在缺省桌面中)。新建和切换桌面只要几个 api 函数即可实现,但切换到新桌面后不能用 run / shellexec 等来启动 exe,因为这些函数只能在缺省桌面中创建新进程,所以需要用到 CreateProcess 这个 api 来创建并启动新进程,它其中的一个结构参数可指定要执行进程所在的桌面。
Run 方法接收两个参数,第一个是要执行的完整文件路径及名称,第二个参数【可选】指定等待它运行多长时间后自动返回缺省桌面,缺省将一直等待到你关闭新桌面中的进程为止,下面是示例代码:
- m.oDesktop = NEWOBJECT( 'NewDesktop' )
- IF ( 'O' == VARTYPE( m.oDesktop )) AND !ISNULL( m.oDesktop )
- m.cFile = GETFILE( 'exe', '文件名(&N):', '运行(&R)', 0, '请选择要在新桌面中运行的程序' )
- m.oDesktop.Run( m.cFile )
- ENDIF
- m.oDesktop = NULL
- DEFINE CLASS NewDesktop AS Custom
- HIDDEN hOldDesktop
- hOldDesktop = 0
- HIDDEN hOldInput
- hOldInput = 0
- HIDDEN hNewDesktop
- hNewDesktop = 0
- HIDDEN pcNewDesktop
- pcNewDesktop = 0
- HIDDEN NewDesktopName
- NewDesktopName = ('desktop' +SYS(2015))
- HIDDEN ErrMsg
- ErrMsg = ''
- Name = 'NewDesktop'
- HIDDEN PROCEDURE api_decl
- DECLARE Long GetThreadDesktop IN WIN32API Long dwThreadId
- DECLARE Long OpenInputDesktop IN WIN32API ;
- Long dwFlags, Long fInherit, Long dwDesiredAccess
- DECLARE Long CreateDesktop IN WIN32API ;
- String lpszDesktop, Long lpszDevice, Long pDevmode, ;
- Long dwFlags, Long dwDesiredAccess, Long lpsa
- DECLARE Long SetThreadDesktop IN WIN32API Long hDesktop
- DECLARE Long SwitchDesktop IN WIN32API Long hDesktop
- DECLARE Long CloseDesktop IN WIN32API Long hDesktop
- DECLARE Long CreateProcess IN WIN32API ;
- String pszAppName, String pszCmdLine, ;
- Long lpProcessAttr, Long lpThreadAttr, ;
- Long bInheritHandles, Long dwCreationFlags, ;
- Long lpEnvironment, String pszCurDir, ;
- String lpStartupInfo, String @ lpProcInfo
- DECLARE Long CloseHandle IN WIN32API Long hObject
- DECLARE Long WaitForSingleObject IN WIN32API ;
- Long hHandle, Long dwMilliseconds
- DECLARE Long GetProcessHeap IN WIN32API
- DECLARE Long HeapAlloc IN WIN32API ;
- Long hHeap, Long dwFlags, Long dwBytes
- DECLARE Long HeapFree IN WIN32API ;
- Long hHeap, Long dwFlags, Long lpMem
- DECLARE Long GetLastError IN WIN32API
- This.pcNewDesktop = HeapAlloc( GetProcessHeap(), 8, LEN(This.NewDesktopName)+1 )
- IF ( 0 == This.pcNewDesktop )
- MESSAGEBOX( '内存分配失败 !', 16, '错误' )
- ELSE
- SYS( 2600, This.pcNewDesktop, LEN(This.NewDesktopName), This.NewDesktopName )
- ENDIF
- ENDPROC
- PROCEDURE Run
- *!* ------------------------------------------------------------------------
- *!* 功能: 在新建桌面中运行指定的程序
- *!* 入口: tcExeFile - 要执行的 exe 文件
- *!* tiTimeout - 等待执行时间(单位 ms, 0 代表一直等待到它终止并退出)
- *!* ------------------------------------------------------------------------
- LPARAMETERS tcExeFile, tiTimeout
- IF ( 'C' != VARTYPE( m.tcExeFile )) OR EMPTY( m.tcExeFile )
- RETURN .F.
- ENDIF
- IF ( PCOUNT() < 2 ) OR ( 'N' != VARTYPE( m.tiTimeout )) OR ( m.tiTimeout <= 0 )
- m.tiTimeout = -1
- ENDIF
- This.ErrMsg = ''
- IF This.SwitchToNewDesktop()
- This.StartProcess( m.tcExeFile, m.tiTimeout )
- ENDIF
- IF !EMPTY( This.ErrMsg )
- MESSAGEBOX( This.ErrMsg, 16, '错误' )
- ENDIF
- ENDPROC
- HIDDEN PROCEDURE SwitchToNewDesktop
- #define DESKTOP_SWITCHDESKTOP 0x0100
- #define GENERIC_ALL 0x10000000
- *!* 保存当前线程所在的桌面句柄
- This.hOldDesktop = GetThreadDesktop( _vfp.ThreadId )
- *!* 保存当前用户输入线程的桌面句柄
- This.hOldInput = OpenInputDesktop( 0, 0, DESKTOP_SWITCHDESKTOP )
- *!* 创建新桌面
- This.hNewDesktop = CreateDesktop( This.NewDesktopName, 0, 0, 0, GENERIC_ALL, 0 )
- IF ( 0 == This.hNewDesktop )
- This.ErrMsg = '创建桌面失败!错误代码: ' + TRANSFORM( GetLastError())
- RETURN .F.
- ENDIF
- *!* 切换当前线程到新建桌面中
- SetThreadDesktop( This.hNewDesktop )
- IF ( 0 == SwitchDesktop( This.hNewDesktop ))
- This.ErrMsg = '切换桌面失败!错误代码: ' + TRANSFORM( GetLastError())
- RETURN .F.
- ENDIF
- ENDPROC
- HIDDEN PROCEDURE StartProcess
- LPARAMETERS tcExeFile, tiTimeout
- *!* typedef struct _PROCESS_INFORMATION {
- *!* HANDLE hProcess;
- *!* HANDLE hThread;
- *!* DWORD dwProcessId;
- *!* DWORD dwThreadId;
- *!* } PROCESS_INFORMATION
- *!* typedef struct _STARTUPINFO {
- *!* DWORD cb;
- *!* LPTSTR lpReserved;
- *!* LPTSTR lpDesktop;
- *!* LPTSTR lpTitle;
- *!* DWORD dwX;
- *!* DWORD dwY;
- *!* DWORD dwXSize;
- *!* DWORD dwYSize;
- *!* DWORD dwXCountChars;
- *!* DWORD dwYCountChars;
- *!* DWORD dwFillAttribute;
- *!* DWORD dwFlags;
- *!* WORD wShowWindow;
- *!* WORD cbReserved2;
- *!* LPBYTE lpReserved2;
- *!* HANDLE hStdInput;
- *!* HANDLE hStdOutput;
- *!* HANDLE hStdError;
- *!* } STARTUPINFO
- LOCAL cSI, cPI, hProcess
- IF ( m.tiTimeout <= 0 )
- m.tiTimeout = -1
- ENDIF
- m.cSI = BINTOC( 0, 'rs' ) ;
- + BINTOC( This.pcNewDesktop, 'rs' ) + REPLICATE( CHR(0), 14*4 )
- m.cSI = BINTOC( LEN( m.cSI )+4, 'rs' ) + m.cSI
- m.cPI = REPLICATE( CHR(0), 4*4 )
- IF ( 0 == CreateProcess( ;
- m.tcExeFile, '', 0, 0, 0, 0, 0, NULL, m.cSI, @ m.cPI ))
- This.ErrMsg = '在新桌面中创建进程失败! 错误代码: ' + TRANSFORM( GetLastError())
- ELSE
- m.hProcess = CTOBIN( LEFT( m.cPI, 4 ), 'rs' )
- *!* 等待此进程终止
- WaitForSingleObject( m.hProcess, m.tiTimeout )
- *!* CloseHandle( m.hProcess )
- ENDIF
- This.ClearUp()
- ENDPROC
- HIDDEN PROCEDURE ClearUp
- IF ( 0 != This.hOldInput )
- SwitchDesktop( This.hOldInput ) && 切换回默认桌面
- This.hOldInput = 0
- ENDIF
- IF ( 0 != This.hOldDesktop )
- SetThreadDesktop( This.hOldDesktop ) && 回复原线程桌面
- This.hOldDesktop = 0
- ENDIF
- IF ( 0 != This.hNewDesktop )
- CloseDesktop( This.hNewDesktop ) && 关闭新建的桌面
- This.hNewDesktop = 0
- ENDIF
- ENDPROC
- PROCEDURE Init
- This.api_decl()
- ENDPROC
- PROCEDURE Destroy
- IF ( 0 != This.pcNewDesktop )
- HeapFree( GetProcessHeap(), 0, This.pcNewDesktop )
- This.pcNewDesktop = 0
- ENDIF
- ENDPROC
- ENDDEFINE
进一步的,我们可以检查并限制我们的程序只能在新桌面中运行,这样可以最大程度的保护它的运行不受用户切换任务的干扰。