1 // VirtualDub - Video processing and capture application
2 // System library component
3 // Copyright (C) 1998-2004 Avery Lee, All Rights Reserved.
5 // Beginning with 1.6.0, the VirtualDub system library is licensed
6 // differently than the remainder of VirtualDub. This particular file is
7 // thus licensed as follows (the "zlib" license):
9 // This software is provided 'as-is', without any express or implied
10 // warranty. In no event will the authors be held liable for any
11 // damages arising from the use of this software.
13 // Permission is granted to anyone to use this software for any purpose,
14 // including commercial applications, and to alter it and redistribute it
15 // freely, subject to the following restrictions:
17 // 1. The origin of this software must not be misrepresented; you must
18 // not claim that you wrote the original software. If you use this
19 // software in a product, an acknowledgment in the product
20 // documentation would be appreciated but is not required.
21 // 2. Altered source versions must be plainly marked as such, and must
22 // not be misrepresented as being the original software.
23 // 3. This notice may not be removed or altered from any source
27 #include <vd2/system/w32assist.h>
28 #include <vd2/system/seh.h>
29 #include <vd2/system/text.h>
30 #include <vd2/system/vdstdc.h>
31 #include <vd2/system/vdstl.h>
33 bool VDIsForegroundTaskW32() {
34 HWND hwndFore
= GetForegroundWindow();
39 DWORD dwProcessId
= 0;
40 GetWindowThreadProcessId(hwndFore
, &dwProcessId
);
42 return dwProcessId
== GetCurrentProcessId();
45 LPVOID
VDConvertThreadToFiberW32(LPVOID parm
) {
46 typedef LPVOID (WINAPI
*tpConvertThreadToFiber
)(LPVOID p
);
47 static tpConvertThreadToFiber ctof
= (tpConvertThreadToFiber
)GetProcAddress(GetModuleHandle("kernel32"), "ConvertThreadToFiber");
55 void VDSwitchToFiberW32(LPVOID fiber
) {
56 typedef void (WINAPI
*tpSwitchToFiber
)(LPVOID p
);
57 static tpSwitchToFiber stof
= (tpSwitchToFiber
)GetProcAddress(GetModuleHandle("kernel32"), "SwitchToFiber");
63 int VDGetSizeOfBitmapHeaderW32(const BITMAPINFOHEADER
*pHdr
) {
66 if ((pHdr
->biCompression
== BI_RGB
|| pHdr
->biCompression
== BI_RLE4
|| pHdr
->biCompression
== BI_RLE8
) && pHdr
->biBitCount
<= 8) {
67 palents
= pHdr
->biClrUsed
;
69 palents
= 1 << pHdr
->biBitCount
;
71 int size
= pHdr
->biSize
+ palents
* sizeof(RGBQUAD
);
73 if (pHdr
->biSize
< sizeof(BITMAPV4HEADER
) && pHdr
->biCompression
== BI_BITFIELDS
)
74 size
+= sizeof(DWORD
) * 3;
79 void VDSetWindowTextW32(HWND hwnd
, const wchar_t *s
) {
80 if (VDIsWindowsNT()) {
81 SetWindowTextW(hwnd
, s
);
83 SetWindowTextA(hwnd
, VDTextWToA(s
).c_str());
87 void VDSetWindowTextFW32(HWND hwnd
, const wchar_t *format
, ...) {
90 va_start(val
, format
);
93 int r
= vdvswprintf(buf
, 512, format
, val
);
95 if ((unsigned)r
< 512) {
96 VDSetWindowTextW32(hwnd
, buf
);
103 s
.append_vsprintf(format
, val
);
104 VDSetWindowTextW32(hwnd
, s
.c_str());
109 VDStringA
VDGetWindowTextAW32(HWND hwnd
) {
112 int len
= GetWindowTextLengthA(hwnd
);
115 vdblock
<char> tmp(len
+ 1);
116 len
= GetWindowTextA(hwnd
, tmp
.data(), tmp
.size());
118 const char *s
= tmp
.data();
119 VDStringA
text(s
, s
+len
);
121 } else if (len
> 0) {
122 len
= GetWindowTextA(hwnd
, buf
, 512);
124 return VDStringA(buf
, buf
+ len
);
130 VDStringW
VDGetWindowTextW32(HWND hwnd
) {
136 if (VDIsWindowsNT()) {
137 int len
= GetWindowTextLengthW(hwnd
);
140 vdblock
<wchar_t> tmp(len
+ 1);
141 len
= GetWindowTextW(hwnd
, tmp
.data(), tmp
.size());
143 VDStringW
text(tmp
.data(), len
);
145 } else if (len
> 0) {
146 len
= GetWindowTextW(hwnd
, buf
.w
, 256);
148 VDStringW
text(buf
.w
, len
);
152 int len
= GetWindowTextLengthA(hwnd
);
155 vdblock
<char> tmp(len
+ 1);
156 len
= GetWindowTextA(hwnd
, tmp
.data(), tmp
.size());
158 VDStringW
text(VDTextAToW(tmp
.data(), len
));
160 } else if (len
> 0) {
161 len
= GetWindowTextA(hwnd
, buf
.a
, 512);
163 VDStringW
text(VDTextAToW(buf
.a
, len
));
171 void VDAppendMenuW32(HMENU hmenu
, UINT flags
, UINT id
, const wchar_t *text
){
172 if (VDIsWindowsNT()) {
173 AppendMenuW(hmenu
, flags
, id
, text
);
175 AppendMenuA(hmenu
, flags
, id
, VDTextWToA(text
).c_str());
179 void VDAppendMenuSeparatorW32(HMENU hmenu
) {
180 int pos
= GetMenuItemCount(hmenu
);
184 if (VDIsWindowsNT()) {
186 vdfastfixedvector
<wchar_t, 256> bufW
;
188 mmiW
.cbSize
= MENUITEMINFO_SIZE_VERSION_400W
;
189 mmiW
.fMask
= MIIM_TYPE
;
190 mmiW
.fType
= MFT_SEPARATOR
;
192 InsertMenuItemW(hmenu
, pos
, TRUE
, &mmiW
);
196 mmiA
.cbSize
= MENUITEMINFO_SIZE_VERSION_400A
;
197 mmiA
.fMask
= MIIM_TYPE
;
198 mmiA
.fType
= MFT_SEPARATOR
;
200 InsertMenuItemA(hmenu
, pos
, TRUE
, &mmiA
);
204 void VDCheckMenuItemByPositionW32(HMENU hmenu
, uint32 pos
, bool checked
) {
205 CheckMenuItem(hmenu
, pos
, checked
? MF_BYPOSITION
|MF_CHECKED
: MF_BYPOSITION
|MF_UNCHECKED
);
208 void VDCheckMenuItemByCommandW32(HMENU hmenu
, UINT cmd
, bool checked
) {
209 CheckMenuItem(hmenu
, cmd
, checked
? MF_BYCOMMAND
|MF_CHECKED
: MF_BYCOMMAND
|MF_UNCHECKED
);
212 void VDCheckRadioMenuItemByPositionW32(HMENU hmenu
, uint32 pos
, bool checked
) {
215 mii
.cbSize
= sizeof(MENUITEMINFOA
);
216 mii
.fMask
= MIIM_FTYPE
| MIIM_STATE
;
217 if (GetMenuItemInfo(hmenu
, pos
, TRUE
, &mii
)) {
218 mii
.fType
|= MFT_RADIOCHECK
;
219 mii
.fState
&= ~MFS_CHECKED
;
221 mii
.fState
|= MFS_CHECKED
;
222 SetMenuItemInfo(hmenu
, pos
, TRUE
, &mii
);
226 void VDCheckRadioMenuItemByCommandW32(HMENU hmenu
, UINT cmd
, bool checked
) {
229 mii
.cbSize
= sizeof(MENUITEMINFOA
);
230 mii
.fMask
= MIIM_FTYPE
| MIIM_STATE
;
231 if (GetMenuItemInfo(hmenu
, cmd
, FALSE
, &mii
)) {
232 mii
.fType
|= MFT_RADIOCHECK
;
233 mii
.fState
&= ~MFS_CHECKED
;
235 mii
.fState
|= MFS_CHECKED
;
236 SetMenuItemInfo(hmenu
, cmd
, FALSE
, &mii
);
240 void VDEnableMenuItemByCommandW32(HMENU hmenu
, UINT cmd
, bool checked
) {
241 EnableMenuItem(hmenu
, cmd
, checked
? MF_BYCOMMAND
|MF_ENABLED
: MF_BYCOMMAND
|MF_GRAYED
);
244 VDStringW
VDGetMenuItemTextByCommandW32(HMENU hmenu
, UINT cmd
) {
247 if (VDIsWindowsNT()) {
249 vdfastfixedvector
<wchar_t, 256> bufW
;
251 mmiW
.cbSize
= MENUITEMINFO_SIZE_VERSION_400W
;
252 mmiW
.fMask
= MIIM_TYPE
;
253 mmiW
.fType
= MFT_STRING
;
254 mmiW
.dwTypeData
= NULL
;
255 mmiW
.cch
= 0; // required to avoid crash on NT4
257 if (GetMenuItemInfoW(hmenu
, cmd
, FALSE
, &mmiW
)) {
258 bufW
.resize(mmiW
.cch
+ 1, 0);
260 mmiW
.dwTypeData
= bufW
.data();
262 if (GetMenuItemInfoW(hmenu
, cmd
, FALSE
, &mmiW
))
267 vdfastfixedvector
<char, 256> bufA
;
269 mmiA
.cbSize
= MENUITEMINFO_SIZE_VERSION_400A
;
270 mmiA
.fMask
= MIIM_TYPE
;
271 mmiA
.fType
= MFT_STRING
;
272 mmiA
.dwTypeData
= NULL
;
274 if (GetMenuItemInfoA(hmenu
, cmd
, FALSE
, &mmiA
)) {
275 bufA
.resize(mmiA
.cch
+ 1, 0);
277 mmiA
.dwTypeData
= bufA
.data();
279 if (GetMenuItemInfoA(hmenu
, cmd
, FALSE
, &mmiA
))
280 s
= VDTextAToW(bufA
.data());
287 void VDSetMenuItemTextByCommandW32(HMENU hmenu
, UINT cmd
, const wchar_t *text
) {
288 if (VDIsWindowsNT()) {
291 mmiW
.cbSize
= MENUITEMINFO_SIZE_VERSION_400W
;
292 mmiW
.fMask
= MIIM_TYPE
;
293 mmiW
.fType
= MFT_STRING
;
294 mmiW
.dwTypeData
= (LPWSTR
)text
;
296 SetMenuItemInfoW(hmenu
, cmd
, FALSE
, &mmiW
);
299 VDStringA
textA(VDTextWToA(text
));
301 mmiA
.cbSize
= MENUITEMINFO_SIZE_VERSION_400A
;
302 mmiA
.fMask
= MIIM_TYPE
;
303 mmiA
.fType
= MFT_STRING
;
304 mmiA
.dwTypeData
= (LPSTR
)textA
.c_str();
306 SetMenuItemInfoA(hmenu
, cmd
, FALSE
, &mmiA
);
310 LRESULT
VDDualCallWindowProcW32(WNDPROC wp
, HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
) {
311 return (IsWindowUnicode(hwnd
) ? CallWindowProcW
: CallWindowProcA
)(wp
, hwnd
, msg
, wParam
, lParam
);
314 LRESULT
VDDualDefWindowProcW32(HWND hwnd
, UINT msg
, WPARAM wParam
, LPARAM lParam
) {
315 return IsWindowUnicode(hwnd
) ? DefWindowProcW(hwnd
, msg
, wParam
, lParam
) : DefWindowProcA(hwnd
, msg
, wParam
, lParam
);
318 EXECUTION_STATE
VDSetThreadExecutionStateW32(EXECUTION_STATE esFlags
) {
319 EXECUTION_STATE es
= 0;
321 // SetThreadExecutionState(): requires Windows 98+/2000+.
322 typedef EXECUTION_STATE (WINAPI
*tSetThreadExecutionState
)(EXECUTION_STATE
);
323 static tSetThreadExecutionState pFunc
= (tSetThreadExecutionState
)GetProcAddress(GetModuleHandle("kernel32"), "SetThreadExecutionState");
331 bool VDSetFilePointerW32(HANDLE h
, sint64 pos
, DWORD dwMoveMethod
) {
332 LONG posHi
= (LONG
)(pos
>> 32);
333 DWORD result
= SetFilePointer(h
, (LONG
)pos
, &posHi
, dwMoveMethod
);
335 if (result
!= INVALID_SET_FILE_POINTER
)
338 DWORD dwError
= GetLastError();
340 return (dwError
== NO_ERROR
);
343 bool VDGetFileSizeW32(HANDLE h
, sint64
& size
) {
345 DWORD dwSizeLow
= GetFileSize(h
, &dwSizeHigh
);
347 if (dwSizeLow
== (DWORD
)-1 && GetLastError() != NO_ERROR
)
350 size
= dwSizeLow
+ ((sint64
)dwSizeHigh
<< 32);
354 #if !defined(_MSC_VER) || _MSC_VER < 1300
355 HMODULE
VDGetLocalModuleHandleW32() {
356 MEMORY_BASIC_INFORMATION meminfo
;
357 static HMODULE shmod
= (VirtualQuery((HINSTANCE
)&VDGetLocalModuleHandleW32
, &meminfo
, sizeof meminfo
), (HMODULE
)meminfo
.AllocationBase
);
363 bool VDDrawTextW32(HDC hdc
, const wchar_t *s
, int nCount
, LPRECT lpRect
, UINT uFormat
) {
365 if (VDIsWindowsNT()) {
366 // If multiline and vcentered (not normally supported...)
367 if (!((uFormat
^ DT_VCENTER
) & (DT_VCENTER
|DT_SINGLELINE
))) {
368 uFormat
&= ~DT_VCENTER
;
371 if (!DrawTextW(hdc
, s
, nCount
, &r
, uFormat
| DT_CALCRECT
))
374 int dx
= ((lpRect
->right
- lpRect
->left
) - (r
.right
- r
.left
)) >> 1;
375 int dy
= ((lpRect
->bottom
- lpRect
->top
) - (r
.bottom
- r
.top
)) >> 1;
384 return !!DrawTextW(hdc
, s
, nCount
, lpRect
, uFormat
);
386 VDStringA
strA(VDTextWToA(s
, nCount
));
388 // If multiline and vcentered (not normally supported...)
389 if (!((uFormat
^ DT_VCENTER
) & (DT_VCENTER
|DT_SINGLELINE
))) {
390 uFormat
&= ~DT_VCENTER
;
393 if (!DrawTextA(hdc
, strA
.data(), strA
.size(), &r
, uFormat
| DT_CALCRECT
))
396 int dx
= ((lpRect
->right
- lpRect
->left
) - (r
.right
- r
.left
)) >> 1;
397 int dy
= ((lpRect
->bottom
- lpRect
->top
) - (r
.bottom
- r
.top
)) >> 1;
406 return !!DrawTextA(hdc
, strA
.data(), strA
.size(), lpRect
, uFormat
);
410 bool VDPatchModuleImportTableW32(HMODULE hmod
, const char *srcModule
, const char *name
, void *pCompareValue
, void *pNewValue
, void *volatile *ppOldValue
) {
411 char *pBase
= (char *)hmod
;
414 // The PEheader offset is at hmod+0x3c. Add the size of the optional header
415 // to step to the section headers.
417 const uint32 peoffset
= ((const long *)pBase
)[15];
418 const uint32 signature
= *(uint32
*)(pBase
+ peoffset
);
420 if (signature
!= IMAGE_NT_SIGNATURE
)
423 const IMAGE_FILE_HEADER
*pHeader
= (const IMAGE_FILE_HEADER
*)(pBase
+ peoffset
+ 4);
425 // Verify the PE optional structure.
427 if (pHeader
->SizeOfOptionalHeader
< 104)
430 // Find import header.
432 const IMAGE_IMPORT_DESCRIPTOR
*pImportDir
;
435 switch(*(short *)((char *)pHeader
+ IMAGE_SIZEOF_FILE_HEADER
)) {
438 case IMAGE_NT_OPTIONAL_HDR64_MAGIC
:
440 const IMAGE_OPTIONAL_HEADER64
*pOpt
= (IMAGE_OPTIONAL_HEADER64
*)((const char *)pHeader
+ sizeof(IMAGE_FILE_HEADER
));
442 if (pOpt
->NumberOfRvaAndSizes
< 2)
445 pImportDir
= (const IMAGE_IMPORT_DESCRIPTOR
*)(pBase
+ pOpt
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
].VirtualAddress
);
446 nImports
= pOpt
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
].Size
/ sizeof(IMAGE_IMPORT_DESCRIPTOR
);
450 case IMAGE_NT_OPTIONAL_HDR32_MAGIC
:
452 const IMAGE_OPTIONAL_HEADER32
*pOpt
= (IMAGE_OPTIONAL_HEADER32
*)((const char *)pHeader
+ sizeof(IMAGE_FILE_HEADER
));
454 if (pOpt
->NumberOfRvaAndSizes
< 2)
457 pImportDir
= (const IMAGE_IMPORT_DESCRIPTOR
*)(pBase
+ pOpt
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
].VirtualAddress
);
458 nImports
= pOpt
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_IMPORT
].Size
/ sizeof(IMAGE_IMPORT_DESCRIPTOR
);
463 default: // reject PE32+
467 // Hmmm... no imports?
469 if ((const char *)pImportDir
== pBase
)
472 // Scan down the import entries. We are looking for MSVFW32.
476 for(i
=0; i
<nImports
; ++i
) {
477 if (!_stricmp(pBase
+ pImportDir
[i
].Name
, srcModule
))
484 // Found it. Start scanning MSVFW32 imports until we find DrawDibDraw.
486 const long *pImports
= (const long *)(pBase
+ pImportDir
[i
].OriginalFirstThunk
);
487 void * volatile *pVector
= (void * volatile *)(pBase
+ pImportDir
[i
].FirstThunk
);
490 if (*pImports
>= 0) {
491 const char *pName
= pBase
+ *pImports
+ 2;
493 if (!strcmp(pName
, name
)) {
495 // Found it! Reset the protection.
499 if (VirtualProtect((void *)pVector
, sizeof(void *), PAGE_EXECUTE_READWRITE
, &dwOldProtect
)) {
502 void *old
= *pVector
;
503 if (pCompareValue
&& pCompareValue
!= old
)
507 if (old
== VDAtomicCompareExchangePointer(pVector
, pNewValue
, old
))
511 *pVector
= pNewValue
;
514 VirtualProtect((void *)pVector
, sizeof(void *), dwOldProtect
, &dwOldProtect
);
526 } vd_seh_guard_except
{
532 bool VDPatchModuleExportTableW32(HMODULE hmod
, const char *name
, void *pCompareValue
, void *pNewValue
, void *volatile *ppOldValue
) {
533 char *pBase
= (char *)hmod
;
536 // The PEheader offset is at hmod+0x3c. Add the size of the optional header
537 // to step to the section headers.
539 const uint32 peoffset
= ((const long *)pBase
)[15];
540 const uint32 signature
= *(uint32
*)(pBase
+ peoffset
);
542 if (signature
!= IMAGE_NT_SIGNATURE
)
545 const IMAGE_FILE_HEADER
*pHeader
= (const IMAGE_FILE_HEADER
*)(pBase
+ peoffset
+ 4);
547 // Verify the PE optional structure.
549 if (pHeader
->SizeOfOptionalHeader
< 104)
552 // Find export directory.
554 const IMAGE_EXPORT_DIRECTORY
*pExportDir
;
556 switch(*(short *)((char *)pHeader
+ IMAGE_SIZEOF_FILE_HEADER
)) {
559 case IMAGE_NT_OPTIONAL_HDR64_MAGIC
:
561 const IMAGE_OPTIONAL_HEADER64
*pOpt
= (IMAGE_OPTIONAL_HEADER64
*)((const char *)pHeader
+ sizeof(IMAGE_FILE_HEADER
));
563 if (pOpt
->NumberOfRvaAndSizes
< 1)
566 DWORD exportDirRVA
= pOpt
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXPORT
].VirtualAddress
;
571 pExportDir
= (const IMAGE_EXPORT_DIRECTORY
*)(pBase
+ exportDirRVA
);
575 case IMAGE_NT_OPTIONAL_HDR32_MAGIC
:
577 const IMAGE_OPTIONAL_HEADER32
*pOpt
= (IMAGE_OPTIONAL_HEADER32
*)((const char *)pHeader
+ sizeof(IMAGE_FILE_HEADER
));
579 if (pOpt
->NumberOfRvaAndSizes
< 1)
582 DWORD exportDirRVA
= pOpt
->DataDirectory
[IMAGE_DIRECTORY_ENTRY_EXPORT
].VirtualAddress
;
587 pExportDir
= (const IMAGE_EXPORT_DIRECTORY
*)(pBase
+ exportDirRVA
);
592 default: // reject PE32+
596 // Scan for the export name.
597 DWORD nameCount
= pExportDir
->AddressOfNames
;
598 const DWORD
*nameRVAs
= (const DWORD
*)(pBase
+ pExportDir
->AddressOfNames
);
599 const WORD
*nameOrdinals
= (const WORD
*)(pBase
+ pExportDir
->AddressOfNameOrdinals
);
600 DWORD
*functionTable
= (DWORD
*)(pBase
+ pExportDir
->AddressOfFunctions
);
602 for(DWORD i
=0; i
<nameCount
; ++i
) {
603 DWORD nameRVA
= nameRVAs
[i
];
604 const char *pName
= (const char *)(pBase
+ nameRVA
);
607 if (!strcmp(pName
, name
)) {
609 // name matches -- look up the function entry
610 WORD ordinal
= nameOrdinals
[i
];
611 DWORD
*pRVA
= &functionTable
[ordinal
];
613 // Reset the protection.
615 DWORD newRVA
= (DWORD
)pNewValue
- (DWORD
)pBase
;
618 if (VirtualProtect((void *)pRVA
, sizeof(DWORD
), PAGE_EXECUTE_READWRITE
, &dwOldProtect
)) {
621 DWORD oldRVA
= *pRVA
;
622 void *old
= pBase
+ oldRVA
;
623 if (pCompareValue
&& pCompareValue
!= old
)
626 *ppOldValue
= pBase
+ oldRVA
;
627 if (oldRVA
== VDAtomicInt::staticCompareExchange((volatile int *)pRVA
, newRVA
, oldRVA
))
634 VirtualProtect((void *)pRVA
, sizeof(DWORD
), dwOldProtect
, &dwOldProtect
);
642 } vd_seh_guard_except
{
648 HMODULE
VDLoadSystemLibraryW32(const char *name
) {
649 if (VDIsWindowsNT()) {
650 vdfastvector
<wchar_t> pathW(MAX_PATH
, 0);
652 size_t len
= GetSystemDirectoryW(pathW
.data(), MAX_PATH
);
657 if (len
> MAX_PATH
) {
658 pathW
.resize(len
+ 1, 0);
660 len
= GetSystemDirectoryW(pathW
.data(), len
);
661 if (!len
|| len
>= pathW
.size())
667 if (pathW
.back() != '\\')
668 pathW
.push_back('\\');
670 while(const char c
= *name
++)
675 return LoadLibraryW(pathW
.data());
677 vdfastvector
<char> pathA(MAX_PATH
, 0);
678 size_t len
= GetSystemDirectoryA(pathA
.data(), MAX_PATH
);
683 if (len
> MAX_PATH
) {
684 pathA
.resize(len
+ 1, 0);
686 len
= GetSystemDirectoryA(pathA
.data(), len
);
687 if (!len
|| len
>= pathA
.size())
693 if (pathA
.back() != '\\')
694 pathA
.push_back('\\');
696 pathA
.insert(pathA
.end(), name
, name
+ strlen(name
));
699 return LoadLibraryA(pathA
.data());