0

I have a code exmple.

unit Unit1;

interface

uses
...

type
...

function Wow64EnableWow64FsRedirection(Wow64FsEnableRedirection: BOOL): BOOL; stdcall;

var
...

implementation

{$R *.dfm}

uses
...

function Wow64EnableWow64FsRedirection; external kernel32 name 'Wow64EnableWow64FsRedirection';

procedure LaunchOSK;
begin
  Wow64EnableWow64FsRedirection(False);
  try
    ShellExecute(0, 'OPEN', PChar('osk.exe'), nil, nil, SW_SHOWNORMAL);
  finally
    Wow64EnableWow64FsRedirection(True);
  end;
end;

end.

This code works fine in 64-bit OS. However, naturally when you try to use this in a 32-bit OS (XP) you get an error message (The procedure entry point Wow64Enable64FsRedirection could not be located in the dynamic link library kernel32.dll.). If you isolate Wow64EnableWow64FsRedirection and just run ShellExecute it launches fine.

I have beaten my head the past 2 days trying different things I have Googled and not one them of works as far as safely starting OSK in both 32 and 64 bit. Even a well known Delphi guru's suggestion does not work.

I do not need a lecture about disabling Wow64EnableWow64FsRedirection. Since it is temporary no harm no foul.

What I need is understanding how I can take this code and isolate the Wow64Enable64FsRedirection interface procedure and its accompanying implementation part and the necessary parts of the LaunchOSK procedure when the application is running on XP for example. Is this possible??? Compiler statements are useless, right?

If this is not possible then I will have to resort in producing 2 apps (one for XP and the other for 64 bit) which is a last resort.

Thanks in advance.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 1
    Nice try, Victoria. Changed bool to boolean. Also added "delayed" to implementation function. App started fine now. However, when I launch OSK I get an "External exception C06D007F" error. Oh well. That Wow64EnableWow64FsRedirection code in the launch procedure needs isolating, too. I guess I could test for 64-bit OS. –  Feb 21 '18 at 22:57
  • Not only bitness. You'll need to test the OS version as well. Or you can use dynamic loading instead of delayed. – Victoria Feb 21 '18 at 23:21
  • It is done. Used the 64-bit test. Rest of the code is intact. Thanks. The simplest solution is always the best. As far as awarding you victory your deleted comment was it, not the answer you gave. Not sure how to award based on a comment. –  Feb 21 '18 at 23:26
  • Edit the code to the final changes just in case someone needs it. –  Feb 21 '18 at 23:31
  • I said the same in the answer. There was an extra note about wrong parameter type. But as I said, testing bitness is not enough. You can have 64-bit Windows XP and there will be no such function. – Victoria Feb 21 '18 at 23:32
  • I am not worried about 64-bit XP which is a dinosaur. –  Feb 21 '18 at 23:54
  • The bottom line is that none of this would be necessary if Embarcadero would give us keyboard popup when entering an editable control for keyboardless tablets, etc. That is one of the love/hate situations I have with them. –  Feb 22 '18 at 00:01
  • I reverted the damage you did to the question by your edit which changed it completely – David Heffernan Feb 22 '18 at 07:13

2 Answers2

1

You can use dynamic loading and simply check whether the GetProcAddress function returns a valid function address.

Or you can use delayed loading and call the imported function only if the OS version and bitness matches necessary requirements.

