2017-01-23 128 views
2

我想在Windows 10的Turbo Delphi中使用IVirtualDesktopManager。 我没有得到任何错误,但IsWindowOnCurrentVirtualDesktop和GetWindowDesktopId不返回任何有用的东西。有谁知道我在这里做错了吗?谢谢。在Delphi中使用IVirtualDesktopManager

unit VDMUnit; 

interface 

uses ActiveX, Comobj; 

Const 
IID_VDM: TGUID ='{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}'; 
CLSID_VDM: TGUID ='{AA509086-5CA9-4C25-8F95-589D3C07B48A}'; 

type 
    {$EXTERNALSYM IVirtualDesktopManager} 
    IVirtualDesktopManager = interface(IUnknown) 
    ['{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}'] 
    function IsWindowOnCurrentVirtualDesktop(Wnd:cardinal; var IsTrue: boolean): HResult; stdcall; 
    function GetWindowDesktopId(Wnd:cardinal; pDesktopID: PGUID): HResult; stdcall; 
    function MoveWindowToDesktop(Wnd:cardinal; DesktopID: PGUID): HResult; stdcall; 
    end; 

function IsOnCurrentDesktop(wnd:cardinal):boolean; 
procedure GetWindowDesktopId(Wnd:cardinal; pDesktopID: PGUID); 
procedure MoveWindowToDesktop(Wnd:cardinal; DesktopID: PGUID); 

implementation 

var 
    vdm:IVirtualDesktopManager; 

function IsOnCurrentDesktop(wnd:cardinal):boolean; 
begin 
    CoInitialize(nil); 
    OleCheck(CoCreateInstance(CLSID_VDM, nil, CLSCTX_INPROC_SERVER,IVirtualDesktopManager,vdm)); 
    OleCheck(vdm.IsWindowOnCurrentVirtualDesktop(wnd,result)); 
    CoUninitialize; 
end; 

procedure GetWindowDesktopId(Wnd:cardinal; pDesktopID: PGUID); 
begin 
    CoInitialize(nil); 
    OleCheck(CoCreateInstance(CLSID_VDM, nil, CLSCTX_INPROC_SERVER ,IVirtualDesktopManager,vdm)); 
    OleCheck(vdm.GetWindowDesktopId(wnd,pDesktopID)); 
    CoUninitialize; 
end; 

procedure MoveWindowToDesktop(Wnd:cardinal; DesktopID: PGUID); 
begin 
    CoInitialize(nil); 
    OleCheck(CoCreateInstance(CLSID_VDM, nil, CLSCTX_INPROC_SERVER,IVirtualDesktopManager,vdm)); 
    OleCheck(vdm.MoveWindowToDesktop(wnd,DesktopID)); 
    CoUninitialize; 
end; 

end. 

好的,下面是一个简单的例子:这个项目只是一个TMemo和一个Ttimer的表单。 它显示Form1.handle不能用于检查窗口是否在当前桌面上。但是,如果您检查Application.Handle,那么如果您切换到另一个桌面并再次返回,则会正确返回false,以便检查备注中的内容。 我觉得这很了不起,因为我认为一个应用程序可以有多个窗口显示在不同的桌面上?

unit Unit1; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, ActiveX, Comobj, StdCtrls, ExtCtrls; 

const 
IID_VDM: TGUID = '{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}'; 
CLSID_VDM: TGUID ='{AA509086-5CA9-4C25-8F95-589D3C07B48A}'; 

type 
    IVirtualDesktopManager = interface(IUnknown) 
    ['{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}'] 
    function IsWindowOnCurrentVirtualDesktop(Wnd: HWND; out IsTrue: BOOL): HResult; stdcall; 
    function GetWindowDesktopId(Wnd: HWND; out DesktopID: TGUID): HResult; stdcall; 
    function MoveWindowToDesktop(Wnd: HWND; const DesktopID: TGUID): HResult; stdcall; 
    end; 

