There is something special about osk under UAC. This code fails with error code 740, ERROR_ELEVATION_REQUIRED
, The requested operation requires elevation.
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));
This fails under both 32 and 64 bit processes on machines with UAC. You can find some discussion of the issue here: https://web.archive.org/web/20170311141004/http://blog.delphi-jedi.net/2008/05/17/the-case-of-shellexecute-shellexecuteex-createprocess-and-oskexe/
So your problem is not related to 32 or 64 bit, rather it is down to your XP system not having UAC.
More broadly I think this should be enough to convince you never to call ShellExecute
again. It only exists for 16 bit compatibility and is singularly useless at reporting errors. If you want errors call ShellExecuteEx
. However, since we are starting a new process, CreateProcess
would normally be the right API to call.
That said, in this specific case the design of osk is such that it cannot be started programmatically by CreateProcess
. It does need to be invoked by ShellExecute
, or ShellExecuteEx
. This allows the shell to perform its UAC magic. Now, it turns out that magic cannot happen from a 32 bit WOW64 process. The solution then is to start osk from a 64 bit process with a call to ShellExecuteEx
.
Here is your workaround:
- On a 32 bit system, you can simply call
ShellExecuteEx
to openosk
. - On a 64 bit system, if your process is 64 bit, you can again call
ShellExecuteEx
to openosk
. - On a 64 bit system, if your process is 32 bit WOW64 process, you need to start a separate 64 bit process which in turn calls
ShellExecuteEx
to openosk
.
Since you don't appear to be using a 64 bit version of Delphi, you'll need to find a 64 bit compiler. You could use the 64 bit fpc, or a 64 bit C++ compiler. The following C++ program is enough:
#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);
}
You can compile that with a 64 bit C++ compiler and then call it from your 32 bit WOW64 process. Long winded I know, but it does have the merit of actually working!