2017-01-23 6 views
2

Windows 10でTurbo DelphiでIVirtualDesktopManagerを使用しようとしています。 エラーは発生しませんが、IsWindowOnCurrentVirtualDesktopとGetWindowDesktopIdは何も役に立ちません。誰も私がここで間違っていることを知っていますか?ありがとう。ここで[OK]を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をチェックすると、別のデスクトップに切り替えてもう一度やり直すと正しくメモを返しますので、メモに書かれている内容を確認してください。 私は、1つのアプリケーションが異なるデスクトップ上に複数のウィンドウを表示できると仮定しているので、これは注目に値すると思いますか?

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

あなたのコードは正常に動作します。いくつかの奇妙なことがあります。 'cardinal'の代わりに' HWND'を使うべきです。 'GetWindowDesktopId'ラッパーは' TGUID'を返します。あなたの 'MoveWindowToDesktop'ラッパーはconst' TGUID'を受け入れます。しかし、ええ、あなたのコードはうまく動作します。誤解のように見える。あなたが[mcve]を提供するなら、それが何であるかを考え出すチャンスがあります。 –

+1

また、 'IsTrue'パラメータを' boolean'ではなく 'BOOL'と宣言する必要があります。これらの型はサイズが異なるため、現在のコードがスタックを上書きし、クラッシュする可能性があります。 – EugeneK

+0

ありがとう、訂正のため! 私は何か変わったことに気づいた。フォーム(form1.handle)が現在のデスクトップ上にあるかどうかをチェックすると、私はいつも答えをyesにします。しかし、私がapplication.handleを使うと、デスクトップが別のデスクトップに切り替えられたときに正しい答えが得られます。 アプリケーションハンドルのみを確認できるようですか? –

答えて

3

あなたのインターフェイスのメソッドのすべてが間違って宣言されているが、その2番目のパラメータはBOOLへのポインタではなく、Booleanへのポインタを期待しているため、特にIsWindowOnCurrentVirtualDesktop()は面倒です。 BOOLおよびBooleanは、非常にの異なるデータ型です。 BOOLは、のエイリアスです(サイズは4バイトですが、Booleanは1バイトです)。

以外のパラメータWndのパラメータには、Cardinalの代わりにHWNDを使用する必要があります。また、生ポインタの代わりにDesktopIDパラメータにoutconstを使用することをお勧めします。

また、実際には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には、例えば、あなたはもっと優雅にエラーを処理することができますCoCreateInstance()OleCheck()を削除する必要があります。

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; 
関連する問題