2014-04-16 34 views
4

我想從我的應用程序運行屏幕鍵盤。它在Windows XP 32位下正常工作,但在Win 7 64位下不正確。德爾福 - 屏幕鍵盤(osk.exe)在Win32上工作,但在Win64上失敗

unit Unit5; 

interface 

uses 
    Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, 
    Dialogs, ShellAPI; 

type 
    TForm5 = class(TForm) 
    procedure FormCreate(Sender: TObject); 
    private 
    { Private declarations } 
    public 
    class function IsWOW64: Boolean; 
    { Public declarations } 
    end; 

var 
    Form5: TForm5; 

implementation 

{$R *.dfm} 


procedure TForm5.FormCreate(Sender: TObject); 
var path:String; 
    res : Integer; 

function GetSysDir: string; 
var 
    Buf: array[0..MAX_PATH] of Char; 
    Len: UINT; 
    S: String; 
begin 
    {$IFNDEF WIN64} 
    if TForm5.IsWOW64 then 
    begin 
    Len := GetWindowsDirectory(Buf, MAX_PATH); 
    if Len = 0 then RaiseLastOSError; 
    SetString(S, Buf, Len); 
    Result := IncludeTrailingPathDelimiter(S) + 'Sysnative\'; 
    Exit; 
    end; 
    {$ENDIF} 
    Len := GetSystemDirectory(Buf, MAX_PATH); 
    if Len = 0 then RaiseLastOSError; 
    SetString(S, Buf, Len); 
    Result := IncludeTrailingPathDelimiter(S); 
end; 

begin 
path := GetSysDir; 

path := path + 'osk.exe'; 

    res := ShellExecute(self.Handle,'open',Pchar(path),nil,nil,SW_NORMAL); 

if res <> 42 then 
    begin 
    ShowMessage(path); 
    RaiseLastOSError; 
    end; 
end; 

class function TForm5.IsWOW64: Boolean; 
type 
    TIsWow64Process = function(// Type of IsWow64Process API fn 
    Handle: THandle; 
    var Res: BOOL 
): BOOL; stdcall; 
var 
    IsWow64Result: BOOL;    // result from IsWow64Process 
    IsWow64Process: TIsWow64Process; // IsWow64Process fn reference 
begin 
    // Try to load required function from kernel32 
    IsWow64Process := GetProcAddress(
    GetModuleHandle('kernel32'), 'IsWow64Process' 
); 
    if Assigned(IsWow64Process) then 
    begin 
    // Function is implemented: call it 
    if not IsWow64Process(GetCurrentProcess, IsWow64Result) then 
    RaiseLastOSError; 
    // Return result of function 
    Result := IsWow64Result; 
    end 
    else 
    // Function not implemented: can't be running on Wow64 
    Result := False; 
end; 


end. 

在x64下運行該應用程序顯示路徑C:\ Windows \ Sysnative \ osk.exe,並引發'調用OS函數失敗'錯誤。

搜索在Windows目錄顯示,osk.exe存在

enter image description here

+0

在調用ShellExecute後處理所有關於錯誤的信息是什麼? 42的特別之處是什麼?而ShellExecute不使用最後的錯誤。如果你想錯誤檢查使用ShellExecuteEx或CreateProcess。 –

回答

4

有UAC下什麼特別的OSK。此代碼失敗,錯誤代碼爲740,ERROR_ELEVATION_REQUIRED,請求的操作需要提升

var 
    si: TStartupInfo; 
    pi: TProcessInformation; 
.... 
si.cb := SizeOf(si); 
GetStartupInfo(si); 
Win32Check(CreateProcess('C:\Windows\system32\osk.exe', nil, nil, nil, 
    False, 0, nil, nil, si, pi)); 

在具有UAC的計算機上,這在32位和64位進程下都失敗。你可以在這裏找到一些關於這個問題的討論:http://blog.delphi-jedi.net/2008/05/17/the-case-of-shellexecute-shellexecuteex-createprocess-and-oskexe/

所以你的問題是不相關的32或64位,而不是你的XP系統沒有UAC。

更廣泛地說,我認爲這應該足以說服你永遠不要再打電話ShellExecute。它只存在於16位兼容性,在報告錯誤時毫無用處。如果您想要錯誤,請致電ShellExecuteEx。但是,由於我們正在啓動一個新流程,因此CreateProcess通常是正確的API調用。

也就是說,在這個特定的情況下,osk的設計是這樣的,它不能通過CreateProcess以編程方式啓動。它確實需要由ShellExecuteShellExecuteEx調用。這允許外殼執行其UAC魔術。現在,事實證明魔法不可能發生在32位WOW64進程中。然後,解決方案是通過調用ShellExecuteEx從64位進程啓動osk。

這裏是你的解決方法:

  1. 在32位系統上,你可以簡單地調用ShellExecuteEx打開osk
  2. 在64位系統上,如果您的進程是64位,則可以再次調用ShellExecuteEx來打開osk
  3. 在64位系統上,如果您的進程是32位WOW64進程,則需要啓動一個單獨的64位進程,然後調用ShellExecuteEx來打開osk

由於您似乎沒有使用64位版本的Delphi,因此您需要找到64位編譯器。您可以使用64位fpc或64位C++編譯器。下面的C++程序是不夠的:

#include <Windows.h> 
#include <Shellapi.h> 

int CALLBACK WinMain(
    HINSTANCE hInstance, 
    HINSTANCE hPrevInstance, 
    LPSTR lpCmdLine, 
    int nCmdShow 
) 
{ 
    SHELLEXECUTEINFOW sei = { sizeof(sei) }; 
    sei.lpVerb = L"open"; 
    sei.lpFile = L"osk.exe"; 
    sei.nShow = SW_SHOW; 
    ShellExecuteExW(&sei); 
} 

您可以編譯與64位C++編譯器,然後從32位WOW64進程調用它。我知道囉嗦,但確實有實際工作的優點!

+0

好的,在我的開發系統上UAC被禁用了...... nice catch –

+2

@sirrufo你應該啓用uac! –

+0

像往常一樣,你是對的......閱讀文章後,似乎問題與UAC有關。 – RBA