Victoria
  • 7,822
  • 2
  • 21
  • 44
  • Answers containing nothing but links to off-site locations are not appropriate here (and you already know that, I'm sure). The accept doesn't change things. Sorry, but a DV from me because the relevant content isn't here in your answer. Links off-site should be used only as additional references, not as the primary (or in this case, only) content. – Ken White Feb 22 '18 at 04:22
  • 1
    @Ken, then unthink the links. The answer will remain _Use delayed loading. Or dynamic loading._ One can easily find necessary information about the topic. – Victoria Feb 22 '18 at 05:11
  • Not unthinking the links. Rethink your answer. :-) From [How do I write a good answer?](http://stackoverflow.com/help/how-to-answer): *Links to external resources are encouraged, but **please add context around the link so your fellow users will have some idea what it is and why it’s there. Always quote the most relevant part of an important link, in case the target site is unreachable or goes permanently offline.*** – Ken White Feb 22 '18 at 13:18
  • In the case of "delayed loading", you can assign a failure hook that returns a pointer to a fallback function that does nothing, then you don't need to test the OS version at all. Just call the function normally, and it will either call the real OS function or the fallback function. – Remy Lebeau Feb 22 '18 at 20:39
  • @Remy, true. That can be better. Shall I include that in this post? – Victoria Feb 22 '18 at 20:52
-1

Your code uses load time linking. The imported function must be present in the system or your executable will not start.

Instead you need to use run time linking. Use LoadLibrary and GetProcAddress to link to the function. If GetProcAddress fails to find the function then it isn't available and of course you don't need to call it because you are on a 32 bit OS.

The right way to do this though is to spawn a 64 bit process which can then launch the OSK process. And if ever you really do need to disable redirection do heed the documentation which tells you to use Wow64DisableWow64FsRedirection.

Finally it's always best to use CreateProcess to launch a process. There's nothing to be gained from involving the shell and file associations. Why ask the shell to call CreateProcess when you can do so directly?

OK, that's normally good advice, but once again, OSK is special. See Delphi - On Screen Keyboard (osk.exe) works on Win32 but fails on Win64.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • 1
    "*The right way to do this though is to spawn a 64 bit process which can then launch the OSK process*" - `osk.exe` resides in both the `%windir%\System32` and `%windir%\SysWow64` folders. You shouldn't need to spawn a 64bit process just to run OSK. You shouldn't even need to mess around with the FS redirector at all, just run `%windir\system32\osk.exe` and let Windows pick the appropriate OSK version. Of course, if you want to force running the native version, then conditionally run `%windir%\SysNative\osk.exe` in a Wow64 process, and `%windir%\System32\osk.exe` in a non-Wow64 process. – Remy Lebeau Feb 22 '18 at 01:08
  • @Remy, in the end it [might be](https://stackoverflow.com/a/43886052/8041231) [implementing UI automation](https://forums.embarcadero.com/thread.jspa?messageID=717510). – Victoria Feb 22 '18 at 01:32
  • @Remy, I tried to you your code from this page https://forums.embarcadero.com/thread.jspa?messageID=670677. Got an access violation on GetWindowsDirectory. I am using Tokyo. –  Feb 22 '18 at 02:26
  • @Remy, I am have a 32-bit application running on W7 64-bit. According to what I have read, sysnative is not recognized in what I mentioned. Too bad. –  Feb 22 '18 at 02:50
  • 1
    @ZtecSoftwareLLC: This is not the contact point for questions asked at another site. If you have issues with the code posted at that site, then ask about them there. Bringing them up in a totally different topic on a totally different site isn't appropriate, especially at this site. – Ken White Feb 22 '18 at 04:25
  • @ZtecSoftwareLLC there is no possible AV in the `GetWindowsDirectory` code I posted in that forum. If you are getting an AV, you are doing something wrong. And `sysnative` works fine in a 32bit app on Win7 64bit. If you have questions, feel free to post them separately, not in comments here. – Remy Lebeau Feb 22 '18 at 05:27
  • @Remy For a normal process what you say is of course correct. But OSK is a very special process and you will find that naively starting osk.exe from a WOW64 process does indeed fail. – David Heffernan Feb 22 '18 at 07:18
  • @DavidHeffernan: tested a 32bit app on Win7 64bit. Unelevated app, run in either an (un)elevated console, using either `System32\osk.exe` (w/ redirection) or `SysWOW64\osk.exe`, displays the keyboard fine. Same app, run from Explorer, the OSK process starts but displays a popup message: "Could not start On-Screen Keyboard". Either way, using `Sysnative\osk.exe` fails with `ERROR_FILE_NOT_FOUND`. Elevated app, run from Explorer or console, all 3 paths fail with `ERROR_ELEVATION_REQUIRED`. – Remy Lebeau Feb 22 '18 at 19:14
  • @Remy Not what I see. Judging from the comments in my linked answer UAC can confound. What is the state of UAC on your test machine. – David Heffernan Feb 22 '18 at 19:26
  • @DavidHeffernan: UAC is set to its Default setting. I did tests with and without a `requireAdministrator` manifest in the app, and running `cmd` and `explorer` with and without "Run as administrator" – Remy Lebeau Feb 22 '18 at 19:30
  • @Remy As you can see from my linked answer, and from the asker's experience, and from the hundreds of similar topics on the subject of starting osk, there are issues. Or do you doubt that? – David Heffernan Feb 22 '18 at 19:33
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/165677/discussion-between-remy-lebeau-and-david-heffernan). – Remy Lebeau Feb 22 '18 at 19:36