How to define Location and Display language from Windows Control panel - delphi

Delphi xe.
For Tab Administrative - Unicode lang
use GetSystemDefaultLangID
For Tab Formats -
use GetUserDefaultLangID
But what do I use for For Tab Location?
For Tab "Keyboard and Language"
for Vista and above: Getlocaleinfo with key LOCALE_CUSTOM_UI_DEFAULT
Function GetLocaleInformation(flag: integer): string;
var
pclca: array[0..20] of char;
begin
if (GetLocaleInfo(
//locale_system_default - Always identical values returns
LOCALE_CUSTOM_UI_DEFAULT // work only Vista-Win7, not Xp **
,flag,pclca,19) <= 0 ) then begin
pclca[0] := #0;
end;
Result := pclca;
end;
How do I define Location in Xp+Win7 and Display Language in Xp?
Can be a universal key for definition "Display Language" both for Xp and for Win7
How to receive the list of the established languages of the interface?

1.1 - How to get selected geographical location (geographic ID) ?
Use the GetUserGeoID function which returns the geographical location currently selected by user.
1.2 - How to get selected display language for Multilingual User Interface (MUI) in Windows XP ?
Use the GetUserDefaultUILanguage function which returns the language identifier currently selected by user.
2 - Is there an universal way how to get the selected display language supported since Windows XP till Windows 7 ?
Yes, it is. It's just the previously mentioned GetUserDefaultUILanguage function. There's a remark:
If the user UI language is part of a Language Interface Pack (LIP) and
corresponds to a supplemental locale, this function returns
LOCALE_CUSTOM_UI_DEFAULT.
It is supported since Windows 2000 and it should return selected display language even for Windows Vista above (LOCALE_CUSTOM_UI_DEFAULT).
3 - How to get the list of available user interface languages ?
Use the EnumUILanguages function. In Windows XP, it passes the language identifiers to the EnumUILanguagesProc callback function. Since Windows Vista you can even specify additional flags which supplies to pass the language names to that callback function or you can specify the filtering for licensed languages or for the languages allowed by the group policy.

Related

SAPI 5.4/11 Japanese TTS in Delphi GUI vs console