type 
    TForm1 = class(TForm) 
    Memo1: TMemo; 
    Timer1: TTimer; 
    procedure Timer1Timer(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    { Public declarations } 
    end; 

var 
    Form1: TForm1; 

implementation 

{$R *.dfm} 

function GetVDM: IVirtualDesktopManager; 
begin 
    Result := nil; 
    OleCheck(CoCreateInstance(CLSID_VDM, nil, CLSCTX_INPROC_SERVER, IVirtualDesktopManager, Result)); 
end; 

function IsOnCurrentDesktop(wnd: HWND): Boolean; 
var 
    value: BOOL; 
begin 
    OleCheck(GetVDM.IsWindowOnCurrentVirtualDesktop(Wnd, value)); 
    Result := value; 
end; 

procedure TForm1.Timer1Timer(Sender: TObject); 
begin 
    if IsOnCurrentDesktop(Form1.Handle) then 
    Memo1.Lines.Add('Yes') 
    else 
    Memo1.Lines.Add('No'); 
end; 



end. 
+1

你的代码工作正常。有一些古怪。你应该使用'HWND'来代替'cardinal'。你的'GetWindowDesktopId'包装应该返回'TGUID'。你的'MoveWindowToDesktop'包装应该接受一个const'TGUID'。但是,你的代码工作得很好。看起来像是一个误会。如果你想提供[mcve],那么我们就有机会弄清楚它是什么。 –

+1

此外,您必须将'IsTrue'参数声明为'BOOL'而不是'boolean',因为这些类型具有不同的大小,您当前的代码会覆盖堆栈并导致崩溃。 – EugeneK

+0

谢谢,更正! 我注意到别的奇怪。当我检查窗体(form1.handle)是否在当前桌面上时,我总是得到答案是。但是当我使用application.handle时,当桌面切换到另一个桌面时,我得到了正确的答案。 因此,似乎我只能检查应用程序句柄? –

回答

3

您所有的接口方法的声明有误,但IsWindowOnCurrentVirtualDesktop()尤其是麻烦,因为它的第二个参数需要一个指向一个BOOL,而不是一个指针BooleanBOOLBoolean不同的数据类型。 BOOLLongBool的别名,其大小为4个字节,而Boolean的大小为1个字节。

除此之外,您应该使用HWND而不是Cardinal作为Wnd参数。我还建议使用outconst代替原始指针DesktopID参数。

此外,你真的需要摆脱Co(Un)Initialize()调用,他们根本不属于你的功能。 调用者负责(不)初始化COM,因为它必须决定访问COM时要使用的COM线程模型。个人功能不应该代表主叫方作出决定。 COM必须基于每个线程进行初始化,因此在调用函数之前调用CoInitialize()是各个应用程序线程的责任,并且在终止之前调用CoUninitialize()

由于您的vdm变量,这一点尤其重要。该变量属于每个函数的内部,而不是全局内存。在编译器试图在已经调用CoUninitialize()之后的单元终止期间释放该接口时,您冒着崩溃风险。

与所述所有这一切,尝试一些更喜欢这个:

unit VDMUnit; 

interface 

uses 
    Windows; 

function IsOnCurrentDesktop(wnd: HWND): Boolean; 
function GetWindowDesktopId(Wnd: HWND): TGUID; 
procedure MoveWindowToDesktop(Wnd: HWND; const DesktopID: TGUID); 

implementation 

uses 
    ActiveX, Comobj; 

const 
IID_VDM: TGUID = '{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}'; 
CLSID_VDM: TGUID ='{AA509086-5CA9-4C25-8F95-589D3C07B48A}'; 

type 
    IVirtualDesktopManager = interface(IUnknown) 
    ['{A5CD92FF-29BE-454C-8D04-D82879FB3F1B}'] 
    function IsWindowOnCurrentVirtualDesktop(Wnd: HWND; out IsTrue: BOOL): HResult; stdcall; 
    function GetWindowDesktopId(Wnd: HWND; out DesktopID: TGUID): HResult; stdcall; 
    function MoveWindowToDesktop(Wnd: HWND; const DesktopID: TGUID): HResult; stdcall; 
    end; 

function GetVDM: IVirtualDesktopManager; 
begin 
    Result := nil; 
    OleCheck(CoCreateInstance(CLSID_VDM, nil, CLSCTX_INPROC_SERVER, IVirtualDesktopManager, Result)); 
end; 

function IsOnCurrentDesktop(wnd: HWND): Boolean; 
var 
    value: BOOL; 
begin 
    OleCheck(GetVDM.IsWindowOnCurrentVirtualDesktop(Wnd, value)); 
    Result := value; 
end; 

function GetWindowDesktopId(Wnd: HWND): TGUID; 
being 
    OleCheck(GetVDM.GetWindowDesktopId(Wnd, Result)); 
end; 

procedure MoveWindowToDesktop(Wnd: HWND; const DesktopID: TGUID); 
begin 
    OleCheck(GetVDM.MoveWindowToDesktop(Wnd, DesktopID)); 
end; 

end. 

最后,注意IVirtualDesktopManager仅适用于Windows 10及更高版本,所以如果你不想让你的代码崩溃上早期版本的Windows,你应该更优雅去除OleCheck()CoCreateInstance()这样你就可以处理错误,如:

uses 
    Classes; 

type 
    TFakeVirtualDesktopManager = class(TInterfacedObject, IVirtualDesktopManager) 
    public 
    function IsWindowOnCurrentVirtualDesktop(Wnd: HWND; out IsTrue: BOOL): HResult; stdcall; 
    function GetWindowDesktopId(Wnd: HWND; out DesktopID: TGUID): HResult; stdcall; 
    function MoveWindowToDesktop(Wnd: HWND; const DesktopID: TGUID): HResult; stdcall; 
    end; 

function TFakeVirtualDesktopManager.IsWindowOnCurrentVirtualDesktop(Wnd: HWND; out IsTrue: BOOL): HResult; stdcall; 
begin 
    IsTrue := False; 
    Result := S_OK; 
end; 

function TFakeVirtualDesktopManager.GetWindowDesktopId(Wnd: HWND; out DesktopID: TGUID): HResult; stdcall; 
begin 
    DesktopID := GUID_NULL; 
    Result := S_OK; 
end; 

function TFakeVirtualDesktopManager.MoveWindowToDesktop(Wnd: HWND; const DesktopID: TGUID): HResult; stdcall; 
begin 
    Result := S_OK; 
end; 

function GetVDM: IVirtualDesktopManager; 
var 
    hr: HResult; 
begin 
    Result := nil; 
    hr := CoCreateInstance(CLSID_VDM, nil, CLSCTX_INPROC_SERVER, IVirtualDesktopManager, Result); 
    if Failed(hr) then 
    begin 
    if hr = REGDB_E_CLASSNOTREG then 
     Result := TFakeVirtualDesktopManager.Create as IVirtualDesktopManager 
    else 
     OleCheck(hr); 
    end; 
end;