2011年5月12日 星期四

虛擬桌面技術的初步探討

【文章標題】虛擬桌面技術的初步探討  

【文章作者】:  newjueqi  

【編寫語言】:VC++6.0

【操作平臺】: XP-SP2

【作者聲明】這幾天研究了虛擬桌面技術,感覺到這是一種非常有意思的技術,這篇就當成是學習筆記吧!本人只是感興趣,沒有其它目的,失誤之處敬請給位大俠原諒!



什麼是桌面?

      
每一個運行著Window NT 的系統中都有一個Window 工作站物件,這個物件是安全物件的第一層,是所有使用者安全物件的繼承之源,每一個Window 工作站物件可以擁有一些桌面物件,每一個桌面都擁有一個視窗鏈。視窗鏈裡存放著顯示在所屬桌面的各種視窗。Window NT 用了兩個桌面視窗物件,一個是用來處理登陸介面、遮罩、鎖住工作站等,一個是我們登陸之後進來操作的視窗了。 

    Window NT
通過"explorer.exe"進程來管理這個桌面物件。這就是為什麼我們在工作管理員裡殺掉"explorer.exe",我們的桌面就會消失的原因。



什麼是虛擬桌面?

     
虛擬桌面是一種可以在電腦原來桌面基礎上再創造一個新的桌面出來,在新的桌面上可以進行日常的操作。



虛擬桌面的用途?

1  本人覺得這門技術最重要的用途就是可以把任何有UI介面的軟體變成一個後臺軟體(即看不到任何介面,包括啟動介面)

2  可以時工作時是一個桌面,娛樂時是一個桌面(大家可以去下載網站上搜索一下這類軟體的用途,上面的功能描述非常有意思)



虛擬桌面的實現方法

windows中,要創建一下新的桌面可用到APICreateDesktop(),函式宣告如下

HDESK CreateDesktop(

  LPCTSTR lpszDesktop,         // 
新桌面的名稱

  LPCTSTR lpszDevice,          // 
NULL

  LPDEVMODE pDevmode,          // 
NULL

  DWORD dwFlags,               // 
指定應用程式在桌面的相容方式

  ACCESS_MASK dwDesiredAccess, // 
指定新桌面的許可權

  LPSECURITY_ATTRIBUTES lpsa   //
指定控制碼是否能被繼承

);



返回值是新創建的桌面的控制碼。



那麼新建了一個桌面後,怎麼在這個新的桌面上運行程式呢?先不要著急,我們先來回顧一下創建進程的函數CreateProcess(),在這個函數的參數中StartupInfo中有 lpDesktop這麼一個屬性,如果這個屬性為NULL則在當前的桌面創建執行緒,如果指定了桌面的名稱,則進程將會在指定的桌面上啟動,所以想在創建的新桌面裡初始化一些程式,只要把lpDesktop參數指定為新桌面的名稱即可。



另外也有一個簡單的方法可把新的執行緒掛在新創建的桌面下,就是使用API函數SetThreadDesktop(),聲明如下:

BOOL SetThreadDesktop(

  HDESK hDesktop  // 
指向指定的桌面控制碼

);



但使用這個函數要注意一點,根據MSDN的說法:The SetThreadDesktop function will fail if the calling thread has any windows or hooks on its current desktop (unless the hDesktop parameter is a handle to the current desktop)  意思就是除非要指定的桌面控制碼是當前的桌面,不然的話這個函數的調用會失敗如果當前執行緒擁有任何的視窗(即UI介面).



怎麼實現不同桌面之間的切換呢?

     
要在不同的桌面之間切換,可用API函數SwitchDesktop,聲明如下:

BOOL SwitchDesktop(

  HDESK hDesktop  // 
桌面的控制碼

);



另外也可通過點擊切換按鈕實現桌面的切換。



但又引申出一個新的問題,必須要知道各個桌面的控制碼,獲取桌面的控制碼可通過API函數GetThreadDesktop,函數的聲明如下:

HDESK GetThreadDesktop(

  DWORD dwThreadId   //
執行緒的ID);



返回值就是指定執行緒所在的桌面了。

    
而且我們必須要認清的是創建新桌面的執行緒啟動是在舊的桌面上,所以可以用下面的語句輕鬆獲得當前桌面的控制碼:

GetThreadDesktop(GetCurrentThreadId());   

返回值就是舊的桌面控制碼。



