Mega Search
23.2 Million


Sign Up

Make a donation  
OSK.exe on a W7 74bit from 32 bit application...  
News Group: embarcadero.public.delphi.nativeapi

Hello,

I am using Delphi XE.

I have a program that opens and uses OSK.exe. The program is 32 bit and 
the OS is 64 bit. Works great.

I am not trying to use the code in another program, copy and paste, and 
the second program and test program fail. 

The reported error is "Could not start On-Screen Keyboard".

Below is the source code. It really is not that much. Why it works for 
one program and not others is a mystery. I have tried using 
CreateProcess inplace of ShellExecute without joy.


Any ideas?

Thanks,

Mark


unit Main;

interface

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

type
  TForm1 = class(TForm)
    ShowBtn: TButton;
    HideBtn: TButton;
    procedure ShowBtnClick(Sender: TObject);
    procedure HideBtnClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses ShellAPI;

type
 TWow64DisableWow64FsRedirection = function (Var 
Wow64FsEnableRedirection: LongBool): LongBool; stdcall;
 TWow64EnableWow64FsRedirection = function (Wow64FsEnableRedirection: 
LongBool):LongBool; stdcall;
 TIsWow64Process = function (Handle:THandle; var IsWow64 : BOOL) : BOOL; 
stdcall;

var
 is64Bit:boolean;
 kernalHandle:THandle;

 Wow64DisableWow64FsRedirection:TWow64DisableWow64FsRedirection;
 Wow64EnableWow64FsRedirection:TWow64EnableWow64FsRedirection;
 IsWow64Process:TIsWow64Process;

function IS64:Boolean;
var
 xIS64:Bool;
begin
 if IsWow64Process(GetCurrentProcess, xIS64) then
  Result:=xIS64
 else
  Result:=false;
end;

procedure LaunchApplication(const appName:string; pStrings:array of 
string);
var
 i,start:integer;
 paramString:string;
 fName,lpOperation,params,defaultDir:array[0..511] of char;
