Skip to content

Commit c6c3855

Browse files
committed
[LPK] Rewrite LPK_ApplyMirroring
Don't use version.dll APIs! This code runs during DLL process attach and all it can call is ntdll and kernel32. There is still a hack, that calls GetFileVersionInfoSizeW, which is required for some reason, otherwise comctl32_winetest tooltips starts to fail. It has possibly something to do with the initialization order of loaded DLLs.
1 parent 617f3bd commit c6c3855

File tree

1 file changed

+91
-40
lines changed

1 file changed

+91
-40
lines changed

dll/win32/lpk/lpk.c

Lines changed: 91 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -110,53 +110,104 @@ static void LPK_DrawUnderscore(HDC hdc, int x, int y, LPCWSTR str, int count, in
110110
DeleteObject(hpen);
111111
}
112112

113-
/* Code taken from the GetProcessDefaultLayout() function from Wine's user32
114-
* Wine version 3.17
113+
static
114+
BOOL
115+
GetProcessImageVersionInfo(
116+
_Out_ PVOID* pFileInfo,
117+
_Out_ LPDWORD pdwLen)
118+
{
119+
HRSRC hResource;
120+
DWORD dwResourceSize;
121+
HGLOBAL hResourceData;
122+
PVOID pvResourceData;
123+
124+
*pFileInfo = NULL;
125+
*pdwLen = 0;
126+
127+
/* Try to get the VersionInfo resource */
128+
hResource = FindResourceW(NULL, MAKEINTRESOURCEW(VS_VERSION_INFO), RT_VERSION);
129+
if (hResource == NULL)
130+
return FALSE;
131+
132+
/* Get resource size and do basic size check */
133+
dwResourceSize = SizeofResource(NULL, hResource);
134+
if (dwResourceSize < sizeof(VS_FIXEDFILEINFO))
135+
return FALSE;
136+
137+
/* Load the resource */
138+
hResourceData = LoadResource(NULL, hResource);
139+
if (hResourceData == NULL)
140+
return FALSE;
141+
142+
/* Lock the resource */
143+
pvResourceData = LockResource(hResourceData);
144+
if (pvResourceData == NULL)
145+
return FALSE;
146+
147+
*pFileInfo = pvResourceData;
148+
*pdwLen = dwResourceSize;
149+
return TRUE;
150+
}
151+
152+
/* Checks the VersionInfo of the main executable for RTL layout
153+
* and applies it to the process if found.
154+
* See https://web.archive.org/web/20050207001156/http://www.microsoft.com/globaldev/getwr/steps/WRG_mirror.mspx
115155
*
116156
* This function should be called from LpkInitialize(),
117157
* which is in turn called by GdiInitializeLanguagePack() (from gdi32).
118158
* TODO: Move call from LpkDllInitialize() to LpkInitialize() when latter
119159
* function is implemented.
120160
*/
121-
static void LPK_ApplyMirroring()
161+
static void LPK_ApplyMirroring(void)
122162
{
123-
static const WCHAR translationW[] = { '\\','V','a','r','F','i','l','e','I','n','f','o',
124-
'\\','T','r','a','n','s','l','a','t','i','o','n', 0 };
125-
static const WCHAR filedescW[] = { '\\','S','t','r','i','n','g','F','i','l','e','I','n','f','o',
126-
'\\','%','0','4','x','%','0','4','x',
127-
'\\','F','i','l','e','D','e','s','c','r','i','p','t','i','o','n',0 };
128-
WCHAR *str, buffer[MAX_PATH];
129-
#ifdef __REACTOS__
130-
DWORD i, version_layout = 0;
131-
UINT len;
132-
#else
133-
DWORD i, len, version_layout = 0;
134-
#endif
135-
DWORD user_lang = GetUserDefaultLangID();
136-
DWORD *languages;
137-
void *data = NULL;
138-
139-
GetModuleFileNameW( 0, buffer, MAX_PATH );
140-
if (!(len = GetFileVersionInfoSizeW( buffer, NULL ))) goto done;
141-
if (!(data = HeapAlloc( GetProcessHeap(), 0, len ))) goto done;
142-
if (!GetFileVersionInfoW( buffer, 0, len, data )) goto done;
143-
if (!VerQueryValueW( data, translationW, (void **)&languages, &len ) || !len) goto done;
144-
145-
len /= sizeof(DWORD);
146-
for (i = 0; i < len; i++) if (LOWORD(languages[i]) == user_lang) break;
147-
if (i == len) /* try neutral language */
148-
for (i = 0; i < len; i++)
149-
if (LOWORD(languages[i]) == MAKELANGID( PRIMARYLANGID(user_lang), SUBLANG_NEUTRAL )) break;
150-
if (i == len) i = 0; /* default to the first one */
151-
152-
sprintfW( buffer, filedescW, LOWORD(languages[i]), HIWORD(languages[i]) );
153-
if (!VerQueryValueW( data, buffer, (void **)&str, &len )) goto done;
154-
TRACE( "found description %s\n", debugstr_w( str ));
155-
if (str[0] == 0x200e && str[1] == 0x200e) version_layout = LAYOUT_RTL;
156-
157-
done:
158-
HeapFree( GetProcessHeap(), 0, data );
159-
SetProcessDefaultLayout(version_layout);
163+
PWSTR pwstrFileDescription = NULL;
164+
PVOID pvFileInfo;
165+
DWORD cbFileInfoSize;
166+
167+
/* HACK!!!! For some reason this call, that does nothing, is required,
168+
otherwise comctl32_winetest tooltips starts to fail. */
169+
GetFileVersionInfoSizeW(L"", NULL );
170+
171+
/* Get the VersionInfo resource of the main executable */
172+
if (!GetProcessImageVersionInfo(&pvFileInfo, &cbFileInfoSize))
173+
{
174+
return;
175+
}
176+
177+
/* The length (in WCHARs) of the key name, including the null-terminator */
178+
const DWORD cchKeyLength = sizeof(L"FileDescription") / sizeof(WCHAR);
179+
180+
/* Calculate the required length (in WCHARs) for a FileDescription entry:
181+
The key (incl. null) + alignment byte + 2 LTR markers (note: the key
182+
is always unaligned by 2 bytes) */
183+
const DWORD cchRequired = cchKeyLength + 1 + 2;
184+
185+
/* Scan the VersionInfo resource for the "FileDescription" key */
186+
PWSTR pwchCurrent = (PWSTR)pvFileInfo;
187+
DWORD cchRemining = cbFileInfoSize / sizeof(WCHAR);
188+
while (cchRemining >= cchRequired)
189+
{
190+
/* Compare current position with the "FileDescription" key */
191+
if (lstrcmpW(pwchCurrent, L"FileDescription") == 0)
192+
{
193+
/* The value starts after the key, the Null-terminator and 1 WCHAR alignment */
194+
pwstrFileDescription = pwchCurrent + cchKeyLength + 1;
195+
break;
196+
}
197+
198+
/* Move to next position */
199+
pwchCurrent++;
200+
cchRemining--;
201+
}
202+
203+
/* Check if the description starts with 2 LTR markers (u200E).
204+
Yes, this is how MS marks the layout as RTL (see article linked above). */
205+
if ((pwstrFileDescription != NULL) &&
206+
(pwstrFileDescription[0] == 0x200E) &&
207+
(pwstrFileDescription[1] == 0x200E))
208+
{
209+
SetProcessDefaultLayout(LAYOUT_RTL);
210+
}
160211
}
161212

162213
BOOL

0 commit comments

Comments
 (0)