Trying to use MS Speech API v11 with a Japanese engine (MS Haruka) in Delphi 10.3.
I have a sample app with a form and a button. The click handler goes:
uses SpeechLib11_TLB; // Imported from "Microsoft Speech Object Library" v.B.0
procedure TForm1.Button1Click(Sender: TObject);
var
v: ISpeechVoice;
begin
v := CoSpVoice.Create();
//5.4 only but won't hurt
v.Voice := v.GetVoices('language=411', '').Item(0);
v.Speak('時間', SVSFDefault);
end;
That causes an error, "Catastrophic failure" (HRESULT 0x8000FFFF, E_UNEXPECTED). The code that I think should be equivalent works in a C++ project:
#include <windows.h>
#import "libid:d3c4a7f2-7d27-4332-b41f-593d71e16db1" rename_namespace("SAPI") //v11
//#import "libid:C866CA3A-32F7-11D2-9602-00C04F8EE628" rename_namespace("SAPI") //v5.4
int wmain()
{
CoInitializeEx(0, COINIT_APARTMENTTHREADED);
{
SAPI::ISpeechVoicePtr v;
v.CreateInstance(__uuidof(SAPI::SpVoice));
//Needed for 5.4 only, but won't hurt
SAPI::ISpeechObjectTokensPtr voices(v->GetVoices(L"language=411", L""));
v->Voice = voices->Item(0);
v->Speak(L"時間", SAPI::SVSFDefault);
}
CoUninitialize();
return 0;
}
That works and speaks. So the SAPI per se is not broken on the machine. The platform is Win32 in both projects, not Win64. The Japanese voice is the default one (no need to set it explicitly).
Same result with SAPI 5.4 proper (not OneCore), although the Japanese voice is not the default one and I had to add a couple of lines to set it as the default.
Further debugging reveals that on the Delphi side, as much as calling the v.Voice property getter immediately after the first line causes the same error, E_UNEXPECTED. Meanwhile, the Voice setter works if you pass it a valid voice token object from GetVoices(). It looks as if the voice object initializes itself to its defaults correctly in C++, but somehow skips that in the Delphi project.
Requesting v.Voice right after construction works though in Delphi with SAPI 5.4. Calling Speak() still throws a E_UNEXPECTED.
What could be the difference in process/threadwide execution context between C++ and Delphi? It's not the thread locale. The COM thread model is apartment in both.
The same Delphi code works with an English phrase and an English voice (MS Helen). So whatever init failure there might be, it's probably specific to Haruka.
The SAPI 11 runtime is available here. The language data for TTS are here.
Another data point. I've rewritten the SAPI logic in Delphi to use SAPI 5.4 OneCore instead (not SAPI 5.4 proper). Unlike 5.4 and 11, it doesn't expose an IDispatch-based interface, and it's somewhat clumsier specifically in Delphi, but Japanese TTS works. The question, as initially posed, is still unanswered, but at least there's a workaround. I'll write up an answer, but I won't accept it.
However, it's not the custom vs. dual distinction that's to blame. I've changed the logic to use custom interfaces instead of automation ones with SAPI 5.4 proper (the typelib defines both), still got E_UNEXPECTED from Speak(). There's no error info.
Here's another beautiful data point: SAPI 5.4 TTS with automation based API works and talks as expected in a Delphi console app. So it's not even Delphi specific, it's somehow VCL specific. What is it with Delphi GUI? Needless to say, I immediately retested the C++ snippet in a C++ GUI application, with a form and a button. The C++ one talks.
Not an answer, but a workaround.
Windows 10 comes with two flavors of 32-bit SAPI. There's SAPI 5.4 proper (in system32\speech), and also SAPI 5.4 OneCore (in system32\speech_onecore). The latter, even though it's superficially the same, exposes a different typelib - there's no automation support, all interfaces are custom instead of dual. What's more important, when you download the Japanese TTS voice in the Windows 10 Settings app, you end up with 3 voices under OneCore (Sayaka, somehow, missing) and only one, Haruka, under 5.4 proper.
Delphi can consume custom interfaces in a typelib, but the methods look somewhat clumsier. Also, the enumeration of voices in the automation API is cleaner. Anyway, here goes.
uses SpeechLib54Core_TLB; // This time it's OneCore
procedure TForm1.Button1Click(Sender: TObject);
var
v: ISpVoice;
cat: ISpObjectTokenCategory;
toks: IEnumSpObjectTokens;
tok: ISpObjectToken;
sno: LongWord;
hr: HResult;
n: Cardinal;
begin
v := CoSpVoice.Create();
hr := v.GetVoice(tok);
tok.GetCategory(cat);
cat.EnumTokens('language=411', '', toks); //411 means Japanese
toks.GetCount(n);
if n = 0 then
exit; // No Japanese voices installed
toks.Item(0, tok); //Take the first one - typically Ayumi
v.SetVoice(tok);
v.Speak('時間', 0, sno);
end;
Note that passing a Japanese string literal to a COM method works without an explicit cast to a wide string.

Win10 Theme used in running program

