You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1147 lines
33 KiB
1147 lines
33 KiB
/****************************** Module Header ******************************\
|
|
*
|
|
* Module Name: extract.c
|
|
*
|
|
* Copyright (c) 1985 - 1999, Microsoft Corporation
|
|
*
|
|
* Icon Extraction Routines
|
|
*
|
|
* History:
|
|
\***************************************************************************/
|
|
|
|
#include "precomp.h"
|
|
#pragma hdrstop
|
|
#include "newexe.h"
|
|
|
|
/****************************************************************************
|
|
****************************************************************************/
|
|
|
|
#define ICON_MAGIC 0
|
|
#define ICO_MAGIC1 1
|
|
#define CUR_MAGIC1 2
|
|
#define BMP_MAGIC ((WORD)'B'+((WORD)'M'<<8))
|
|
#define ANI_MAGIC ((WORD)'R'+((WORD)'I'<<8))
|
|
#define ANI_MAGIC1 ((WORD)'F'+((WORD)'F'<<8))
|
|
#define ANI_MAGIC4 ((WORD)'A'+((WORD)'C'<<8))
|
|
#define ANI_MAGIC5 ((WORD)'O'+((WORD)'N'<<8))
|
|
#define MZMAGIC ((WORD)'M'+((WORD)'Z'<<8))
|
|
#define PEMAGIC ((WORD)'P'+((WORD)'E'<<8))
|
|
#define LEMAGIC ((WORD)'L'+((WORD)'E'<<8))
|
|
|
|
typedef struct new_exe NEWEXE, *LPNEWEXE;
|
|
typedef struct exe_hdr EXEHDR, *LPEXEHDR;
|
|
typedef struct rsrc_nameinfo RESNAMEINFO, *LPRESNAMEINFO;
|
|
typedef struct rsrc_typeinfo RESTYPEINFO, *LPRESTYPEINFO;
|
|
typedef struct rsrc_typeinfo UNALIGNED *ULPRESTYPEINFO;
|
|
typedef struct new_rsrc RESTABLE, *LPRESTABLE;
|
|
|
|
#define NUMBER_OF_SECTIONS(x) ((x)->FileHeader.NumberOfSections)
|
|
|
|
#define FCC(c0,c1,c2,c3) ((DWORD)(c0)|((DWORD)(c1)<<8)|((DWORD)(c2)<<16)|((DWORD)(c3)<<24))
|
|
|
|
#define COM_FILE FCC('.', 'c', 'o', 'm')
|
|
#define BAT_FILE FCC('.', 'b', 'a', 't')
|
|
#define CMD_FILE FCC('.', 'c', 'm', 'd')
|
|
#define PIF_FILE FCC('.', 'p', 'i', 'f')
|
|
#define LNK_FILE FCC('.', 'l', 'n', 'k')
|
|
#define ICO_FILE FCC('.', 'i', 'c', 'o')
|
|
#define EXE_FILE FCC('.', 'e', 'x', 'e')
|
|
|
|
|
|
#define WIN32VER30 0x00030000 // for CreateIconFromResource()
|
|
|
|
#define GET_COUNT 424242
|
|
|
|
|
|
/***************************************************************************\
|
|
* PathIsUNC
|
|
*
|
|
* Inline function to check for a double-backslash at the
|
|
* beginning of a string
|
|
*
|
|
\***************************************************************************/
|
|
|
|
__inline BOOL PathIsUNC(
|
|
LPWSTR psz)
|
|
{
|
|
return (psz[0] == L'\\' && psz[1] == L'\\');
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ReadAByte
|
|
*
|
|
* This is used to touch memory to assure that if we page-fault, it is
|
|
* outside win16lock. Most icons aren't more than two pages.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
BOOL ReadAByte(
|
|
LPCVOID pMem)
|
|
{
|
|
return ((*(PBYTE)pMem) == 0);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* RVAtoP
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
LPVOID RVAtoP(
|
|
LPVOID pBase,
|
|
DWORD rva)
|
|
{
|
|
LPEXEHDR pmz;
|
|
IMAGE_NT_HEADERS *ppe;
|
|
IMAGE_SECTION_HEADER *pSection; // section table
|
|
int i;
|
|
DWORD size;
|
|
|
|
pmz = (LPEXEHDR)pBase;
|
|
ppe = (IMAGE_NT_HEADERS*)((BYTE*)pBase + pmz->e_lfanew);
|
|
|
|
/*
|
|
* Scan the section table looking for the RVA
|
|
*/
|
|
pSection = IMAGE_FIRST_SECTION(ppe);
|
|
|
|
for (i = 0; i < NUMBER_OF_SECTIONS(ppe); i++) {
|
|
|
|
size = pSection[i].Misc.VirtualSize ?
|
|
pSection[i].Misc.VirtualSize : pSection[i].SizeOfRawData;
|
|
|
|
if (rva >= pSection[i].VirtualAddress &&
|
|
rva < pSection[i].VirtualAddress + size) {
|
|
|
|
return (LPBYTE)pBase + pSection[i].PointerToRawData + (rva - pSection[i].VirtualAddress);
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetResourceTablePE
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
LPVOID GetResourceTablePE(
|
|
LPVOID pBase)
|
|
{
|
|
LPEXEHDR pmz;
|
|
IMAGE_NT_HEADERS *ppe;
|
|
|
|
pmz = (LPEXEHDR)pBase;
|
|
ppe = (IMAGE_NT_HEADERS*)((BYTE*)pBase + pmz->e_lfanew);
|
|
|
|
if (pmz->e_magic != MZMAGIC)
|
|
return 0;
|
|
|
|
if (ppe->Signature != IMAGE_NT_SIGNATURE)
|
|
return 0;
|
|
|
|
if (ppe->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC)
|
|
{
|
|
IMAGE_NT_HEADERS64* ppe64 = (IMAGE_NT_HEADERS64*)ppe;
|
|
|
|
if (ppe64->FileHeader.SizeOfOptionalHeader < IMAGE_SIZEOF_NT_OPTIONAL64_HEADER)
|
|
{
|
|
return 0;
|
|
}
|
|
return RVAtoP(pBase, ppe64->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
|
|
}
|
|
else
|
|
{
|
|
// assume a 32-bit image
|
|
IMAGE_NT_HEADERS32* ppe32 = (IMAGE_NT_HEADERS32*)ppe;
|
|
|
|
if (ppe32->FileHeader.SizeOfOptionalHeader < IMAGE_SIZEOF_NT_OPTIONAL32_HEADER)
|
|
{
|
|
return 0;
|
|
}
|
|
return RVAtoP(pBase, ppe32->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress);
|
|
}
|
|
}
|
|
|
|
/****************************************************************************
|
|
* FindResourcePE
|
|
*
|
|
* given a PE resource directory will find a resource in it.
|
|
*
|
|
* if iResIndex < 0 we will search for the specific index
|
|
* if iResIndex >= 0 we will return the Nth index
|
|
* if iResIndex == GET_COUNT the count of resources will be returned
|
|
*
|
|
\*****************************************************************************/
|
|
|
|
LPVOID FindResourcePE(
|
|
LPVOID pBase,
|
|
LPVOID prt,
|
|
int iResIndex,
|
|
int ResType,
|
|
DWORD *pcb)
|
|
{
|
|
int i;
|
|
int cnt;
|
|
IMAGE_RESOURCE_DIRECTORY *pdir;
|
|
IMAGE_RESOURCE_DIRECTORY_ENTRY *pres;
|
|
IMAGE_RESOURCE_DATA_ENTRY UNALIGNED *pent;
|
|
|
|
pdir = (IMAGE_RESOURCE_DIRECTORY *)prt;
|
|
|
|
/*
|
|
* First find the type always a ID so ignore strings totaly
|
|
*/
|
|
cnt = pdir->NumberOfIdEntries + pdir->NumberOfNamedEntries;
|
|
pres = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pdir+1);
|
|
|
|
for (i = 0; i < cnt; i++) {
|
|
|
|
if (pres[i].Name == (DWORD)ResType)
|
|
break;
|
|
}
|
|
|
|
if (i==cnt) // did not find the type
|
|
return 0;
|
|
|
|
/*
|
|
* Now go find the actual resource either by id (iResIndex < 0) or
|
|
* by ordinal (iResIndex >= 0)
|
|
*/
|
|
pdir = (IMAGE_RESOURCE_DIRECTORY*)((LPBYTE)prt +
|
|
(pres[i].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY));
|
|
|
|
cnt = pdir->NumberOfIdEntries + pdir->NumberOfNamedEntries;
|
|
pres = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pdir+1);
|
|
|
|
/*
|
|
* If we just want size, do it.
|
|
*/
|
|
if (iResIndex == GET_COUNT)
|
|
return (LPVOID)UIntToPtr( cnt );
|
|
|
|
/*
|
|
* if we are to search for a specific id do it.
|
|
*/
|
|
if (iResIndex < 0) {
|
|
|
|
for (i = 0; i < cnt; i++)
|
|
if (pres[i].Name == (DWORD)(-iResIndex))
|
|
break;
|
|
} else {
|
|
i = iResIndex;
|
|
}
|
|
|
|
/*
|
|
* is the index in range?
|
|
*/
|
|
if (i >= cnt)
|
|
return 0;
|
|
|
|
/*
|
|
* if we get this far the resource has a language part, ick!
|
|
* We don't handle multi-language icons, so just return the first one.
|
|
* Note, this isn't a problem since this function isn't called from
|
|
* anywhere that could specify a language. As this is called from an
|
|
* API (albeit a private one), changing this behavior is dangerous.
|
|
*/
|
|
if (pres[i].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY) {
|
|
|
|
pdir = (IMAGE_RESOURCE_DIRECTORY*)((LPBYTE)prt +
|
|
(pres[i].OffsetToData & ~IMAGE_RESOURCE_DATA_IS_DIRECTORY));
|
|
pres = (IMAGE_RESOURCE_DIRECTORY_ENTRY*)(pdir+1);
|
|
i = 0; // choose first one
|
|
}
|
|
|
|
/*
|
|
* Nested way to deep for me!
|
|
*/
|
|
if (pres[i].OffsetToData & IMAGE_RESOURCE_DATA_IS_DIRECTORY)
|
|
return 0;
|
|
|
|
pent = (IMAGE_RESOURCE_DATA_ENTRY*)((LPBYTE)prt + pres[i].OffsetToData);
|
|
|
|
/*
|
|
* all OffsetToData fields except the final one are relative to
|
|
* the start of the section. the final one is a virtual address
|
|
* we need to go back to the header and get the virtual address
|
|
* of the resource section to do this right.
|
|
*/
|
|
*pcb = pent->Size;
|
|
return RVAtoP(pBase, pent->OffsetToData);
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* GetResourceTableNE
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
LPVOID GetResourceTableNE(
|
|
LPVOID pBase)
|
|
{
|
|
LPNEWEXE pne;
|
|
LPEXEHDR pmz;
|
|
|
|
pmz = (LPEXEHDR)pBase;
|
|
pne = (LPNEWEXE)((LPBYTE)pBase + pmz->e_lfanew);
|
|
|
|
if (pmz->e_magic != MZMAGIC)
|
|
return 0;
|
|
|
|
if (pne->ne_magic != NEMAGIC) // must be a NEWEXE
|
|
return 0;
|
|
|
|
if (pne->ne_exetyp != NE_WINDOWS && // must be a Win DLL/EXE/386
|
|
pne->ne_exetyp != NE_DEV386)
|
|
return 0;
|
|
|
|
if (pne->ne_expver < 0x0300) // must be 3.0 or greater
|
|
return 0;
|
|
|
|
if (pne->ne_rsrctab == pne->ne_restab) // no resources
|
|
return 0;
|
|
|
|
return (LPBYTE)pne + pne->ne_rsrctab; // return resource table pointer
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* FindResourceNE
|
|
*
|
|
* This returns a pointer to the rsrc_nameinfo of the resource with the
|
|
* given index and type, if it is found, otherwise it returns NULL.
|
|
*
|
|
* if iResIndex is < 0, then it is assumed to be a ID and the res table
|
|
* will be searched for a matching id.
|
|
*
|
|
* if iResIndex is >= 0, then it is assumed to be a index and the Nth
|
|
* resorce of the specifed type will be returned.
|
|
*
|
|
* if iResIndex == GET_COUNT the count of resources will be returned
|
|
*
|
|
\***************************************************************************/
|
|
|
|
LPVOID FindResourceNE(
|
|
LPVOID lpBase,
|
|
LPVOID prt,
|
|
int iResIndex,
|
|
int iResType,
|
|
DWORD *pcb)
|
|
{
|
|
LPRESTABLE lpResTable;
|
|
ULPRESTYPEINFO ulpResTypeInfo;
|
|
LPRESNAMEINFO lpResNameInfo; // 16 bit alignment ok - had ushorts only
|
|
int i;
|
|
|
|
lpResTable = (LPRESTABLE)prt;
|
|
//ulpResTypeInfo = (ULPRESTYPEINFO)(LPWBYTE)&lpResTable->rs_typeinfo;
|
|
ulpResTypeInfo = (ULPRESTYPEINFO)((LPBYTE)lpResTable + 2);
|
|
|
|
while (ulpResTypeInfo->rt_id) {
|
|
|
|
if (ulpResTypeInfo->rt_id == (iResType | RSORDID)) {
|
|
|
|
lpResNameInfo = (LPRESNAMEINFO)(ulpResTypeInfo + 1);
|
|
|
|
if (iResIndex == GET_COUNT)
|
|
return (LPVOID)ulpResTypeInfo->rt_nres;
|
|
|
|
if (iResIndex < 0) {
|
|
|
|
for (i=0; i < (int)ulpResTypeInfo->rt_nres; i++) {
|
|
|
|
if (lpResNameInfo[i].rn_id == ((-iResIndex) | RSORDID))
|
|
break;
|
|
}
|
|
|
|
iResIndex = i;
|
|
}
|
|
|
|
if (iResIndex >= (int)ulpResTypeInfo->rt_nres)
|
|
return NULL;
|
|
|
|
*pcb = ((DWORD)lpResNameInfo[iResIndex].rn_length) << lpResTable->rs_align;
|
|
return (LPBYTE)lpBase + ((long)lpResNameInfo[iResIndex].rn_offset << lpResTable->rs_align);
|
|
}
|
|
|
|
ulpResTypeInfo =
|
|
(ULPRESTYPEINFO)((LPRESNAMEINFO)(ulpResTypeInfo + 1) +
|
|
ulpResTypeInfo->rt_nres);
|
|
}
|
|
|
|
*pcb = 0;
|
|
return NULL;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ExtractIconFromICO
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
UINT ExtractIconFromICO(
|
|
LPTSTR szFile,
|
|
int nIconIndex,
|
|
int cxIcon,
|
|
int cyIcon,
|
|
HICON *phicon,
|
|
UINT flags)
|
|
{
|
|
HICON hicon;
|
|
|
|
if (nIconIndex >= 1)
|
|
return 0;
|
|
|
|
flags |= LR_LOADFROMFILE;
|
|
|
|
again:
|
|
|
|
hicon = LoadImage(NULL,
|
|
szFile,
|
|
IMAGE_ICON,
|
|
LOWORD(cxIcon),
|
|
LOWORD(cyIcon),
|
|
flags);
|
|
|
|
if (hicon == NULL)
|
|
return 0;
|
|
|
|
/*
|
|
* Do we just want a count?
|
|
*/
|
|
if (phicon == NULL)
|
|
DestroyCursor((HCURSOR)hicon);
|
|
else
|
|
*phicon = hicon;
|
|
|
|
/*
|
|
* Check for large/small icon extract
|
|
*/
|
|
if (HIWORD(cxIcon)) {
|
|
|
|
cxIcon = HIWORD(cxIcon);
|
|
cyIcon = HIWORD(cyIcon);
|
|
phicon++;
|
|
|
|
goto again;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ExtractIconFromBMP
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
#define ROP_DSna 0x00220326
|
|
|
|
UINT ExtractIconFromBMP(
|
|
LPTSTR szFile,
|
|
int nIconIndex,
|
|
int cxIcon,
|
|
int cyIcon,
|
|
HICON *phicon,
|
|
UINT flags)
|
|
{
|
|
HICON hicon;
|
|
HBITMAP hbm;
|
|
HBITMAP hbmMask;
|
|
HDC hdc;
|
|
HDC hdcMask;
|
|
ICONINFO ii;
|
|
|
|
if (nIconIndex >= 1)
|
|
return 0;
|
|
|
|
/*
|
|
* BUGUS: don't use LR_CREATEDIBSECTION. USER can't make an icon out
|
|
* of a DibSection.
|
|
*/
|
|
flags |= LR_LOADFROMFILE;
|
|
|
|
again:
|
|
|
|
hbm = (HBITMAP)LoadImage(NULL,
|
|
szFile,
|
|
IMAGE_BITMAP,
|
|
LOWORD(cxIcon),
|
|
LOWORD(cyIcon),
|
|
flags);
|
|
|
|
if (hbm == NULL)
|
|
return 0;
|
|
|
|
/*
|
|
* do we just want a count?
|
|
*/
|
|
if (phicon == NULL) {
|
|
DeleteObject(hbm);
|
|
return 1;
|
|
}
|
|
|
|
hbmMask = CreateBitmap(LOWORD(cxIcon), LOWORD(cyIcon), 1, 1, NULL);
|
|
|
|
hdc = CreateCompatibleDC(NULL);
|
|
SelectObject(hdc, hbm);
|
|
|
|
hdcMask = CreateCompatibleDC(NULL);
|
|
SelectObject(hdcMask, hbmMask);
|
|
|
|
SetBkColor(hdc, GetPixel(hdc, 0, 0));
|
|
|
|
BitBlt(hdcMask, 0, 0, LOWORD(cxIcon), LOWORD(cyIcon), hdc, 0, 0, SRCCOPY);
|
|
BitBlt(hdc, 0, 0, LOWORD(cxIcon), LOWORD(cyIcon), hdcMask, 0, 0, ROP_DSna);
|
|
|
|
ii.fIcon = TRUE;
|
|
ii.xHotspot = 0;
|
|
ii.yHotspot = 0;
|
|
ii.hbmColor = hbm;
|
|
ii.hbmMask = hbmMask;
|
|
hicon = CreateIconIndirect(&ii);
|
|
|
|
DeleteObject(hdc);
|
|
DeleteObject(hbm);
|
|
DeleteObject(hdcMask);
|
|
DeleteObject(hbmMask);
|
|
|
|
*phicon = hicon;
|
|
|
|
/*
|
|
* Check for large/small icon extract
|
|
*/
|
|
if (HIWORD(cxIcon)) {
|
|
cxIcon = HIWORD(cxIcon);
|
|
cyIcon = HIWORD(cyIcon);
|
|
phicon++;
|
|
|
|
goto again;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* ExtractIconFromEXE
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
UINT ExtractIconFromEXE(
|
|
HANDLE hFile,
|
|
int nIconIndex,
|
|
int cxIconSize,
|
|
int cyIconSize,
|
|
HICON *phicon,
|
|
UINT *piconid,
|
|
UINT nIcons,
|
|
UINT flags)
|
|
{
|
|
HANDLE hFileMap = INVALID_HANDLE_VALUE;
|
|
LPVOID lpFile = NULL;
|
|
EXEHDR *pmz;
|
|
NEWEXE UNALIGNED *pne;
|
|
LPVOID pBase;
|
|
LPVOID pres = NULL;
|
|
UINT result = 0;
|
|
LONG FileLength;
|
|
DWORD cbSize;
|
|
int cxIcon;
|
|
int cyIcon;
|
|
|
|
LPVOID (*FindResourceX)(LPVOID pBase,
|
|
LPVOID prt,
|
|
int iResIndex,
|
|
int iResType,
|
|
DWORD *pcb);
|
|
|
|
FileLength = (LONG)GetFileSize(hFile, NULL);
|
|
|
|
hFileMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL);
|
|
if (hFileMap == NULL)
|
|
goto exit;
|
|
|
|
lpFile = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0);
|
|
if (lpFile == NULL)
|
|
goto exit;
|
|
|
|
pBase = (LPVOID)lpFile;
|
|
pmz = (struct exe_hdr *)pBase;
|
|
|
|
_try {
|
|
|
|
if (pmz->e_magic != MZMAGIC)
|
|
goto exit;
|
|
|
|
if (pmz->e_lfanew <= 0) // not a new exe
|
|
goto exit;
|
|
|
|
if (pmz->e_lfanew >= FileLength) // not a new exe
|
|
goto exit;
|
|
|
|
pne = (NEWEXE UNALIGNED *)((BYTE*)pmz + pmz->e_lfanew);
|
|
|
|
switch (pne->ne_magic) {
|
|
case NEMAGIC:
|
|
pres = GetResourceTableNE(pBase);
|
|
FindResourceX = FindResourceNE;
|
|
break;
|
|
|
|
case PEMAGIC:
|
|
pres = GetResourceTablePE(pBase);
|
|
FindResourceX = FindResourcePE;
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* cant find the resource table, fail
|
|
*/
|
|
if (pres == NULL)
|
|
goto exit;
|
|
|
|
/*
|
|
* do we just want a count?
|
|
*/
|
|
if (phicon == NULL) {
|
|
result = PtrToUlong(FindResourceX(pBase,
|
|
pres,
|
|
GET_COUNT,
|
|
(LONG_PTR)RT_GROUP_ICON,
|
|
&cbSize));
|
|
goto exit;
|
|
}
|
|
|
|
while (result < nIcons) {
|
|
|
|
LPVOID lpIconDir;
|
|
LPVOID lpIcon;
|
|
int idIcon;
|
|
|
|
cxIcon = cxIconSize;
|
|
cyIcon = cyIconSize;
|
|
|
|
/*
|
|
* find the icon dir for this icon.
|
|
*/
|
|
lpIconDir = FindResourceX(pBase,
|
|
pres,
|
|
nIconIndex,
|
|
(LONG_PTR)RT_GROUP_ICON,
|
|
&cbSize);
|
|
|
|
if (lpIconDir == NULL)
|
|
goto exit;
|
|
|
|
if ((((LPNEWHEADER)lpIconDir)->Reserved != 0) ||
|
|
(((LPNEWHEADER)lpIconDir)->ResType != FT_ICON)) {
|
|
|
|
goto exit;
|
|
}
|
|
again:
|
|
idIcon = LookupIconIdFromDirectoryEx((LPBYTE)lpIconDir,
|
|
TRUE,
|
|
LOWORD(cxIcon),
|
|
LOWORD(cyIcon),
|
|
flags);
|
|
lpIcon = FindResourceX(pBase,
|
|
pres,
|
|
-idIcon,
|
|
(LONG_PTR)RT_ICON,
|
|
&cbSize);
|
|
|
|
if (lpIcon == NULL)
|
|
goto exit;
|
|
|
|
if ((((UPBITMAPINFOHEADER)lpIcon)->biSize != sizeof(BITMAPINFOHEADER)) &&
|
|
(((UPBITMAPINFOHEADER)lpIcon)->biSize != sizeof(BITMAPCOREHEADER))) {
|
|
|
|
goto exit;
|
|
}
|
|
|
|
#ifndef WINNT
|
|
/* touch this memory before calling USER
|
|
* so if we page fault we will do it outside of the Win16Lock
|
|
* most icons aren't more than 2 pages
|
|
*/
|
|
ReadAByte(((BYTE *)lpIcon) + cbSize - 1);
|
|
#endif
|
|
|
|
if (piconid)
|
|
piconid[result] = idIcon;
|
|
|
|
phicon[result++] = CreateIconFromResourceEx((LPBYTE)lpIcon,
|
|
cbSize,
|
|
TRUE,
|
|
WIN32VER30,
|
|
LOWORD(cxIcon),
|
|
LOWORD(cyIcon),
|
|
flags);
|
|
|
|
/*
|
|
* check for large/small icon extract
|
|
*/
|
|
if (HIWORD(cxIcon)) {
|
|
|
|
cxIcon = HIWORD(cxIcon);
|
|
cyIcon = HIWORD(cyIcon);
|
|
|
|
goto again;
|
|
}
|
|
|
|
nIconIndex++; // next icon index
|
|
}
|
|
|
|
} _except (W32ExceptionHandler(FALSE, RIP_WARNING)) {
|
|
result = 0;
|
|
}
|
|
|
|
exit:
|
|
|
|
if (lpFile)
|
|
UnmapViewOfFile(lpFile);
|
|
|
|
if (hFileMap != INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFileMap);
|
|
|
|
return result;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* PathFindExtension
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
LPWSTR PathFindExtension(
|
|
LPWSTR pszPath)
|
|
{
|
|
LPWSTR pszDot;
|
|
|
|
for (pszDot = NULL; *pszPath; pszPath = CharNext(pszPath)) {
|
|
|
|
switch (*pszPath) {
|
|
case L'.':
|
|
pszDot = pszPath; // remember the last dot
|
|
break;
|
|
|
|
case L'\\':
|
|
case L' ': // extensions can't have spaces
|
|
pszDot = NULL; // forget last dot, it was in a directory
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* if we found the extension, return ptr to the dot, else
|
|
* ptr to end of the string (NULL extension) (cast->non const)
|
|
*/
|
|
return pszDot ? (LPWSTR)pszDot : (LPWSTR)pszPath;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* PrivateExtractIconExA
|
|
*
|
|
* Ansi version of PrivateExtractIconExW
|
|
*
|
|
\***************************************************************************/
|
|
|
|
WINUSERAPI UINT PrivateExtractIconExA(
|
|
LPCSTR szFileName,
|
|
int nIconIndex,
|
|
HICON *phiconLarge,
|
|
HICON *phiconSmall,
|
|
UINT nIcons)
|
|
{
|
|
LPWSTR szFileNameW;
|
|
UINT uRet;
|
|
|
|
if (!MBToWCS(szFileName, -1, &szFileNameW, -1, TRUE))
|
|
return 0;
|
|
|
|
uRet = PrivateExtractIconExW(szFileNameW,
|
|
nIconIndex,
|
|
phiconLarge,
|
|
phiconSmall,
|
|
nIcons);
|
|
|
|
UserLocalFree(szFileNameW);
|
|
|
|
return uRet;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* HasExtension
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
DWORD HasExtension(
|
|
LPWSTR pszPath)
|
|
{
|
|
LPWSTR p = PathFindExtension(pszPath);
|
|
|
|
/*
|
|
* (BobDay, emended by JasonSch):
|
|
*
|
|
* NOTE: This routine can produce false positives. E.g., "Fister.Bather"
|
|
* would return .BAT. This doesn't currently hurt us in the instances where
|
|
* this routine is used. However, people stealing this code should verify
|
|
* that this is okay for them.
|
|
*
|
|
* NOTE: We could make this EXTKEY based like the extension matching
|
|
* stuff elsewhere (e.g., shlwapi\urlpars.cpp). EXTKEY is a QWORD
|
|
* so UNICODE would fit.
|
|
*/
|
|
if (*p == L'.') {
|
|
|
|
WCHAR szExt[5];
|
|
|
|
lstrcpynW(szExt, p, 5);
|
|
|
|
if (lstrcmpiW(szExt,TEXT(".com")) == 0) return COM_FILE;
|
|
if (lstrcmpiW(szExt,TEXT(".bat")) == 0) return BAT_FILE;
|
|
if (lstrcmpiW(szExt,TEXT(".cmd")) == 0) return CMD_FILE;
|
|
if (lstrcmpiW(szExt,TEXT(".pif")) == 0) return PIF_FILE;
|
|
if (lstrcmpiW(szExt,TEXT(".lnk")) == 0) return LNK_FILE;
|
|
if (lstrcmpiW(szExt,TEXT(".ico")) == 0) return ICO_FILE;
|
|
if (lstrcmpiW(szExt,TEXT(".exe")) == 0) return EXE_FILE;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* PrivateExtractIconsW
|
|
*
|
|
* Extracts 1 or more icons from a file.
|
|
*
|
|
* input:
|
|
* szFileName - EXE/DLL/ICO/CUR/ANI file to extract from
|
|
* nIconIndex - what icon to extract
|
|
* 0 = first icon, 1=second icon, etc.
|
|
* -N = icon with id==N
|
|
* cxIcon - icon size wanted (if HIWORD != 0 two sizes...)
|
|
* cyIcon - icon size wanted (if HIWORD != 0 two sizes...)
|
|
* 0,0 means extract at natural size.
|
|
* phicon - place to return extracted icon(s)
|
|
* nIcons - number of icons to extract.
|
|
* flags - LoadImage LR_* flags
|
|
*
|
|
* returns:
|
|
* if picon is NULL, number of icons in the file is returned.
|
|
*
|
|
* notes:
|
|
* handles extraction from PE (Win32), NE (Win16), ICO (Icon),
|
|
* CUR (Cursor), ANI (Animated Cursor), and BMP (Bitmap) files.
|
|
* only Win16 3.x files are supported (not 2.x)
|
|
*
|
|
* cx/cyIcon are the size of the icon to extract, two sizes
|
|
* can be extracted by putting size 1 in the loword and size 2 in the
|
|
* hiword, ie MAKELONG(24, 48) would extract 24 and 48 size icons.
|
|
* This is a hack it is done so IExtractIcon::Extract can be called by
|
|
* outside people with custom large/small icon sizes that are not what
|
|
* the shell uses internaly.
|
|
*
|
|
\***************************************************************************/
|
|
|
|
WINUSERAPI UINT WINAPI PrivateExtractIconsW(
|
|
LPCWSTR szFileName,
|
|
int nIconIndex,
|
|
int cxIcon,
|
|
int cyIcon,
|
|
HICON *phicon,
|
|
UINT *piconid,
|
|
UINT nIcons,
|
|
UINT flags)
|
|
{
|
|
HANDLE hFile = (HANDLE)INVALID_HANDLE_VALUE;
|
|
UINT result = 0;
|
|
WORD magic[6];
|
|
WCHAR achFileName[MAX_PATH];
|
|
FILETIME ftAccess;
|
|
WCHAR szExpFileName[MAX_PATH];
|
|
DWORD dwBytesRead;
|
|
|
|
/*
|
|
* Set failure defaults.
|
|
*/
|
|
if (phicon)
|
|
*phicon = NULL;
|
|
|
|
/*
|
|
* Check for special extensions, and fail quick
|
|
*/
|
|
switch (HasExtension((LPWSTR)szFileName)) {
|
|
case COM_FILE:
|
|
case BAT_FILE:
|
|
case CMD_FILE:
|
|
case PIF_FILE:
|
|
case LNK_FILE:
|
|
goto exit;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/*
|
|
* Try expanding environment variables in the file name we're passed.
|
|
*/
|
|
ExpandEnvironmentStrings(szFileName, szExpFileName, MAX_PATH);
|
|
szExpFileName[ MAX_PATH-1 ] = (WCHAR)0;
|
|
|
|
/*
|
|
* Open the file - First check to see if it is a UNC path. If it
|
|
* is make sure that we have access to the path...
|
|
*/
|
|
if (PathIsUNC(szExpFileName)) {
|
|
|
|
lstrcpynW(achFileName, szExpFileName, ARRAYSIZE(achFileName));
|
|
|
|
} else {
|
|
|
|
if (SearchPath(NULL,
|
|
szExpFileName,
|
|
NULL,
|
|
ARRAYSIZE(achFileName),
|
|
achFileName, NULL) == 0) {
|
|
|
|
goto error_file;
|
|
}
|
|
}
|
|
|
|
hFile = CreateFile(achFileName,
|
|
GENERIC_READ|FILE_WRITE_ATTRIBUTES,
|
|
FILE_SHARE_WRITE | FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
|
|
0);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE) {
|
|
|
|
hFile = CreateFile(achFileName, GENERIC_READ,
|
|
FILE_SHARE_READ,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
|
|
0);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
goto error_file;
|
|
|
|
} else {
|
|
|
|
/*
|
|
* Restore the Access Date
|
|
*/
|
|
if (GetFileTime(hFile, NULL, &ftAccess, NULL))
|
|
SetFileTime(hFile, NULL, &ftAccess, NULL);
|
|
}
|
|
|
|
|
|
ReadFile(hFile, &magic, sizeof(magic), &dwBytesRead, NULL);
|
|
if (dwBytesRead != sizeof(magic))
|
|
goto exit;
|
|
|
|
if (piconid)
|
|
*piconid = (UINT)-1; // Fill in "don't know" value
|
|
|
|
switch (magic[0]) {
|
|
case MZMAGIC:
|
|
result = ExtractIconFromEXE(hFile,
|
|
nIconIndex,
|
|
cxIcon,
|
|
cyIcon,
|
|
phicon,
|
|
piconid,
|
|
nIcons,
|
|
flags);
|
|
break;
|
|
|
|
case ANI_MAGIC: // possible .ani cursor
|
|
|
|
/*
|
|
* Ani cursors are easy they are RIFF files of type 'ACON'
|
|
*/
|
|
if (magic[1] == ANI_MAGIC1 && magic[4] == ANI_MAGIC4 &&
|
|
magic[5] == ANI_MAGIC5) {
|
|
|
|
result = ExtractIconFromICO(achFileName,
|
|
nIconIndex,
|
|
cxIcon,
|
|
cyIcon,
|
|
phicon,
|
|
flags);
|
|
}
|
|
break;
|
|
|
|
case BMP_MAGIC: // possible bitmap
|
|
result = ExtractIconFromBMP(achFileName,
|
|
nIconIndex,
|
|
cxIcon,
|
|
cyIcon,
|
|
phicon,
|
|
flags);
|
|
break;
|
|
|
|
case ICON_MAGIC: // possible .ico or .cur
|
|
|
|
/*
|
|
* Icons and cursors look like this
|
|
*
|
|
* iReserved - always zero
|
|
* iResourceType - 1 for icons 2 cor cursor.
|
|
* cresIcons - number of resolutions in this file
|
|
*
|
|
* We only allow 1 <= cresIcons <= 10
|
|
*/
|
|
if (magic[1] == ICO_MAGIC1 || magic[1] == CUR_MAGIC1) {
|
|
|
|
result = ExtractIconFromICO(achFileName,
|
|
nIconIndex,
|
|
cxIcon,
|
|
cyIcon,
|
|
phicon,
|
|
flags);
|
|
}
|
|
break;
|
|
}
|
|
|
|
exit:
|
|
|
|
if (hFile!=INVALID_HANDLE_VALUE)
|
|
CloseHandle(hFile);
|
|
|
|
return result;
|
|
|
|
/*
|
|
* if we cant open the file, return a code saying we cant open the file
|
|
* if phicon==NULL return the count of icons in the file 0
|
|
*/
|
|
|
|
error_file:
|
|
|
|
result = (phicon ? (UINT)-1 : 0);
|
|
|
|
goto exit;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* PrivateExtractIconsA
|
|
*
|
|
*
|
|
\***************************************************************************/
|
|
|
|
WINUSERAPI UINT WINAPI PrivateExtractIconsA(
|
|
LPCSTR szFileName,
|
|
int nIconIndex,
|
|
int cxIcon,
|
|
int cyIcon,
|
|
HICON *phicon,
|
|
UINT *piconid,
|
|
UINT nIcons,
|
|
UINT flags)
|
|
{
|
|
LPWSTR szFileNameW;
|
|
UINT uRet;
|
|
|
|
if (!MBToWCS(szFileName, -1, &szFileNameW, -1, TRUE))
|
|
return 0;
|
|
|
|
uRet = PrivateExtractIconsW(szFileNameW,
|
|
nIconIndex,
|
|
cxIcon,
|
|
cyIcon,
|
|
phicon,
|
|
piconid,
|
|
nIcons,
|
|
flags);
|
|
|
|
UserLocalFree(szFileNameW);
|
|
|
|
return uRet;
|
|
}
|
|
|
|
/***************************************************************************\
|
|
* PrivateExtractIconExW
|
|
*
|
|
* extracts 1 or more icons from a file.
|
|
*
|
|
* input:
|
|
* szFileName - EXE/DLL/ICO file to extract from
|
|
* nIconIndex - what icon to extract
|
|
* 0 = first icon, 1=second icon, etc.
|
|
* -N = icon with id==N
|
|
* phiconLarge - place to return extracted icon(s)
|
|
* phiconSmall - place to return extracted icon(s) (small size)
|
|
* nIcons - number of icons to extract.
|
|
*
|
|
* returns:
|
|
* number of icons extracted, or the count of icons if phiconLarge==NULL
|
|
*
|
|
* notes:
|
|
* handles extraction from PE (Win32), NE (Win16), and ICO (Icon) files.
|
|
* only Win16 3.x files are supported (not 2.x)
|
|
*
|
|
\***************************************************************************/
|
|
|
|
WINUSERAPI UINT PrivateExtractIconExW(
|
|
LPCWSTR szFileName,
|
|
int nIconIndex,
|
|
HICON *phiconLarge,
|
|
HICON *phiconSmall,
|
|
UINT nIcons)
|
|
{
|
|
UINT result = 0;
|
|
|
|
if ((nIconIndex == -1) || ((phiconLarge == NULL) && (phiconSmall == NULL)))
|
|
return PrivateExtractIconsW(szFileName, 0, 0, 0, NULL, NULL, 0, 0);
|
|
|
|
if (phiconLarge && phiconSmall && (nIcons == 1)) {
|
|
|
|
HICON ahicon[2];
|
|
|
|
ahicon[0] = NULL;
|
|
ahicon[1] = NULL;
|
|
|
|
result = PrivateExtractIconsW(szFileName,
|
|
nIconIndex,
|
|
MAKELONG(GetSystemMetrics(SM_CXICON),
|
|
GetSystemMetrics(SM_CXSMICON)),
|
|
MAKELONG(GetSystemMetrics(SM_CYICON),
|
|
GetSystemMetrics(SM_CYSMICON)),
|
|
ahicon,
|
|
NULL,
|
|
2,
|
|
0);
|
|
|
|
*phiconLarge = ahicon[0];
|
|
*phiconSmall = ahicon[1];
|
|
|
|
} else {
|
|
|
|
if (phiconLarge)
|
|
result = PrivateExtractIconsW(szFileName,
|
|
nIconIndex,
|
|
GetSystemMetrics(SM_CXICON),
|
|
GetSystemMetrics(SM_CYICON),
|
|
phiconLarge,
|
|
NULL,
|
|
nIcons,
|
|
0);
|
|
|
|
if (phiconSmall)
|
|
result = PrivateExtractIconsW(szFileName,
|
|
nIconIndex,
|
|
GetSystemMetrics(SM_CXSMICON),
|
|
GetSystemMetrics(SM_CYSMICON),
|
|
phiconSmall,
|
|
NULL,
|
|
nIcons,
|
|
0);
|
|
}
|
|
|
|
return result;
|
|
}
|