begin
 if (appName = '') then
  Exit;

 start:=0;
 if (pStrings[0] = '') then
  start:=1;

 paramString:='';
 for i:= start to high(pStrings) do
  begin
   paramString:=paramString + pStrings[i];
   if (i <> high(pStrings)) then
    paramString:=paramString + ' ';
  end;

 ShellExecute(0,
              StrPCopy(lpOperation,pStrings[0]),
              StrPCopy(fName,appName),
              StrPCopy(params,paramString),
              StrPCopy(defaultDir,'C:\'),SW_SHOWNORMAL);
end;

procedure LaunchOSK;
var
 Wow64FsEnableRedirection:longBool;
begin
 try
  if is64Bit then
   Wow64DisableWow64FsRedirection(Wow64FsEnableRedirection);

  LaunchApplication('OSK.EXE',['']);
 finally
  if is64Bit then
   Wow64EnableWow64FsRedirection(Wow64FsEnableRedirection)
 end;
end;

procedure KillOSK;
var
 appHandle:THandle;
begin
 appHandle:=FindWindow(nil,PWideChar('On-Screen Keyboard'));
 if (appHandle <> 0) then
  PostMessage(appHandle,WM_QUIT,0,0);
end;

procedure TForm1.HideBtnClick(Sender: TObject);
begin
 KillOSK;
end;

procedure TForm1.ShowBtnClick(Sender: TObject);
begin
 LaunchOSK
end;

initialization
 kernalHandle:=LoadLibrary('kernel32.dll');
 if (kernalHandle <> 0) then
  begin
   @IsWow64Process:=GetProcAddress(kernalHandle, 'IsWow64Process');
   is64Bit:=IS64;
   if is64Bit then
    begin
     @Wow64DisableWow64FsRedirection:=GetProcAddress(kernalHandle, 
'Wow64EnableWow64FsRedirection');
     @Wow64EnableWow64FsRedirection:=GetProcAddress(kernalHandle, 
'Wow64EnableWow64FsRedirection');
    end;
  end;

end.


---------------------DFM------------------------

object Form1: TForm1
  Left = 308
  Top = 94
  Caption = 'Main'
  ClientHeight = 712
  ClientWidth = 764
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object ShowBtn: TButton
    Left = 96
    Top = 80
    Width = 505
    Height = 113
    Caption = 'Show keyboard'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -21
    Font.Name = 'Tahoma'
    Font.Style = []
    ParentFont = False
    TabOrder = 0
    OnClick = ShowBtnClick
  end
  object HideBtn: TButton
    Left = 96
    Top = 199
    Width = 505
    Height = 113
    Caption = 'Hide keyboard'
    Font.Charset = DEFAULT_CHARSET
    Font.Color = clWindowText
    Font.Height = -21
    Font.Name = 'Tahoma'
    Font.Style = []
    ParentFont = False
    TabOrder = 1
    OnClick = HideBtnClick
  end
end

Vote for best question.
Score: 0  # Vote:  0
Date Posted: 25-Sep-2014, at 1:59 PM EST
From: Mark Marks
 
Re: OSK.exe on a W7 74bit from 32 bit application...  
News Group: embarcadero.public.delphi.nativeapi
Mark wrote:

> When I tested on XP, orginally, it gave an error that kernel32.dll
> could not be loaded. Not sure what SP but, dynamically loading works.

EVERY process uses kernel32.dll.  It is loaded automatically as part of the 
startup logic for a new process.  That has always been true.  

--
Remy Lebeau (TeamB)

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 26-Sep-2014, at 12:27 PM EST
From: Remy Lebeau (TeamB)
 
Re: OSK.exe on a W7 74bit from 32 bit application...  
News Group: embarcadero.public.delphi.nativeapi
> All processes have kernel32.dll automatically loaded at startup, so 
> you do 

When I tested on XP, orginally, it gave an error that kernel32.dll could 
not be loaded. Not sure what SP but, dynamically loading works.

> As for IsWow64Process(), it exists in XP 

Same as above, I have customers running on some real old stuff. Plus XP 
embedded and I need to really defensive program.  

Thanks for the data on "delayed". 

Thanks for your help.

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 25-Sep-2014, at 5:55 PM EST
From: Mark Marks
 
Re: OSK.exe on a W7 74bit from 32 bit application...  
News Group: embarcadero.public.delphi.nativeapi
Mark wrote:

> I am targetting XP so I need to dynamiclly load the kernel32.dll.

All processes have kernel32.dll automatically loaded at startup, so you do 
not need to call LoadLibrary() on it.  If you need a handle to it, use GetModuleHandle('kernel32') 
instead.  As for IsWow64Process(), it exists in XP (SP2 or higher is needed). 
 XP 64bit was the first 64bit version of Windows, and is the one that introduced 
WOW64.

If you look more carefully at the code I gave you, it uses Delphi's delay-load 
feature (which was introduced in Delphi 2010) to dynamically load the function:

{code}
function IsWow64Process(Handle: THandle; var IsWow64 : BOOL) : BOOL; stdcall; 
external 'kernel32.dll' delayed;
{code}

You do not need to call GetProcAddress() manually, the RTL can do it for 
you.  Read the documentation for more info:

http://docwiki.embarcadero.com/RADStudio/2010/en/Libraries_and_Packages#Delayed_Loading
http://docwiki.embarcadero.com/CodeExamples/XE7/en/DelayedLoading_(Delphi)

Also, I see you removed the IFDEFs I had added.  If you ever decide to recompile 
your app for 64bit in the future, there is no need to call IsWow64Process() 
since it will always report False.  You only need to call IsWow64Process() 
in a 32bit process running on a 64bit OS.

--
Remy Lebeau (TeamB)

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 25-Sep-2014, at 5:16 PM EST
From: Remy Lebeau (TeamB)
 
Re: OSK.exe on a W7 74bit from 32 bit application...  
News Group: embarcadero.public.delphi.nativeapi
Remy, thank you very much. Your answer is excellent.

I am targetting XP so I need to dynamiclly load the kernel32.dll.

So below is the working/tested code in the proof program. If I have 
trouble in the real programs I will update the thread.

Again, Remy--Thank you.

unit Main;

interface

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

type
  TForm1 = class(TForm)
    ShowBtn: TButton;
    HideBtn: TButton;
    procedure ShowBtnClick(Sender: TObject);
    procedure HideBtnClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

uses ShellAPI;

type
 TIsWow64Process = function (Handle:THandle; var IsWow64 : BOOL) : BOOL; 
stdcall;

var
 is64Bit:boolean;
 kernalHandle:THandle;
 IsWow64Process:TIsWow64Process;

function IS64:Boolean;
var
 xIS64:Bool;
begin
 result:=false;
 if CheckWin32Version(5, 2) then
  begin
   if IsWow64Process(GetCurrentProcess, xIS64) then
    result:=xIS64;
  end;
end;

procedure LaunchApplication(const appName:string; pStrings:array of 
string);
var
 i,start:integer;
 paramString:string;
 fName,lpOperation,params,defaultDir:array[0..511] of char;
begin
 if (appName = '') then
  Exit;

 start:=0;
 if (pStrings[0] = '') then
  start:=1;

 paramString:='';
 for i:= start to high(pStrings) do
  begin
   paramString:=paramString + pStrings[i];
   if (i <> high(pStrings)) then
    paramString:=paramString + ' ';
  end;

 ShellExecute(0,
              StrPCopy(lpOperation,pStrings[0]),
              StrPCopy(fName,appName),
              StrPCopy(params,paramString),
              StrPCopy(defaultDir,'C:\'),SW_SHOWNORMAL);
end;


function GetWindowsDir: string;
var
  Path: array[0..MAX_PATH] of Char;
begin
 if (GetWindowsDirectory(Path, MAX_PATH) = 0) then
  RaiseLastOSError;
 Result:=IncludeTrailingPathDelimiter(Path);
end;

function GetSystemDir: string;
var
  Path: array[0..MAX_PATH] of Char;
begin
 if (GetSystemDirectory(Path, MAX_PATH) = 0) then
  RaiseLastOSError;
 Result:= IncludeTrailingPathDelimiter(Path);
end;

function GetNativeSystemDir: string;
begin
 if is64Bit then
  Result:=GetWindowsDir + 'sysnative' + PathDelim
 else
  Result:=GetSystemDir;
end;

procedure LaunchOSK;
begin
  LaunchApplication(GetNativeSystemDir + 'OSK.EXE', ['']);
end;

procedure KillOSK;
var
 appHandle:THandle;
begin
 appHandle:=FindWindow(nil,PWideChar('On-Screen Keyboard'));
 if (appHandle <> 0) then
  PostMessage(appHandle,WM_QUIT,0,0);
end;

procedure TForm1.HideBtnClick(Sender: TObject);
begin
 KillOSK;
end;

procedure TForm1.ShowBtnClick(Sender: TObject);
begin
 LaunchOSK;
end;

initialization
 kernalHandle:=LoadLibrary('kernel32.dll');
 if (kernalHandle <> 0) then
  begin
   @IsWow64Process:=GetProcAddress(kernalHandle, 'IsWow64Process');
   is64Bit:=IS64;
  end;

end.

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 25-Sep-2014, at 4:38 PM EST
From: Mark Marks
 
Re: OSK.exe on a W7 74bit from 32 bit application...  
News Group: embarcadero.public.delphi.nativeapi
Mark your:

> Below is the source code. It really is not that much. Why it works
> for one program and not others is a mystery. I have tried using
> CreateProcess inplace of ShellExecute without joy.

You are doing too much work, and dangerous work at that.  Disabling the FileSystem 
Redirector, even momentarily, has a global effect on the entire system, not 
just the calling process.  OSK.EXE resides in the System32 folder, so you 
can use WOW64's special "sysnative" alias to bypass the FileSystem Redirector 
and use the real System32 folder path.  I demonstrated this in the following 
StackOverflow answer:

http://stackoverflow.com/a/12852118/65863

If you want to run the 64bit OSK.EXE (why not let WOW64 run the 32bit version 
normaly?) then try something like this:

{code}
{$IFDEF WIN64}
function IsWow64: Boolean;
begin
  Result := False;
end;
{$ELSE}
function IsWow64Process(Handle: THandle; var IsWow64 : BOOL) : BOOL; stdcall; 
external 'kernel32.dll' delayed;
function IsWow64: Boolean;
var
  xIsWow64: BOOL;
begin
  Result := false;
  if CheckWin32Version(5, 2) then
  begin
    if IsWow64Process(GetCurrentProcess(), xIsWow64) then
      Result := xIsWow64;
  end;
end;
{$ENDIF}

procedure LaunchApplication(const appName: string; params: array of string 
= []; const operation: string = '', const workingDir: string = '');
var
  i: Integer;
  paramString: string;
begin
  if (appName = '') then
    Exit;
  paramString := '';
  for i := Low(params) to high(params) do
  begin
    paramString := paramString + params[i];
    if (i < High(pStrings)) then
      paramString := paramString + ' ';
  end;
  // the Pointer casts allow nil to be passed for empty strings...
  ShellExecute(0,
    PChar(Pointer(operation)), // nil = default operation
    PChar(appName),
    PChar(Pointer(paramString)), // nil = no params
    PChar(Pointer(workingDir)), // nil = use current working directory
    SW_SHOWNORMAL);
end;

function GetWindowsDir: string;
var
  Path: array[0..MAX_PATH] of Char;
begin
  if GetWindowsDirectory(Path, MAX_PATH) = 0 then
    RaiseLastOSError;
  Result = IncludeTrailingPathDelimiter(Path);
end;

function GetSystemDir: string;
var
  Path: array[0..MAX_PATH] of Char;
begin
  if GetSystemDirectory(Path, MAX_PATH) = 0 then
    RaiseLastOSError;
  Result = IncludeTrailingPathDelimiter(Path);
end;

function GetNativeSystemDir: string;
begin
  if IsWow64 then
    Result := GetWindowsDir + 'sysnative' + PathDelim;
  else
    Result := GetSystemDir;
end;

procedure LaunchOSK;
begin
  LaunchApplication(GetNativeSystemDir + 'OSK.EXE');
end;

procedure KillOSK;
var
  appHandle: HWND;
begin
  appHandle := FindWindow(nil, 'On-Screen Keyboard');
  if (appHandle <> 0) then
    PostMessage(appHandle, WM_QUIT, 0, 0);
end;
{code}

--
Remy Lebeau (TeamB)

Vote for best answer.
Score: 0  # Vote:  0
Date Posted: 25-Sep-2014, at 4:11 PM EST
From: Remy Lebeau (TeamB)