Is there any information available to the running program about which Win10 theme is being used?
I am looking primarily to get more debug information, as I have a user with a reported "scrambled GUI".
Is there any way to kill themes, or force to a default theme, from within the running program?
If I understood your question correctly I would recommend to you to use SetWindowTheme.
You should check out information provided with link above to see what docs.microsoft (former msdn) tells us in Remark section:
When pszSubAppName and pszSubIdList are NULL, the theme manager removes the previously applied associations. You can prevent visual styles from being applied to a specified window by specifying an empty string, (L" "), which does not match any section entries.
So, having this in your mind you can easily restrict theming for any window those Handle is known by calling SetWindowTheme with both arguments set to "there should be whitespace between braces".
By the way I wouldn't highly recommend to kill themes for entire OS from your application. Personally, I delete any soft that has such a useful feature.
Important addendum!
I've read your question again after some time passed and I can answer to the first part of your question.
You can obtain theme's info via UXTheme unit.
Primarily you must use this two functions:
GetCurrentThemeName
GetThemeDocumentationProperty
Here is some code that shows how to do this.
uses
..., UXTheme;
var
ThemeName: Array[0..512] of Char;
ThemeColorScheme: Array[0..512] of Char;
ThemeSizeName: Array[0..512] of Char;
PropertyName: Array[0..512] of Char;
begin
UXTheme.GetCurrentThemeName(#ThemeName, SizeOf(ThemeName), #ThemeColorScheme, SizeOf(ThemeColorScheme), #ThemeSizeName, SizeOf(ThemeSizeName));
UXTheme.GetThemeDocumentationProperty(#ThemeName, SZ_THDOCPROP_CANONICALNAME, #PropertyName, SizeOf(PropertyName));
end;
Please read about GetThemeDocumentationProperty attentively - this function takes one of arguments that is in a charge of what property of OS theme will be return. According to docs.microsoft, the flag SZ_THDOCPROP_CANONICALNAME in the code above will return string property for
Retrieves the name of the theme.
So you will be able to check what theme is being using at the moment.
But you still can just disable themes for your own application.

Decimal rounding and printer selection

I use Delphi RAD Studio 2010 and DecimalRounding_JH1.pas from http://cc.embarcadero.com/item/21909.
It works good, but I don't know why, in some old machines (Pentium IV with Windows XP SP3), the round fails after access to printer.printerindex. I have checked that the problem not is Windows XP due it works in other machines with this OS.
I have made a simple project that round an extended value with DecimalRounding_JH1 (drHalfUp) with two decimals (1.105 -> rounds to 1.11). But If I read printer.printerindex, then 1.105 rounds to 1.10).
I thought it could be the "FDIV bug", but compiling with "FDIV safe" doesn't resolve the problem.
The code:
var d1,d2:extended;
i:integer;
begin
d1:=1.105;
d2:=DecimalRounding_JH1.DecimalRoundExt(d1,2,drHalfUp);
memo1.lines.add(FloatToStr(d2)); // --> shows 1.11 (OK)
i:=Printer.printerindex;
d2:=DecimalRounding_JH1.DecimalRoundExt(d1,2,drHalfUp);
memo1.lines.add(FloatToStr(d2)); // --> shows 1.10 (ERROR!!!)
...
I know that it is very strange, but I've tested it and It's as I said.
What could I do?
Edited:
If I add Printer.printerindex:=1; (for example) before i:=Printer.printerindex; then again it works good. Reading printer unit, the difference is about execute "SetToDefaultPrinter" or not:
function TPrinter.GetPrinterIndex: Integer;
begin
if FPrinterIndex = -1 then SetToDefaultPrinter;
Result := FPrinterIndex;
end;
thanks in advance.
Certain parts of the system printer libraries have a rather nasty habit of modifying the 8087 control word. You should restore it to its default value after using methods and properties of Printer.
For example, you might write it like this:
Set8087CW(Default8087CW);
The comments in my codebase suggest that you only need to do this after the VCL printer code has been initialized for the first time. So you could deal with this in your program's startup. Read Printer.PrinterIndex and then immediately set the control word to its desired value.

Get user picture

OS: Win7x64 (2008,2008r2).
Lang: Delphi Xe2.
How to receive a full path (and file name) to the image "user account picture"?
How to set new picture?
Example on delphi plz.
Need:
...function GetCurrentUserPicture:string;
...function GetUserPicture(UserName:String):string;
...function SetUserNewPicture(UserName, ImageFileName:String):bool;
There is an undocumented function in shell32.dll. On Windows XP its ordinal is 233, on Windows Vista and 7 its ordinal is 261.
Its function prototype (from Airesoft) is:
HRESULT WINAPI SHGetUserPicturePath (
LPCWSTR pwszPicOrUserName,
DWORD sguppFlags,
LPWSTR pwszPicPath,
UINT picPathLen
)
You can use this function to retrieve the path where the user picture is stored. Just pass the user name as pwszPicOrUserName, the buffer where you want to store the path to the picture as pwszPicPath and the size of the buffer in chars as picPathLen. You can set sguppFlags to 0 or to any of the other flags posssible.
There is also a undocumented function which you can use in order to set the user picture of a user. Its ordinal is 234 on Windows XP, 262 on Windows Vista and Windows 7.
Its function prototype (from Airesoft) is:
HRESULT WINAPI SHSetUserPicturePath (
LPWSTR pwszAcctName,
DWORD reserved,
LPCWSTR pwszPictureFile
)
Pass the name of the user whose picture should be changed as pwszAcctName and the path to the picture you want to set as pwszPictureFile. Set reserved to 0. You have to initialize COM prior to calling this function.
According to Microsoft you should not rely on undocumented function because they can be removed or changed with any patch which is installed on Windows.
According to MSDN:
In Windows 7 or later, each user profile has an associated image
presented as a user tile. These tiles appear to users on the User
Accounts Control Panel item and its Manage Accounts subpage.. The
image files for the default Guest and default User accounts also
appear here if you have Administrator access rights.
....
The user's tile image is stored as
C:\Users\<username>\Local\Temp folder as .bmp. Any slash
characters () are converted to plus sign characters (+). For example,
DOMAIN\user is converted to DOMAIN+user.
I could not find an API to obtain the image and since the official documentation is calling out this implementation detail I think that means that you are safe to rely on it. That is I think this is a supported way to obtain the tile image.

Find out what process registered a global hotkey? (Windows API)

As far as I've been able to find out, Windows doesn't offer an API function to tell what application has registered a global hotkey (via RegisterHotkey). I can only find out that a hotkey is registered if RegisterHotkey returns false, but not who "owns" the hotkey.
In the absence of a direct API, could there be a roundabout way? Windows maintains the handle associated with each registred hotkey - it's a little maddening that there should be no way of getting at this information.
Example of something that likely wouldn't work: send (simulate) a registered hotkey, then intercept the hotkey message Windows will send to the process that registered it. First, I don't think intercepting the message would reveal the destination window handle. Second, even if it were possible, it would be a bad thing to do, since sending hotkeys would trigger all sorts of potentially unwanted activity from various programs.
It's nothing critical, but I've seen frequent requests for such functionality, and have myself been a victim of applications that register hotkeys without even disclosing it anywhere in the UI or docs.
(Working in Delphi, and no more than an apprentice at WinAPI, please be kind.)
One possible way is to use the Visual Studio tool Spy++.
Give this a try:
Run the tool (for me, it's at C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\Common7\Tools\spyxx_amd64.exe or you can download it). Note: there is spyxx.exe (32-bit version) and spyxx_amd64.exe (64-bit version) - if you don't see anything in 64-bit use the 32-bit version (ie.catches messages only in same architecture)
In the menu bar, select Spy -> Log messages... (or hit Ctrl + M)
Check All Windows in System in the Additional Windows frame
Switch to the Messages tab
Click the Clear All button
Select WM_HOTKEY in the listbox, or check Keyboard in Message Groups (if you're OK with more potential noise)
Click the OK button
Press the hotkey in question (Win + R, for example)
Select the WM_HOTKEY line in the Messages (All Windows) window, right click, and select Properties... in the context menu
In the Message Properties dialog, click the Window Handle link (this will be the handle for the window that received the message)
Click the Synchronize button on the Window Properties dialog. This will show the window in the main Spy++ window treeview (if it's windows itself or some popup application it shows nothing).
On the Window Properties dialog, select the Process tab
Click the Process ID link. This will show you the process (In my Win + R case: EXPLORER)
Your question piqued my interest, so I've done a bit of digging and while, unfortunately I don't have a proper answer for you, I thought I'd share what I have.
I found this example of creating keyboard hook (in Delphi) written in 1998, but is compilable in Delphi 2007 with a couple of tweaks.
It's a DLL with a call to SetWindowsHookEx that passes through a callback function, which can then intercept key strokes: In this case, it's tinkering with them for fun, changing left cursor to right, etc. A simple app then calls the DLL and reports back its results based on a TTimer event. If you're interested I can post the Delphi 2007 based code.
It's well documented and commented and you potentially could use it as a basis of working out where a key press is going. If you could get the handle of the application that sent the key strokes, you could track it back that way. With that handle you'd be able to get the information you need quite easily.
Other apps have tried determining hotkeys by going through their Shortcuts since they can contain a Shortcut key, which is just another term for hotkey. However most applications don't tend to set this property so it might not return much. If you are interested in that route, Delphi has access to IShellLink COM interface which you could use to load a shortcut up from and get its hotkey:
uses ShlObj, ComObj, ShellAPI, ActiveX, CommCtrl;
procedure GetShellLinkHotKey;
var
LinkFile : WideString;
SL: IShellLink;
PF: IPersistFile;
HotKey : Word;
HotKeyMod: Byte;
HotKeyText : string;
begin
LinkFile := 'C:\Temp\Temp.lnk';
OleCheck(CoCreateInstance(CLSID_ShellLink, nil, CLSCTX_INPROC_SERVER, IShellLink, SL));
// The IShellLink implementer must also support the IPersistFile
// interface. Get an interface pointer to it.
PF := SL as IPersistFile;
// Load file into IPersistFile object
OleCheck(PF.Load(PWideChar(LinkFile), STGM_READ));
// Resolve the link by calling the Resolve interface function.
OleCheck(SL.Resolve(0, SLR_ANY_MATCH or SLR_NO_UI));
// Get hotkey info
OleCheck(SL.GetHotKey(HotKey));
// Extract the HotKey and Modifier properties.
HotKeyText := '';
HotKeyMod := Hi(HotKey);
if (HotKeyMod and HOTKEYF_ALT) = HOTKEYF_ALT then
HotKeyText := 'ALT+';
if (HotKeyMod and HOTKEYF_CONTROL) = HOTKEYF_CONTROL then
HotKeyText := HotKeyText + 'CTRL+';
if (HotKeyMod and HOTKEYF_SHIFT) = HOTKEYF_SHIFT then
HotKeyText := HotKeyText + 'SHIFT+';
if (HotKeyMod and HOTKEYF_EXT) = HOTKEYF_EXT then
HotKeyText := HotKeyText + 'Extended+';
HotKeyText := HotKeyText + Char(Lo(HotKey));
if (HotKeyText = '') or (HotKeyText = #0) then
HotKeyText := 'None';
ShowMessage('Shortcut Key - ' + HotKeyText);
end;
If you've got access to Safari Books Online, there is a good section about working with shortcuts / shell links in the Borland Delphi 6 Developer's Guide by Steve Teixeira and Xavier Pacheco. My example above is a butchered version from there and this site.
Hope that helps!
After some research, it appears that you'd need to get access to the internal structure that MS uses to store the hotkeys. ReactOS has a clean room implementation that implements the GetHotKey call by iterating an internal list and extracting the hotkey that matches the parameters to the call.
Depending on how close ReactOS' implementation is to the MS implementation, you may be able to poke around in memory to find the structure, but that's over my head...
BOOL FASTCALL
GetHotKey (UINT fsModifiers,
UINT vk,
struct _ETHREAD **Thread,
HWND *hWnd,
int *id)
{
PHOT_KEY_ITEM HotKeyItem;
LIST_FOR_EACH(HotKeyItem, &gHotkeyList, HOT_KEY_ITEM, ListEntry)
{
if (HotKeyItem->fsModifiers == fsModifiers &&
HotKeyItem->vk == vk)
{
if (Thread != NULL)
*Thread = HotKeyItem->Thread;
if (hWnd != NULL)
*hWnd = HotKeyItem->hWnd;
if (id != NULL)
*id = HotKeyItem->id;
return TRUE;
}
}
return FALSE;
}
I presume this thread on sysinternals was asked by someone related to this question, but I thought I'd link to it anyway to keep the two together. The thread looks very intriguing, but I suspect that some deep dive spelunking would need to happen to figure this out without access to the MS internals.
Off the top of my head, you might try enumerating all windows with EnumWindows, then in the callback, send WM_GETHOTKEY to each window.
Edit: Apparrently I was wrong about that. MSDN has more information:
WM_HOTKEY is unrelated to the WM_GETHOTKEY and WM_SETHOTKEY hot keys. The WM_HOTKEY message is sent for generic hot keys while the WM_SETHOTKEY and WM_GETHOTKEY messages relate to window activation hot keys.
Note: Here is a program purporting to have the functionality you are looking for. You could try decompiling it.
Another thread mentions a global NT level keyboard hook:
Re-assign/override hotkey (Win + L) to lock windows
maybe you can get the handle of the process that called the hook that way, which you can then resolve to the process name
(disclaimer: I had it in my bookmarks, haven't really tried/tested)
I know you can intercept the stream of messages in any window within your own process - what we used to call subclassing in VB6. (Though I do not remember the function, perhaps SetWindowLong?) I am unsure if you can do this for windows outside your own process. But for the sake of this post lets assume you find a way to do that. Then you can simply intercept the messages for all top level windows, monitor for the WM_HOTKEY message. You wouldn't be able to know all the keys right off the bat, but as they were pressed you could easily figure out what application was using them. If you persisted your results to disk and reloaded each time your monitor application was run you could increase the performance of your application over time.
This doesn't exactly answer the part of the question that is about the Windows API, but it answers the part of the question that is about a list of global hotkeys and the applications that "own" them.
The free Hotkey Explorer at http://hkcmdr.anymania.com/ shows a list of all global hotkeys and the applications that own them. This just has helped me figure out why an application-specific shortcut key stopped working and how to fix it (by reconfiguring the registered global hotkey in the app that had it registered), within a few seconds.

Resources