怎麼關閉新創建的桌面?

     
這個問題其實也不用我們擔心,微軟已經替我們想好了^-^ , CloseDesktop函數可輕鬆實現這個功能,函式宣告如下:

    BOOL CloseDesktop(

  HDESK hDesktop  // 
指定要關閉的桌面的控制碼

);



下面貼一段代碼的例子,是在新創建的桌面上運行計算器(calc.exe)實現計算器的後臺運行



#include <windows.h>



HINSTANCE hInst;  //
當前的實例控制碼

HWND hWnd;        //
窗口控制碼

HDESK hvirtualDesk;    //
新創建的虛擬桌面控制碼

PROCESS_INFORMATION pi; //
計算器進程資訊



//
消息迴圈

LRESULT CALLBACK WinProc(

      HWND hwnd,      // handle to window

      UINT uMsg,      // message identifier

      WPARAM wParam,  // first message parameter

      LPARAM lParam   // second message parameter

              )

{

  switch(uMsg)

  {

  case WM_CLOSE:

      TerminateProcess( pi.hProcess, 1 );

      CloseDesktop( hvirtualDesk );

      DestroyWindow(hwnd);

      PostQuitMessage(0);

      break;



  case WM_DESTROY:



      CloseDesktop( hvirtualDesk );

      PostQuitMessage(0);

      break;



  case WM_HOTKEY: 



    if ( 0x0001 == wParam ) //
為退出桌面熱鍵Alt+Q

    {

            SendMessage(hwnd,WM_CLOSE,0,0);    

    }

    break;



  default:

    return DefWindowProc(hwnd,uMsg,wParam,lParam);

  }

  return 0;

}



//
創建虛擬桌面

void CrateVirtualDesk()

{

  //
把新創建的虛擬桌面控制碼存放在hvirtualDesk

  hvirtualDesk=CreateDesktop( "newdesk",

                NULL,

                NULL,

                DF_ALLOWOTHERACCOUNTHOOK,   

                GENERIC_ALL,     

                NULL);   



}





//
在虛擬桌面上運行一個計算器的實例

void RunCalc()

{

  STARTUPINFO si;   

  

  ZeroMemory( &si, sizeof(si) );  

  si.cb = sizeof(si);   

    si.lpDesktop = "newdesk";   

  

  ZeroMemory( &pi, sizeof(pi) );   

  

  if( !CreateProcess( NULL,                   

    "calc",                                       

    NULL,                       

    NULL,                      

    FALSE,                      

    0,                      

    NULL,                          

    NULL,                      

    &si,                       

    &pi ) )      

  {     

    MessageBox(NULL,"
運行計算器失敗","Error",0);     

    ExitProcess(1);     

  }     



}



int WINAPI WinMain(

    HINSTANCE hInstance,      // handle to current instance

    HINSTANCE hPrevInstance,  // handle to previous instance

    LPSTR lpCmdLine,          // command line

     int nCmdShow              // show state

           )

{

  WNDCLASS wndcls;

  MSG msg;



  hInst=hInstance;

  ZeroMemory( &wndcls,sizeof(wndcls) );

  wndcls.cbClsExtra=0;

  wndcls.cbWndExtra=0;

  wndcls.hbrBackground=(HBRUSH)GetStockObject(BLACK_BRUSH);

  wndcls.hInstance=hInstance;

  wndcls.lpfnWndProc=WinProc;

  wndcls.lpszClassName="hello";

  wndcls.lpszMenuName=NULL;

  wndcls.style=CS_HREDRAW | CS_VREDRAW;

  RegisterClass(&wndcls);

  

  hWnd=CreateWindow( "hello","hello",WS_OVERLAPPEDWINDOW,

    300,300,100,100,NULL,NULL,hInstance,NULL );

  

  //
註冊所需的熱鍵,Alt+Q為退出創建的虛擬桌面

  if( !RegisterHotKey( hWnd,0x0001,MOD_ALT ,'Q' ) )     

    {     

        //
處理退出進程     

        return TRUE;     

    }     



  //
創建虛擬桌面

  CrateVirtualDesk();

  

  //
在虛擬桌面上運行一個計算器的實例

  RunCalc();



  ShowWindow( hWnd,SW_SHOWNORMAL );

  UpdateWindow( hWnd );

  

  while( GetMessage( &msg,NULL,0,0 ) )

  {

    TranslateMessage(&msg);

    DispatchMessage(&msg);

  }



  return msg.wParam;

}




1 則留言:

  1. 原來可以這麼做
    太帥了
    趕快來實驗看看
    感謝教學

    回覆刪除